Removed superfluous 'tracks' directory at the root of the repository.

Testing commits to github.
This commit is contained in:
bsag 2008-05-20 21:28:26 +01:00
parent 6a42901514
commit 4cbf5a34d3
2269 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,17 @@
module ActionView::Helpers
def self.current_controller=(controller)
@@current_controller = controller
end
def self.current_controller
@@current_controller
end
end
class ActionView::Helpers::InstanceTag
include UJS::Helpers
def current_controller
ActionView::Helpers.current_controller
end
end

View file

@ -0,0 +1,34 @@
module ActionView::Helpers::AssetTagHelper
alias_method :rails_javascript_include_tag, :javascript_include_tag
# Adds a new option to Rails' built-in <tt>javascript_include_tag</tt>
# helper - <tt>:unobtrusive</tt>. Works in the same way as <tt>:defaults</tt> - specifying
# <tt>:unobtrusive</tt> will make sure the necessary javascript
# libraries and behaviours file +script+ tags are loaded. Will happily
# work along side <tt>:defaults</tt>.
#
# <%= javascript_include_tag :defaults, :unobtrusive %>
#
# This replaces the old +unobtrusive_javascript_files+ helper.
def javascript_include_tag(*sources)
if sources.delete :unobtrusive
sources = sources.concat(
#['lowpro', behaviours_url] ## This is the original. Hacking to make asset_packager more effective
[behaviours_url]
).uniq
end
rails_javascript_include_tag(*sources)
end
protected
def behaviours_url
action_path = case @controller.request.path
when '', '/'
'/index'
else
@controller.request.path
end
"/behaviours#{action_path}.js"
end
end

View file

@ -0,0 +1,70 @@
module UJS
module BehaviourCaching
def self.included(base)
base.extend ControllerClassMethods
end
attr_accessor :cache_behaviours
module ControllerClassMethods
# Also file caching of the behaviour for hidden actions. This actually
# write the javascript file into the public directory (or given cache dir)
# in the same style as page caching. Which means requests for the behaviour
# will not hit the application at all. Use this for reasonably static behaviours.
#
# NB. If you use caches_page or caches_action your behaviours will automatically ne
# cached in this way. expire_page and expire_action also automatically expire the
# cached behaviour.
#
# caches_behaviour :index, :register
#
# See expire_behaviour to expire cached behaviours.
def caches_behaviour(*actions)
actions.each do |action|
class_eval "prepend_before_filter { |c| c.cache_behaviours = (c.action_name == '#{action}') }"
end
end
alias_method :caches_behavior, :caches_behaviour
def caches_page(*actions)
caches_behaviour(*actions)
super
end
def caches_action(*actions)
caches_behaviour(*actions)
super
end
end
# Expires the behaviours for the given url options.
#
# expire_behaviour :action => 'index'
def expire_behaviour(options={})
self.class.expire_page "/behaviour#{url_for(options)}.js"
end
alias_method :expire_behavior, :expire_behaviour
def expire_page(options={})
expire_behaviour(options)
super
end
def expire_action(options={})
if options[:action].is_a?(Array)
options[:action].dup.each do |action|
expire_behaviour(options.merge({ :action => action }))
end
else
expire_behaviour(options)
end
super
end
end
end

View file

@ -0,0 +1,52 @@
require 'digest/md5'
class UnobtrusiveJavascriptController < ActionController::Base
skip_before_filter :initialise_js_behaviours
skip_after_filter :store_js_behaviours
after_filter :perform_any_caching
after_filter :reset_js_behaviours
# Renders the external javascript behaviours file
# with the appropriate content-type.
def generate
headers['Content-Type'] = 'text/javascript'
if js_behaviours
generate_etag
modified? ? render_script : render_304
else
render :text => "", :layout => false
end
end
protected
def perform_any_caching
if behaviour_caching_enabled?
self.class.cache_page js_behaviours.to_s, request.path
end
end
private
def generate_etag
headers['ETag'] = Digest::MD5.hexdigest(js_behaviours.to_s)
end
def modified?
request.env['HTTP_IF_NONE_MATCH'] != headers['ETag']
end
def render_script
render :text => js_behaviours.to_s, :layout => false
end
def render_304
render :nothing => true, :status => 304
end
def behaviour_caching_enabled?
js_behaviours && js_behaviours.cache?
end
end

View file

