require "rack/jquery_ui/themes/version" require "rack/jquery/helpers" module Rack class JQueryUI # jQuery-UI themes' CDN script tags and fallback in one neat package. class Themes include JQuery::Helpers JQUERY_UI_THEME_FILE = "jquery-ui.min.css" # Script tags for the Media Temple CDN MEDIA_TEMPLE = "<link rel='stylesheet' href='http://code.jquery.com/ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/jquery-ui.css' type='text/css' />" # Script tags for the Google CDN GOOGLE = "<link rel='stylesheet' href='//ajax.googleapis.com/ajax/libs/jqueryui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/jquery-ui.css' type='text/css' />" # Script tags for the Microsoft CDN MICROSOFT = "<link rel='stylesheet' href='//ajax.microsoft.com/ajax/jquery.ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/jquery-ui.css' type='text/css' />" # This javascript checks if the jQuery-UI object has loaded. If not, that most likely means the CDN is unreachable, so it uses the local jQuery-UI theme. FALLBACK = <<STR <script type="text/javascript"> $.each(document.styleSheets, function(i,sheet){ if(sheet.href=='http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css') { var rules = sheet.rules ? sheet.rules : sheet.cssRules; if (rules.length == 0) { $('<link rel="stylesheet" type="text/css" href="/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/#{JQUERY_UI_THEME_FILE}" />').appendTo('head'); } } }) </script> STR STANDARD_THEMES = %w{base black-tie blitzer cupertino dark-hive dot-luv eggplant excite-bike flick hot-sneaks humanity le-frog mint-choc overcast pepper-grinder redmond smoothness south-street start sunny swanky-purse trontastic ui-darkness ui-lightness vader} def self.theme @theme ||= "base" end def self.theme=( name ) fail ArgumentError, "That theme (#{name}) is unknown for this version of the rack-jquery_ui-themes library." unless STANDARD_THEMES.include? name @theme = name end # @param [Symbol] :organisation Choose which CDN to use, either :google, :microsoft or :media_temple # @param [Hash] opts # @option opts [#to_s] :theme # @return [String] The HTML script tags to get the CDN. # @example # Rack::JQueryUI::Themes.cdn :google, theme: "dot-luv" def self.cdn( organisation=:google, opts={} ) theme = opts.fetch :theme, self.theme fail ArgumentError, "That theme (#{theme}) is unknown for this version of the rack-jquery_ui-themes library." unless STANDARD_THEMES.include? theme script = case organisation when :media_temple then MEDIA_TEMPLE when :microsoft then MICROSOFT else GOOGLE end [script,FALLBACK].map{|x| x.sub(/\:THEME/, theme) }.join("\n") end # Default options hash for the middleware. DEFAULT_APP_OPTIONS = { :http_path => "/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/" } # @param [#call] app # @param [Hash] options # @option options [String] theme The theme to use. The default is "base". # @option options [String] :http_path If you wish the jQuery CSS fallback route to be "/js/jquery-ui/1.10.1/base/jquery-ui.min.css" (or whichever version this is at) then do nothing, that's the default. If you want the path to be "/assets/javascripts/jquery-ui/1.10.1/baseā¦" then pass in `:http_path => "/assets/javascripts/#{Rack::JQueryUI::JQUERY_UI_VERSION}". # @note ***Don't leave out the version number!***. The scripts provided by jQuery don't contain the version in the filename like the jQuery scripts do, which means that organising them and sending the right headers back is bound to go wrong unless you put the version number somewhere in the route. You have been warned! # @example # # The default: # use Rack::JQueryUI::Themes def initialize( app, options={} ) @app, @options = app, DEFAULT_APP_OPTIONS.merge(options) self.class.theme = options[:theme] unless options[:theme].nil? @http_path_to_jquery_css = ::File.join( @options[:http_path], self.class.theme, JQUERY_UI_THEME_FILE ) @http_path_to_jquery_images = ::File.join( @options[:http_path], self.class.theme, "images" ) # set the path to all the assets rel_path_to_vendor = "../../../../" path_to_themes = "vendor/assets/javascripts/jquery-ui" version_and_theme = "#{JQueryUI::JQUERY_UI_VERSION}/themes/#{self.class.theme}" @path_to_theme = ::File.expand_path( ::File.join( rel_path_to_vendor, path_to_themes, version_and_theme ), __FILE__) @path_to_images = ::File.join @path_to_theme, "images" d = Dir.new(@path_to_images) @images = d.entries.delete_if{|f| f =~ /^\./ }.select{|f| Rack::Mime.mime_type(::File.extname(f), "text/css" ).start_with? "image" } end # @param [Hash] env Rack request environment hash. def call( env ) dup._call env end # For thread safety # @param (see #call) def _call( env ) request = Rack::Request.new(env.dup) # TODO path for images # path for CSS if request.path_info.start_with? @options[:http_path] response = Rack::Response.new if request.path_info.end_with? ::File.join(self.class.theme,JQUERY_UI_THEME_FILE) # serve CSS # for caching response.headers.merge! caching_headers("#{JQUERY_UI_THEME_FILE}-#{self.class.theme}-#{JQueryUI::JQUERY_UI_VERSION}", JQueryUI::JQUERY_UI_VERSION_DATE) # There's no need to test if the IF_MODIFIED_SINCE against the release date because the header will only be passed if the file was previously accessed by the requester, and the file is never updated. If it is updated then it is accessed by a different path. requested_file = open(::File.join(@path_to_theme, JQUERY_UI_THEME_FILE ), "r") elsif request.path_info =~ /images/ && (fin = request.path_info.split("/").last) (img = @images.find{|img| img == fin }) # serve images requested_file = open ::File.join( @path_to_images, img), "rb" else # bad route response.status = 404 return response.finish # finish early, 404 end if request.env['HTTP_IF_MODIFIED_SINCE'] response.status = 304 else response.status = 200 mime = Mime.mime_type(::File.extname(requested_file.path), 'text/css') response.headers.merge!( "Content-Type" => mime ) if mime response.write ::File.read( requested_file ) end response.finish else @app.call(env) end end # call end # Themes end end