inossidabile/sprockets-preload

View on GitHub
assets/sprockets/preload/load.coffee.erb

Summary

Maintainability
Test Coverage
#= depend_on_asset sprockets/preload/assets

@SprocketsPreload =

  onload: (callback) ->
    @localStorage = true unless @localStorage?

    return callback() if document && document.readyState is "complete"
    
    if window.addEventListener
      window.addEventListener "DOMContentLoaded", callback, false
    else
      window.attachEvent "onload", callback if window.attachEvent

  #
  # Dispatches events through `document`
  #
  trigger: (name, data) ->
    if document.createEvent
      event = document.createEvent('Events')
      event.data = data if data
      event.initEvent name, true, true
      document.dispatchEvent event
    else if document.createEventObject
      event = document.createEventObject()
      event.data = data if data
      try
        document.fireEvent "on" + name, event

  #
  # Happens from time to time during downloading (but only when localStorage caching enabled)
  #
  triggerProgress: (percent) ->
    @progress? percent
    @trigger 'sprockets:progress', percent: percent

  #
  # Happens when preloading assets are loaded
  #
  triggerSuccess: ->
    @success?()
    @trigger 'sprockets:loaded'

  #
  # Creates `script` tag with given attributes and adds it to head
  #
  inject: (attributes={}) ->
    node = document.createElement "script"
    node[k] = v for k, v of attributes
    document.getElementsByTagName("head")[0].appendChild node

  #
  # Loads asset using localStorage as a manual cache
  #
  loadCached: (url, version, size) ->
    try
      attempt = localStorage['sprockets-preload']
      attempt = JSON.parse(attempt) if attempt
    catch e
      delete localStorage['sprockets-preload']

    if attempt?.version == version
      @inject defer: true, text: attempt.source
      @triggerProgress 100
      @triggerSuccess 100
    else
      xhr = @_ajax 'GET', url, (xhr) =>
        clearInterval poller
        localStorage['sprockets-preload'] = JSON.stringify
          version: version
          source: xhr.responseText

        @inject defer: true, text: xhr.responseText
        @triggerProgress 100
        @triggerSuccess()

      if size > 0
        poller = setInterval (=>
          @triggerProgress Math.round(xhr.responseText.length / size * 100 * 100) / 100
        ), 100

  #
  # Loads asset using built-in browser caching
  #
  loadSimple: (url) ->
    script = document.createElement "script"
    done   = false
    self   = @

    proceed = ->
      if !done && (!@readyState? || @readyState == "loaded" || @readyState == "complete")
        done = true; script.onload = script.onreadystatechange = null
        self.triggerSuccess()

    @inject src: url, onload: proceed, onreadystatechange: proceed

  #
  # Starring custom XHR wrapper
  #
  _ajax: (method, url, callback) ->
    if window.XMLHttpRequest
      xhr = new XMLHttpRequest
    else
      xhr = new ActiveXObject 'Microsoft.XMLHTTP'

    xhr.onreadystatechange = -> callback?(xhr) if xhr.readyState > 3
    xhr.open method, url, 1
    xhr.send()

    xhr

@SprocketsPreload.onload ->
  if SprocketsPreload.inline
    delete localStorage['sprockets-preload'] if window.localStorage? && localStorage['sprockets-preload']
    SprocketsPreload.triggerProgress 100
    SprocketsPreload.triggerSuccess()
  else if SprocketsPreload.localStorage && window.localStorage
    SprocketsPreload.loadCached(
      <%= JSON.generate asset_path('sprockets/preload/assets'), quirks_mode: true %>,
      <%= JSON.generate Sprockets::Preload['sprockets/preload/assets'].digest, quirks_mode: true %>,
      <%= Sprockets::Preload['sprockets/preload/assets'].length %>
    )
  else
    delete localStorage['sprockets-preload'] if window.localStorage?
    SprocketsPreload.loadSimple(<%= JSON.generate asset_path('sprockets/preload/assets'), quirks_mode: true %>)