@ -0,0 +1,72 @@
module ActionView::Helpers::PrototypeHelper
class JavaScriptRef
def initialize(ref); @ref = ref; end
def to_json; @ref; end
end
def javascript_variable(name)
JavaScriptRef.new(name)
end
def build_observer(klass, name, options={})
set_default_external!(options)
javascript = build_observer_js(klass, name, options)
unless options.delete :external
javascript_tag(javascript)
else
@controller.apply_behaviour("##{name}", javascript)
return ''
end
end
def build_observer_js(klass, name, options)
if options[:with] && !options[:with].include?("=")
options[:with] = "'#{options[:with]}=' + value"
else
options[:with] ||= 'value' if options[:update]
end
callback = options[:function] || remote_function(options)
javascript = "new #{klass}(#{name.to_json}, "
javascript << "#{options[:frequency]}, " if options[:frequency]
javascript << "function(element, value) {"
javascript << "#{callback}}"
javascript << ", '#{options[:on]}'" if options[:on]
javascript << ")"
javascript
end
def remote_function(options)
javascript_options = options_for_ajax(options)
update = ''
if options[:update] and options[:update].is_a?Hash
update = []
update << "success:'#{options[:update][:success]}'" if options[:update][:success]
update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
update = '{' + update.join(',') + '}'
elsif options[:update]
update << "'#{options[:update]}'"
end
function = update.empty? ?
"new Ajax.Request(" :
"new Ajax.Updater(#{update}, "
url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a? Hash
url = url_options.is_a?(JavaScriptRef) ? url_options.to_json : "'#{url_for(url_options)}'"
function << "#{url}"
function << ", #{javascript_options})"
function = "#{options[:before]}; #{function}" if options[:before]
function = "#{function}; #{options[:after]}" if options[:after]
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
return function
end
end

View file

