lib/asset_hat/js/vendors.rb in asset_hat-0.3.1 vs lib/asset_hat/js/vendors.rb in asset_hat-0.4.0
- old
+ new
@@ -4,104 +4,230 @@
# plugin/framework/library vendors.
module Vendors
# A list of supported 3rd-party JavaScript plugin/vendor names.
# Homepages:
#
- # * {jQuery}[http://jquery.com/]
- # * {jQuery UI}[http://jqueryui.com/]
- # * {Prototype}[http://www.prototypejs.org/]
- # * {script.aculo.us}[http://script.aculo.us/]
- # * {MooTools}[http://mootools.net/]
- # * {Dojo}[http://dojotoolkit.org/]
- # * {SWFObject}[http://code.google.com/p/swfobject/]
- # * {YUI}[http://developer.yahoo.com/yui/]
- # * {Ext Core}[http://extjs.com/products/extcore/]
- # * {WebFont Loader}[http://code.google.com/apis/webfonts/docs/webfont_loader.html]
- VENDORS = [
- :jquery, :jquery_ui,
- :prototype, :scriptaculous,
- :mootools,
+ # * Frameworks/libraries:
+ # * {Dojo}[http://dojotoolkit.org/]
+ # * {Ext Core}[http://extjs.com/products/extcore/]
+ # * {jQuery UI}[http://jqueryui.com/]
+ # * {jQuery}[http://jquery.com/]
+ # * {MooTools}[http://mootools.net/]
+ # * {Prototype}[http://www.prototypejs.org/]
+ # * {script.aculo.us}[http://script.aculo.us/]
+ # * {SWFObject}[http://code.google.com/p/swfobject/]
+ # * {WebFont Loader}[http://code.google.com/apis/webfonts/docs/webfont_loader.html]
+ # * {YUI}[http://developer.yahoo.com/yui/]
+ # * Loaders:
+ # * {LABjs}[http://labjs.com]
+ VENDORS_ON_GOOGLE_CDN = [
:dojo,
- :swfobject,
- :yui,
:ext_core,
- :webfont
+ :jquery,
+ :jquery_ui,
+ :mootools,
+ :prototype,
+ :scriptaculous,
+ :swfobject,
+ :webfont,
+ :yui
]
+ VENDORS_ON_CDNJS = [
+ :lab_js
+ ]
+ VENDORS = VENDORS_ON_GOOGLE_CDN + VENDORS_ON_CDNJS
- # Accepts an item from VENDORS, and returns the URL at which that vendor
- # asset can be found. The URL is either local (relative) or external
- # depending on the environment configuration. If external, the URL
- # points to {Google's CDN}[http://code.google.com/apis/ajaxlibs/].
+ # Accepts an item from `VENDORS`, and returns the URL at which that
+ # vendor asset can be found. The URL is either local (relative) or
+ # remote, depending on the environment configuration:
+ #
+ # - If `AssetHat.consider_all_requests_local?` is true:
+ # - The local file takes precedence.
+ # - If the local file is missing, the remote URL in assets.yml is
+ # used as a fallback.
+ # - If there is no remote URL in assets.yml, the Google CDN URL is
+ # used as a fallback. (This makes setup easier: If the app doesn't
+ # already have a local copy of the vendor file, then it's instead
+ # loaded remotely.)
+ # - If `AssetHat.consider_all_requests_local?` is false:
+ # - The remote URL in assets.yml takes precedence.
+ # - The {Google CDN}[http://code.google.com/apis/ajaxlibs/] URL is
+ # used as a fallback, but only if a version number can be found
+ # (either in assets.yml or via the helper's `:version` option). If
+ # no version number is found, the remote URL cannot be built, so
+ # the local file (if any) is used as a fallback.
+ #
# Options:
#
# [ssl] Boolean for whether to include vendor JS via HTTPS. Defaults
# to false.
- # [version] The vendor version, e.g., '1.4.0' for jQuery 1.4. By
+ # [version] The vendor version, e.g., '1.5.0' for jQuery 1.5. By
# default, each vendor version is taken from
# <code>config/assets.yml</code>; use this option to override
# the configuration.
def self.source_for(vendor, options={})
vendor_config =
AssetHat.config['js']['vendors'][vendor.to_s] rescue nil
- use_local = ActionController::Base.consider_all_requests_local
+ use_local = AssetHat.consider_all_requests_local?
use_ssl = !!options[:ssl]
version = options[:version] || vendor_config['version'] rescue nil
- unless use_local
- src = vendor_config['remote_url'] rescue nil
- src = (vendor_config['remote_ssl_url'] rescue nil) if use_ssl
- end
+ # Prepare local path and default remote URL
+ srcs = Vendors.vendor_uris(vendor,
+ :use_ssl => use_ssl, :version => version)
+ local_src, remote_src = srcs[:local], srcs[:remote]
- if src.blank?
- case vendor
- when :jquery
- src = use_local || version.blank? ?
- "#{['jquery', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery.min.js"
- when :jquery_ui
- src = use_local || version.blank? ?
- "#{['jquery-ui', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jqueryui/#{version}/jquery-ui.min.js"
- when :prototype
- # Prototype currently doesn't provide an official minified version.
- src = use_local || version.blank? ?
- "#{['prototype', version].compact.join('-')}.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/prototype/#{version}/prototype.js"
- when :scriptaculous
- # script.aculo.us currently doesn't provide an official minified version.
- src = use_local || version.blank? ?
- "#{['scriptaculous', version].compact.join('-')}.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/scriptaculous/#{version}/scriptaculous.js"
- when :mootools
- src = use_local || version.blank? ?
- "#{['mootools', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/mootools/#{version}/mootools-yui-compressed.js"
- when :dojo
- src = use_local || version.blank? ?
- "#{['dojo', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/dojo/#{version}/dojo/dojo.xd.js"
- when :swfobject
- src = use_local || version.blank? ?
- "#{['swfobject', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/swfobject/#{version}/swfobject.js"
- when :yui
- src = use_local || version.blank? ?
- "#{['yui', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/yui/#{version}/build/yuiloader/yuiloader-min.js"
- when :ext_core
- src = use_local || version.blank? ?
- "#{['ext_core', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/ext-core/#{version}/ext-core.js"
- when :webfont
- src = use_local || version.blank? ?
- "#{['webfont', version].compact.join('-')}.min.js" :
- "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/webfont/#{version}/webfont.js"
- else nil # TODO: Write to log
+ # Using the local URL requires that the vendor file exists locally. If
+ # the vendor file doesn't exist, use the remote URL as fallback.
+ use_local &&= AssetHat.asset_exists?(local_src, :js)
+
+ # If no version given, can't determine the remote URL; use the local
+ # URL as fallback.
+ use_local ||= version.blank?
+
+ if use_local
+ src = local_src
+ else
+ # To ease setup, if no local copy of the vendor code is found,
+ # use a remote URL as a fallback.
+
+ # Give precedence to configured remote URLs
+ src = vendor_config.try(:[], 'remote_ssl_url') if use_ssl
+ src ||= vendor_config.try(:[], 'remote_url')
+
+ # Use default remote URL as fallback
+ src ||= remote_src
+
+ # Use local URL as final resort, even though the file doesn't
+ # exist, in hopes that the app maintainer finds the 404 (or the
+ # warning below) in the logs. This needs to be fixed in the app,
+ # rather than relying on a CDN to dynamically provide the latest
+ # stable vendor version.
+ if src.blank?
+ src = local_src
+ Rails.logger.warn "\n\nAssetHat WARNING (#{Time.now}):\n" + %{
+ Tried to reference the vendor JS `:#{vendor}`, but
+ #{AssetHat.assets_dir(:js)}/#{local_src} couldn't be found, and
+ couldn't use a remote fallback because no vendor version was
+ given in #{AssetHat::RELATIVE_CONFIG_FILEPATH}.
+ }.squish!
+ # TODO: Create `AssetHat::Logger.warn`, etc. methods
end
end
src
end
+
+
+
+ private
+
+ def self.vendor_uris(vendor, options={})
+ # Returns a hash with keys `:local` and `:remote`.
+
+ uris = {}
+ use_ssl = options[:use_ssl]
+ version = options[:version]
+
+ case vendor
+ when :jquery
+ uris[:local ] = "#{['jquery', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery.min.js"
+ when :jquery_ui
+ uris[:local ] = "#{['jquery-ui', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jqueryui/#{version}/jquery-ui.min.js"
+ when :prototype
+ # Prototype currently doesn't provide an official minified version.
+ uris[:local ] = "#{['prototype', version].compact.join('-')}.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/prototype/#{version}/prototype.js"
+ when :scriptaculous
+ # script.aculo.us currently doesn't provide an official minified version.
+ uris[:local ] = "#{['scriptaculous', version].compact.join('-')}.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/scriptaculous/#{version}/scriptaculous.js"
+ when :mootools
+ uris[:local ] = "#{['mootools', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/mootools/#{version}/mootools-yui-compressed.js"
+ when :dojo
+ uris[:local ] = "#{['dojo', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/dojo/#{version}/dojo/dojo.xd.js"
+ when :swfobject
+ uris[:local ] = "#{['swfobject', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/swfobject/#{version}/swfobject.js"
+ when :yui
+ uris[:local ] = "#{['yui', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/yui/#{version}/build/yuiloader/yuiloader-min.js"
+ when :ext_core
+ uris[:local ] = "#{['ext-core', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/ext-core/#{version}/ext-core.js"
+ when :webfont
+ uris[:local ] = "#{['webfont', version].compact.join('-')}.min.js"
+ uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/webfont/#{version}/webfont.js"
+ when :lab_js
+ uris[:local ] = "#{['LAB', version].compact.join('-')}.min.js"
+
+ remote_host =
+ if use_ssl
+ 'https://d3eee1nukb5wg.cloudfront.net/'
+ # This must match the value in the cdnjs repo:
+ # https://github.com/cdnjs/cdnjs/raw/master/https_location
+ #
+ # Amazon CloudFront doesn't support SSL, as discussed here:
+ # - http://www.cdnjs.com/#IDComment130405257
+ # - https://forums.aws.amazon.com/message.jspa?messageID=141951
+ # As a result, the SSL certificate at <https://cdnjs.com> is
+ # invalid. To work around this, we instead load assets via
+ # cdnjs's CloudFront bucket ID. The bucket ID may change in
+ # the future, so it should be synced with the host published
+ # in the cdnjs repo, as noted above.
+ #
+ # For complete control over this, you can simply download the
+ # vendor JS and host it on a server where you can maintain
+ # SSL certificates.
+ else
+ 'http://ajax.cdnjs.com'
+ end
+ uris[:remote] = "#{remote_host}/ajax/libs/labjs/#{version}/LAB.min.js"
+ else nil # TODO: Write to log
+ end
+
+ # The remote URL can only be properly determined if the version number
+ # is known; otherwise, discard.
+ uris.delete(:remote) if version.blank?
+
+ uris
+ end
+
+ # Usage (currently only supports LABjs):
+ #
+ # AssetHat::JS::Vendors.loader_js :lab_js,
+ # :urls => ['/javascripts/app.js',
+ # 'http://cdn.example.com/jquery.js']
+ #
+ # Returns a string of JS:
+ #
+ # window.$LABinst=$LAB.
+ # script('/javascripts/app.js').wait().
+ # script('http://cdn.example.com/jquery.js').wait();
+ def self.loader_js(loader, opts)
+ return nil unless opts[:urls]
+
+ case loader
+ when :lab_js
+ urls = opts[:urls]
+ lines = ['window.$LABinst=$LAB.']
+ urls.each_with_index do |url, i|
+ is_last = i >= urls.length - 1
+ lines << " script('#{url}').wait()#{is_last ? ';' : '.'}"
+ # Use `wait()` for each script to load in parallel, but
+ # preserve execution order by default. Alternatively, call
+ # `$LAB.setOptions({AlwaysPreserveOrder:true})` at the start
+ # of the chain, but if the list of bundles to include is short,
+ # this may use even more bytes.
+ end
+ lines.join("\n")
+ end
+ end
+
end
end
end