• Jump To … +
    beanvalidator-validation.coffee t5-core-dom-jquery.coffee t5-core-dom-prototype.coffee ajax.coffee ajaxformloop.coffee alert.coffee autocomplete.coffee bootstrap.coffee confirm-click.coffee console.coffee datefield.coffee events.coffee exception-display.coffee exception-frame.coffee fields.coffee form-fragment.coffee forms.coffee init.coffee localdate.coffee messages.coffee moment.coffee pageinit.coffee palette.coffee select.coffee time-interval.coffee tree.coffee utils.coffee validation.coffee zone-refresh.coffee zone.coffee
  • pageinit.coffee

  • ¶

    Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    http:#www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

  • ¶

    t5/core/pageinit

    Module that defines functions used for page initialization. The initialize function is passed an array of initializers; each initializer is itself an array. The first value in the initializer defines the name of the module to invoke. The module name may also indicate the function exported by the module, as a suffix following a colon: e.g., “my/module:myfunc”. Any additional values in the initializer are passed to the function. The context of the function (this) is null.

    define ["underscore", "./console", "./dom", "./events"],
      (_, console, dom, events) ->
        pathPrefix = null
  • ¶

    Borrowed from Prototype:

        isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'
        isIE = !!window.attachEvent && !isOpera
    
        rebuildURL = (path) ->
          return path if path.match /^https?:/
  • ¶

    See Tapestry.rebuildURL() for an error about the path not starting with a leading ‘/‘ We’ll assume that doesn’t happen.

          if !pathPrefix
            l = window.location
            pathPrefix = "#{l.protocol}//#{l.host}"
    
          return pathPrefix + path
    
        rebuildURLOnIE =
          if isIE then rebuildURL else _.identity
    
        addStylesheets = (newStylesheets) ->
          return unless newStylesheets
  • ¶

    Figure out which stylesheets are loaded; adjust for IE, and especially, IE 9 which can report a stylesheet’s URL as null (not empty string).

          loaded = _.chain(document.styleSheets)
          .pluck("href")
          .without("")
          .without(null)
          .map(rebuildURLOnIE)
    
          insertionPoint = _.find(document.styleSheets, (ss) ->
            parent = ss.ownerNode || ss.owningElement
            parent.rel is "stylesheet ajax-insertion-point")
  • ¶

    Most browsers support document.head, but older IE doesn’t:

          head = document.head or document.getElementsByTagName("head")[0]
    
          _.chain(newStylesheets)
          .map((ss) -> { href: rebuildURL(ss.href), media: ss.media })
          .reject((ss) -> loaded.contains(ss.href).value())
          .each((ss) ->
            element = document.createElement "link"
            element.setAttribute "type", "text/css"
            element.setAttribute "rel", "stylesheet"
            element.setAttribute "href", ss.href
            if ss.media
              element.setAttribute "media", ss.media
    
            if insertionPoint
              head.insertBefore element, insertionPoint.ownerNode
            else
              head.appendChild element
    
            console.debug "Added stylesheet #{ss.href}"
          )
    
          return
    
        invokeInitializer = (tracker, qualifiedName, initArguments) ->
          [moduleName, functionName] = qualifiedName.split ':'
    
          require [moduleName], (moduleLib) ->
    
            try
  • ¶

    Some modules export nothing but do some full-page initialization, such as adding event handlers to the body.

              if not functionName and
                initArguments.length is 0 and
                not _.isFunction moduleLib
                  console.debug "Loaded module #{moduleName}"
                  return
    
              fn = if functionName? then moduleLib[functionName] else moduleLib
    
              unless fn?
                throw new Error "Could not locate function `#{qualifiedName}'."
    
              if console.debugEnabled
                argsString = (JSON.stringify arg for arg in initArguments).join(", ")
                console.debug "Invoking #{qualifiedName}(#{argsString})"
    
              fn.apply null, initArguments
            finally
              tracker()
  • ¶

    Loads all specified libraries in order (this includes the core stack, other stacks, and any free-standing libraries). It then executes the initializations. Once all initializations have completed (which is usually an asynchronous operation, as initializations may require the loading of further modules), then the data-page-initialized attribute of the <body> element is set to ‘true’.

    This is the main export of the module; other functions are attached as properties.

        loadLibrariesAndInitialize = (libraries, inits) ->
          console.debug "Loading #{libraries?.length or 0} libraries"
          exports.loadLibraries libraries,
            -> exports.initialize inits,
              ->
  • ¶

    At this point, all libraries have been loaded, and all inits should have executed. Unless some of the inits triggered Ajax updates (such as a core/ProgressiveDisplay component), then the page should be ready to go. We set a flag, mostly used by test suites, to ensure that all is ready. Later Ajax requests will cause the data-ajax-active attribute to be incremented (from 0) and decremented (when the requests complete).

                dom.body.attr "data-page-initialized", "true"
    
                for mask in dom.body.find ".pageloading-mask"
                  mask.remove()
    
        exports = _.extend loadLibrariesAndInitialize,
  • ¶

    Passed a list of initializers, executes each initializer in order. Due to asynchronous loading of modules, the exact order in which initializer functions are invoked is not predictable.

          initialize: (inits = [], callback) ->
            console.debug "Executing #{inits.length} inits"
            callbackCountdown = inits.length + 1
  • ¶

    tracker gets invoked once after each require/callback, plus once extra (to handle the case where there are no inits). When the count hits zero, it invokes the callback (if there is one).

            tracker = ->
              callbackCountdown--
    
              if callbackCountdown is 0
                console.debug "All inits executed"
                callback() if callback
  • ¶

    First value in each init is the qualified module name; anything after that are arguments to be passed to the identified function. A string is the name of a module to load, or function to invoke, that takes no parameters.

            for init in inits
              if _.isString init
                invokeInitializer tracker, init, []
              else
                [qualifiedName, initArguments...] = init
                invokeInitializer tracker, qualifiedName, initArguments
    
            tracker()
  • ¶

    Pre-loads a number of libraries in order. When the last library is loaded, invokes the callback (with no parameters).

          loadLibraries: (libraries, callback) ->
            reducer = (callback, library) -> ->
              console.debug "Loading library #{library}"
              require [library], callback
    
            finalCallback = _.reduceRight libraries, reducer, callback
    
            finalCallback.call null
    
          evalJavaScript: (js) ->
            console.debug "Evaluating: #{js}"
            eval js
  • ¶

    Triggers the focus event on the field, if the field exist. Focus occurs delayed 1/8th of a second, which helps ensure that other initializions on the page are in place.

    • fieldId - element id of field to focus on
          focus: (fieldId) ->
            field = dom fieldId
    
            if field
              _.delay (-> field.focus()), 125
  • ¶

    Passed the response from an Ajax request, when the request is successful. This is used for any request that attaches partial-page-render data to the main JSON object response. If no such data is attached, the callback is simply invoked immediately. Otherwise, Tapestry processes the partial-page-render data. This may involve loading some number of JavaScript libraries and CSS style sheets, and a number of direct updates to the DOM. After DOM updates, the callback is invoked, passed the response (with any Tapestry-specific data removed). After the callback is invoked, page initializations occur. This method returns null.

  • ¶
    • response - the Ajax response object
    • callback - invoked after scripts are loaded, but before page initializations occur (may be null)
          handlePartialPageRenderResponse: (response, callback) ->
  • ¶

    Capture the partial page response portion of the overall response, and then remove it so it doesn’t interfere elsewhere.

            responseJSON = response.json or {}
            partial = responseJSON._tapestry
            delete responseJSON._tapestry
  • ¶

    Extreme case: the data has a redirectURL which forces an immediate redirect to the URL. No other initialization or callback invocation occurs.

            if partial?.redirectURL
              if window.location.href is partial.redirectURL
                window.location.reload true
              else
                window.location.href = partial.redirectURL
              return
    
            addStylesheets partial?.stylesheets
  • ¶

    Make sure all libraries are loaded

            exports.loadLibraries partial?.libraries, ->
  • ¶

    After libraries are loaded, update each zone:

              _(partial?.content).each ([id, content]) ->
                console.debug "Updating content for zone #{id}"
    
                zone = dom.wrap id
    
                if zone
                  zone.trigger events.zone.update, { content }
  • ¶

    Invoke the callback, if present. The callback may do its own content updates.

              callback and callback(response)
  • ¶

    Lastly, perform initializations from the partial page render response.

              exports.initialize partial?.inits
    
            return