@ -0,0 +1,62 @@
module ActionView::Helpers::ScriptaculousHelper
def draggable_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, draggable_element_js(element_id, options).chop!, external)
end
def draggable_element_js(element_id, options = {}) #:nodoc:
%(new Draggable(#{element_id.to_json}, #{options_for_javascript(options)});)
end
def drop_recieving_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, drop_receiving_element_js(element_id, options).chop!, external)
end
def drop_receiving_element_js(element_id, options = {}) #:nodoc:
options[:with] ||= "'id=' + encodeURIComponent(element.id)"
options[:onDrop] ||= "function(element){" + remote_function(options) + "}"
options.delete_if { |key, value| ActionView::Helpers::PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
%(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});)
end
def sortable_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, sortable_element_js(element_id, options).chop!, external)
end
def sortable_element_js(element_id, options = {}) #:nodoc:
options[:with] ||= "Sortable.serialize(#{element_id.to_json})"
options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
options.delete_if { |key, value| ActionView::Helpers::PrototypeHelper::AJAX_OPTIONS.include?(key) }
[:tag, :overlap, :constraint, :handle].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
%(Sortable.create(#{element_id.to_json}, #{options_for_javascript(options)});)
end
protected
def prepare_script(element_id, js, external=true)
unless external
javascript_tag(js)
else
@controller.apply_behaviour "##{element_id}", js
return ''
end
end
end

View file

@ -0,0 +1,37 @@
module ActionView::Helpers::TagHelper
include UJS::Helpers
JAVASCRIPT_EVENTS = %w(click mouseup mousedown dblclick mousemove mouseover mouseout submit change keypress keyup keydown load)
alias_method :rails_tag_options, :tag_options
protected
# Patch to the built-in Rails tag_options method. Looks for any
# javascript event handlers, extracts them and registers them
# as unobtrusive behaviours.
#
# This behaviour affects any built-in Rails helpers that generate
# HTML. Event extraction behaviour can be bypassed by passing in
# <tt>:inline => true</tt> as part of a helper's HTML options hash.
def tag_options(opts, escape = true)
set_default_external!(opts)
if opts[:external]
JAVASCRIPT_EVENTS.each do |event|
unless opts["on#{event}"].blank?
opts['id'] = generate_html_id unless opts['id']
apply_behaviour("##{opts['id']}:#{event}", opts["on#{event}"]) unless opts["on#{event}"].nil?
opts.delete("on#{event}")
end
end
opts.delete(:external)
end
rails_tag_options(opts, escape)
end
# Generate a unique id to be used as the HTML +id+ attribute.
def generate_html_id
@tag_counter ||= 0
@tag_counter = @tag_counter.next
"#{UJS::Settings.generated_id_prefix}#{@tag_counter}"
end
end

View file

@ -0,0 +1,32 @@
module UJS
PLUGIN_NAME = 'unobtrusive_javascript'
PLUGIN_PATH = "#{RAILS_ROOT}/vendor/plugins/#{PLUGIN_NAME}"
PLUGIN_ASSET_PATH = "#{PLUGIN_PATH}/assets"
PLUGIN_CONTROLLER_PATH = "#{PLUGIN_PATH}/lib/controllers"
class Settings
# All elements with attached behaviours that do not
# have an HTML +id+ attribute will have one
# generated automatically, using the form _prefix_x_,
# where the default prefix is "uj_element_" and x is an
# automatically incremented number. You can set the
# generated prefix to anything you like by setting it in your
# environment.rb file:
#
# UJS::Settings.generated_id_prefix = "my_prefix_"
cattr_accessor :generated_id_prefix
@@generated_id_prefix = "uj_element_"
end
class << self
# Adds routes to your application necessary for the plugin to function correctly.
# Simply add the following inside your Routes.draw block in routes.rb:
# UJS::routes
# This is now *mandatory*.
def routes
ActionController::Routing::Routes.add_route "/behaviours/*page_path", :controller => "unobtrusive_javascript", :action => "generate"
end
alias_method :use_fake_script_links, :routes
end
end

View file

@ -0,0 +1,62 @@
module UJS
# The helper methods in this module can all be passed as arguments to apply_behaviour and allow
# you to reproduce common functionailty such as drag and drop and sorting in a simple way. Think
# of them as the apply_javascript equivilent to the Scriptaculous helpers.
#
# Usage:
#
# apply_behaviour '.products', make_draggable
# apply_behaviour '.help', make_remote_link( :action => 'showhelp' )
# apply_behaviour '.todolist', make_sortable
#
module BehaviourHelper
# Make a link send an Ajax request. options identical to linkt_to_remote except that
# :url defaults to the href attribute of the link.
def make_remote_link(options={})
options[:url] ||= javascript_variable('this.href');
"this.observe('click', function(event) {\n#{remote_function(options)};\nreturn false;\n});"
end
# Make a form submit via an Ajax request. options identical remote_form except that
# :url defaults to the action attribute of the form.
def make_remote_form(options={})
options[:url] ||= javascript_variable('this.action');
options[:with] ||= 'Form.serialize(this)'
"this.observe('submit', function(event) {\n#{remote_function(options)};\nreturn false;\n});"
end
# Observe a form or form field (specified with the type argument) using the given options
# which are the same as observe_form and observe_field.
def make_observed(type, options={})
obs = (type.to_s == 'form') ? 'Form' : 'Form.Element'
if options[:frequency] && options[:frequency] > 0
build_observer_js("#{obs}.Observer", javascript_variable('this'), options)
else
build_observer_js("#{obs}.EventObserver", javascript_variable('this'), options)
end
end
# Makes the children of the element sortable.
def make_sortable(options={})
sortable_element_js(javascript_variable('this'), options)
end
# Makes the element draggable.
def make_draggable(options={})
draggable_element_js(javascript_variable('this'), options)
end
# Makes the element a drop target.
def make_drop_receiving(options={})
drop_receiving_element_js(javascript_variable('this'), options)
end
def make_autocomplete # :nodoc:
end
def make_in_place_editor # :nodoc:
end
end
end

View file

@ -0,0 +1,58 @@
class UJS::BehaviourScript
attr_reader :rules
attr_writer :reapply_after_ajax
def initialize(cache=false, reapply_after_ajax=true)
@rules, @cache, @reapply_after_ajax = [], cache, reapply_after_ajax
end
def add_rule(selector, javascript, cancel_default=false)
javascript = javascript << cancel_default_js if cancel_default
@rules << [selector, javascript] unless rule_exists(selector, javascript)
end
def cache?
@cache
end
def enable_cache
@cache = true
end
def reapply_after_ajax?
@reapply_after_ajax
end
# Renders behaviour block and option JavaScript.
def to_s
(@rules && !@rules.empty?) ? "Event.addBehavior({\n#{rule_js}\n});" + option_js : ''
end
# Uses behaviour script converter to conver to a hash for session storage
def to_hash
UJS::BehaviourScriptConverter.convert_to_hash(self)
end
protected
# Renders a collection of behaviour rules in javascript format
def rule_js
@rules.uniq.collect { |sel, js| behaviour_rule(sel, js) }.join(",\n")
end
# Renders behaviour rule javascript for the behaviours file
def behaviour_rule(selector, behaviour)
"\"#{selector}\": function(event) {\n#{behaviour}\n}"
end
def option_js
reapply_after_ajax? ? '' : "\nEvent.addBehavior.reapplyAfterAjax = false;"
end
def cancel_default_js
" return false;"
end
def rule_exists(selector, javascript)
@rules.detect{|r| r[0] == selector && r[1] == javascript} != nil
end
end

View file

@ -0,0 +1,23 @@
class UJS::BehaviourScriptConverter
def initialize(script)
@script = script
end
# Converts a BehaviourScript object into a custom hash format
def self.convert_to_hash(script)
self.new(script).to_hash
end
# Converts a hash-converted BehaviourScript back to a BehaviourScript again
def self.convert_from_hash(script_hash)
script = UJS::BehaviourScript.new(script_hash[:options][:cache], script_hash[:options][:reapply_after_ajax])
script_hash[:rules].each { |r| script.add_rule(r[0], r[1]) }
script
end
# Convert behaviour script to a hash
def to_hash
{ :options => { :cache => @script.cache?, :reapply_after_ajax => @script.reapply_after_ajax? },
:rules => @script.rules }
end
end

View file

@ -0,0 +1,55 @@
module UJS::ControllerMethods
def self.included(base)
base.class_eval do
before_filter :store_controller_for_helpers
before_filter :initialise_js_behaviours
after_filter :store_js_behaviours
end
end
# Lets you register javascript behaviours from within
# the controller. For a description of the different options
# available, see UJS::Helpers#apply_behaviour (note:
# this function does not take a block like the view helper version)
def apply_behaviour(selector, behaviour, opts={})
opts.reverse_merge!(:prevent_default => false)
@js_behaviours.add_rule(selector, behaviour, opts[:prevent_default])
end
def cache_behaviours
@js_behaviours.enable_cache
end
def reapply_behaviours_after_ajax=(val)
@js_behaviours.reapply_after_ajax = val
end
# Make American and English spellings both work for apply_behaviour
alias_method :apply_behavior, :apply_behaviour
protected
# Initialises the javascript behaviours
def initialise_js_behaviours
@js_behaviours = UJS::BehaviourScript.new(@cache_behaviours)
end
# Clears the array of registered javascript behaviours
def reset_js_behaviours
session[:js_behaviours] = nil
end
# Returns a BehaviourScript from the behaviours serialized to the session
def js_behaviours
return nil if session[:js_behaviours].nil?
UJS::BehaviourScriptConverter.convert_from_hash(session[:js_behaviours])
end
# Stores all registered javascript behaviours in the session as a hash
def store_js_behaviours
session[:js_behaviours] = @js_behaviours.to_hash
end
def store_controller_for_helpers
ActionView::Helpers.current_controller = self
end
end

View file

@ -0,0 +1,152 @@
require File.dirname(__FILE__) + '/behaviour_helper'
require File.dirname(__FILE__) + '/javascript_proxies'
module UJS::Helpers
include UJS::BehaviourHelper
include UJS::JavascriptProxies
# This is the core functionality of the plugin;
# it allows you to attach javascript behaviour to your page
# elements in an unobtrusive fashion. It takes three options:
# * +selector+ - CSS selector and event. For a full overview of
# the selector syntax, see the event:Selectors website.
# http://encytemedia.com/event-selectors
# * +behaviour+ - The javascript that you want to attach to the element
# and event specified by +selector+ as a string of javascript.
# * +opts+ - A hash of additional options.
#
# Attaching a behaviour to an element on your page is as simple as
# specifying the element and the event you want the behaviour attached
# to, using the CSS selector format, and passing in a string of javascript:
#
# <% apply_behaviour "#coollink:click", "alert('Hello World')" %>
#
# You can also make use of any of the built-in Rails helpers that
# generate Javascript:
#
# <% apply_behaviour "#coolink:click", visual_effect(:highlight, "coollink") %>
#
# You will have access to two javascript variables inside your javascript string:
# * +this+: returns a reference to the HTML element that the behaviour was attached to
# * +event+: the event the behaviour was attached to:
#
# <% apply_behaviour "#coollink:click", "alert('You clicked ' this.id); Event.stop(event)" %>
#
# The following options can be set using the opts hash:
#
# * <tt>:external</tt> - If true, the behaviour will be attached to the external behaviour file.
# If false, it will be rendered directly in the page inside +script+ tags. Defaults to true.
#
# When setting <tt>:external</tt> to false, you <em>must</em> call the register_js_behaviour from
# within ERb <em>output</em> blocks. If <tt>:external</tt> is true, you can use either output or non-output blocks.
#
# If you set :prevent_default to true the default action of the event is stopped (similar to calling event.stop).
# This is very useful for hijacking links in order to provide some other behaviour.
#
# You can also pass a block to the function instead of a string of javascript -
# the block will be passed in a JavascriptGenerator object (+page+), the element (optional)
# and the event (optional). You can use the Javascript generator
# to write your attached behaviour using Ruby:
#
# <% apply_behaviour "#coollink:click" do |page, element, event|
# page.alert("Hi there, I'm going to fade away...")
# page.visual_effect :fade, element
# end %>
def apply_behaviour(selector, *args, &block) #:yields: page, element, event
opts = args.last.is_a?(Hash) ? args.pop : {}
set_default_external!(opts)
behaviour = normalise_behaviour_string(args.first || '')
if block_given?
generator = new_javascript_generator
args = [generator, element_proxy(generator), event_proxy(generator)][0..block.arity-1]
@template.instance_exec(*args, &block)
behaviour = generator.to_s
end
if !opts[:external]
render_inline_behaviour_block(selector, behaviour, opts)
else
@controller.apply_behaviour(selector, behaviour, opts) and return ''
end
end
# Allow you to apply multiple behaviours with block syntax.
#
# apply_behaviours do
# on 'a:click', 'alert("boo")'
# on 'abbr:mouseover', 'showAbbr(this)'
# end
def apply_behaviours(&block)
mapper = BehaviourMapper.new(self).instance_eval &block
mapper.to_s
end
# Make sure American and English spellings both work
alias_method :apply_behavior, :apply_behaviour
alias_method :apply_behaviors, :apply_behaviours
protected
# A convenient mapper class for applying
# multiple behaviours at once.
class BehaviourMapper
def initialize(base)
@base = base
@output = ''
end
def on(*args, &block)
@output << @base.apply_behaviour(*args, &block)
end
# delegate all other method calls to the view
def method_missing(meth, *args)
@base.send meth, *args
end
def to_s
@output
end
end
# Renders a block of javascript behaviours inside +script+ tags
# directly within the page
def render_inline_behaviour_block(selector, behaviour, opts)
script = UJS::BehaviourScript.new
script.add_rule(selector, behaviour)
javascript_tag(script.to_s)
end
# Use this to set the default :external option for helper calls.
# it set external to false if its an xhr request or false if it's a normal request.
def set_default_external!(options)
options.reverse_merge!(:external => !current_controller.request.xhr?)
end
# Returns a new JavascriptArgumentProxy for the element the behaviour
# has been applied to.
def element_proxy(generator)
UJS::JavascriptProxies::JavascriptArgumentProxy.new(UJS::JavascriptProxies::ReferencedJavascriptElementProxy, generator, 'this')
end
# Returns a new JavascriptArgumentProxy for the event the behaviour has
# been applied to.
def event_proxy(generator)
UJS::JavascriptProxies::JavascriptArgumentProxy.new(UJS::JavascriptProxies::JavascriptEventProxy, generator, 'event')
end
# Returns a new JavasScriptGenerator object
def new_javascript_generator
ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template) { }
end
def current_controller
@controller
end
def normalise_behaviour_string(behaviour)
behaviour << ';' unless behaviour =~ /;$/
behaviour
end
end

View file

@ -0,0 +1,57 @@
module ActionView
module Helpers
class JavaScriptProxy
public :method_missing
protected
def wrap(function, *args)
args = [function_chain[-1].chomp(';')].concat(args.collect(&:to_json)).join(', ')
replace_line("#{function.to_s}(#{args});")
end
def replace_line(new_line)
function_chain[-1] = new_line
end
end
end
end
module UJS
module JavascriptProxies
class ReferencedJavascriptElementProxy < ActionView::Helpers::JavaScriptElementProxy
def initialize(generator, var)
@generator = generator
@generator << var
end
def reload; end
end
class JavascriptArgumentProxy
def initialize(proxy, *contructor_args)
@proxy, @args = proxy, contructor_args
end
def method_missing(meth, *args)
proxy = @proxy.new(*@args)
proxy.__send__(meth, *args)
end
end
class JavascriptEventProxy < ActionView::Helpers::JavaScriptProxy
def stop
wrap('Event.stop')
end
def element
wrap('Event.element')
end
end
end
end