[Ovirt-devel] [PATCH server 1/8] Update core app config for Rails 2.3.2
Joey Boggs
jboggs at redhat.com
Mon Jul 20 15:56:25 UTC 2009
Jason Guiditta wrote:
> This patch updates app config and makes any
> required filename changes to support rails 2.3.2.
>
> Notable changes are:
> * application.rb -> application_controller.rb
> * cleanup of environment.rb
> * new config/initializers dir (this is where much of
> the environment.rb stuff went)
> * gettext/rails -> gettext_rails
>
> Also note that applying this patch makes ovirt server
> no longer run on Fedora 10, as Rails 2.3.2 is not in that
> yum repo. If you wish to run the app on F10 after this
> upgrade, you can probably still do so with:
> gem update rails
> but of course this has the potential to cause issues with
> yum and gem conflicting.
>
> Signed-off-by: Jason Guiditta <jason.guiditt at gmail.com>
> Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com>
> ---
> src/app/controllers/application.rb | 200 --------
> src/app/controllers/application_controller.rb | 198 ++++++++
> src/config.ru | 7 +
> src/config/boot.rb | 11 +-
> src/config/environment.rb | 38 +--
> src/config/initializers/backtrace_silencers.rb | 7 +
> src/config/initializers/inflections.rb | 10 +
> src/config/initializers/mime_types.rb | 5 +
> src/config/initializers/new_rails_defaults.rb | 19 +
> src/config/initializers/session_store.rb | 15 +
> src/public/javascripts/controls.js | 136 +++---
> src/public/javascripts/dragdrop.js | 175 ++++----
> src/public/javascripts/effects.js | 130 +++---
> src/public/javascripts/prototype.js | 629 ++++++++++++++----------
> 14 files changed, 861 insertions(+), 719 deletions(-)
> delete mode 100644 src/app/controllers/application.rb
> create mode 100644 src/app/controllers/application_controller.rb
> create mode 100644 src/config.ru
> create mode 100644 src/config/initializers/backtrace_silencers.rb
> create mode 100644 src/config/initializers/inflections.rb
> create mode 100644 src/config/initializers/mime_types.rb
> create mode 100644 src/config/initializers/new_rails_defaults.rb
> create mode 100644 src/config/initializers/session_store.rb
>
> diff --git a/src/app/controllers/application.rb b/src/app/controllers/application.rb
> deleted file mode 100644
> index e50f71e..0000000
> --- a/src/app/controllers/application.rb
> +++ /dev/null
> @@ -1,200 +0,0 @@
> -#
> -# Copyright (C) 2008 Red Hat, Inc.
> -# Written by Scott Seago <sseago at redhat.com>
> -#
> -# This program is free software; you can redistribute it and/or modify
> -# it under the terms of the GNU General Public License as published by
> -# the Free Software Foundation; version 2 of the License.
> -#
> -# This program is distributed in the hope that it will be useful,
> -# but WITHOUT ANY WARRANTY; without even the implied warranty of
> -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> -# GNU General Public License for more details.
> -#
> -# You should have received a copy of the GNU General Public License
> -# along with this program; if not, write to the Free Software
> -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> -# MA 02110-1301, USA. A copy of the GNU General Public License is
> -# also available at http://www.gnu.org/copyleft/gpl.html.
> -
> -# Filters added to this controller apply to all controllers in the application.
> -# Likewise, all the methods added will be available for all controllers.
> -
> -
> -class ApplicationController < ActionController::Base
> - # FIXME: once all controller classes include this, remove here
> - include ApplicationService
> -
> - # Pick a unique cookie name to distinguish our session data from others'
> - session :session_key => '_ovirt_session_id'
> - init_gettext "ovirt"
> - layout :choose_layout
> -
> - before_filter :is_logged_in, :get_help_section
> -
> - # General error handlers, must be in order from least specific
> - # to most specific
> - rescue_from Exception, :with => :handle_general_error
> - rescue_from PermissionError, :with => :handle_perm_error
> - rescue_from ActionError, :with => :handle_action_error
> - rescue_from PartialSuccessError, :with => :handle_partial_success_error
> -
> - def choose_layout
> - if(params[:component_layout])
> - return (ENV["RAILS_ENV"] != "production")?'components/' << params[:component_layout]:'redux'
> - end
> - return 'redux'
> - end
> -
> - def is_logged_in
> - redirect_to(:controller => "/login", :action => "login") unless get_login_user
> - end
> -
> - def get_help_section
> - help = HelpSection.find(:first, :conditions => [ "controller = ? AND action = ?", controller_name, action_name ])
> - @help_section = help ? help.section : ""
> - if @help_section.index('#')
> - help_sections = @help_section.split('#')
> - @help_section = help_sections[0]
> - @anchor = help_sections[1]
> - else
> - @help_section = @help_section
> - @anchor = ""
> - end
> - end
> -
> - def get_login_user
> - (ENV["RAILS_ENV"] == "production") ? session[:user] : "ovirtadmin"
> - end
> -
> - protected
> - # permissions checking
> -
> - def handle_perm_error(error)
> - handle_error(:error => error, :status => :forbidden,
> - :title => "Access denied")
> - end
> -
> - def handle_partial_success_error(error)
> - failures_arr = error.failures.collect do |resource, reason|
> - if resource.respond_to?(:display_name)
> - resource.display_name + ": " + reason
> - else
> - reason
> - end
> - end
> - @successes = error.successes
> - @failures = error.failures
> - handle_error(:error => error, :status => :ok,
> - :message => error.message + ": " + failures_arr.join(", "),
> - :title => "Some actions failed")
> - end
> -
> - def handle_action_error(error)
> - handle_error(:error => error, :status => :conflict,
> - :title => "Action Error")
> - end
> -
> - def handle_general_error(error)
> - flash[:errmsg] = error.message
> - handle_error(:error => error, :status => :internal_server_error,
> - :title => "Internal Server Error")
> - end
> -
> - def handle_error(hash)
> - log_error(hash[:error]) if hash[:error]
> - msg = hash[:message] || hash[:error].message
> - title = hash[:title] || "Internal Server Error"
> - status = hash[:status] || :internal_server_error
> - respond_to do |format|
> - format.html { html_error_page(title, msg) }
> - format.json { render :json => json_error_hash(msg, status) }
> - format.xml { render :xml => xml_errors(msg), :status => status }
> - end
> - end
> -
> - def html_error_page(title, msg)
> - @title = title
> - @errmsg = msg
> - @ajax = params[:ajax]
> - @nolayout = params[:nolayout]
> - if @layout
> - render :layout => @layout
> - elsif @ajax
> - render :template => 'layouts/popup-error', :layout => 'tabs-and-content'
> - elsif @nolayout
> - render :template => 'layouts/popup-error', :layout => 'help-and-content'
> - else
> - render :template => 'layouts/popup-error', :layout => 'popup'
> - end
> - end
> -
> - # don't define find_opts for array inputs
> - def json_hash(full_items, attributes, arg_list=[], find_opts={}, id_method=:id)
> - page = params[:page].to_i
> - paginate_opts = {:page => page,
> - :order => "#{params[:sortname]} #{params[:sortorder]}",
> - :per_page => params[:rp]}
> - arg_list << find_opts.merge(paginate_opts)
> - item_list = full_items.paginate(*arg_list)
> - json_hash = {}
> - json_hash[:page] = page
> - json_hash[:total] = item_list.total_entries
> - json_hash[:rows] = item_list.collect do |item|
> - item_hash = {}
> - item_hash[:id] = item.send(id_method)
> - item_hash[:cell] = attributes.collect do |attr|
> - if attr.is_a? Array
> - value = item
> - attr.each { |attr_item| value = (value.nil? ? nil : value.send(attr_item))}
> - value
> - else
> - item.send(attr)
> - end
> - end
> - item_hash
> - end
> - json_hash
> - end
> -
> - # json_list is a helper method used to format data for paginated flexigrid tables
> - #
> - # FIXME: what is the intent of this comment? don't define find_opts for array inputs
> - def json_list(full_items, attributes, arg_list=[], find_opts={}, id_method=:id)
> - render :json => json_hash(full_items, attributes, arg_list, find_opts, id_method).to_json
> - end
> -
> - private
> - def json_error_hash(msg, status)
> - json = {}
> - json[:success] = (status == :ok)
> - json.merge!(instance_errors)
> - # There's a potential issue here: if we add :errors for an object
> - # that the view won't generate inline error messages for, the user
> - # won't get any indication what the error is. But if we set :alert
> - # unconditionally, the user will get validation errors twice: once
> - # inline in the form, and once in the flash
> - json[:alert] = msg unless json[:errors]
> - return json
> - end
> -
> - def xml_errors(msg)
> - xml = {}
> - xml[:message] = msg
> - xml.merge!(instance_errors)
> - return xml
> - end
> -
> - def instance_errors
> - hash = {}
> - instance_variables.each do |ivar|
> - val = instance_variable_get(ivar)
> - if val && val.respond_to?(:errors) && val.errors.size > 0
> - hash[:object] = ivar[1, ivar.size]
> - hash[:errors] ||= []
> - hash[:errors] += val.errors.localize_error_messages.to_a
> - end
> - end
> - return hash
> - end
> -end
> diff --git a/src/app/controllers/application_controller.rb b/src/app/controllers/application_controller.rb
> new file mode 100644
> index 0000000..5ce625a
> --- /dev/null
> +++ b/src/app/controllers/application_controller.rb
> @@ -0,0 +1,198 @@
> +#
> +# Copyright (C) 2008 Red Hat, Inc.
> +# Written by Scott Seago <sseago at redhat.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; version 2 of the License.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> +# MA 02110-1301, USA. A copy of the GNU General Public License is
> +# also available at http://www.gnu.org/copyleft/gpl.html.
> +
> +# Filters added to this controller apply to all controllers in the application.
> +# Likewise, all the methods added will be available for all controllers.
> +
> +
> +class ApplicationController < ActionController::Base
> + # FIXME: once all controller classes include this, remove here
> + include ApplicationService
> +
> + init_gettext "ovirt"
> + layout :choose_layout
> +
> + before_filter :is_logged_in, :get_help_section
> +
> + # General error handlers, must be in order from least specific
> + # to most specific
> + rescue_from Exception, :with => :handle_general_error
> + rescue_from PermissionError, :with => :handle_perm_error
> + rescue_from ActionError, :with => :handle_action_error
> + rescue_from PartialSuccessError, :with => :handle_partial_success_error
> +
> + def choose_layout
> + if(params[:component_layout])
> + return (ENV["RAILS_ENV"] != "production")?'components/' << params[:component_layout]:'redux'
> + end
> + return 'redux'
> + end
> +
> + def is_logged_in
> + redirect_to(:controller => "/login", :action => "login") unless get_login_user
> + end
> +
> + def get_help_section
> + help = HelpSection.find(:first, :conditions => [ "controller = ? AND action = ?", controller_name, action_name ])
> + @help_section = help ? help.section : ""
> + if @help_section.index('#')
> + help_sections = @help_section.split('#')
> + @help_section = help_sections[0]
> + @anchor = help_sections[1]
> + else
> + @help_section = @help_section
> + @anchor = ""
> + end
> + end
> +
> + def get_login_user
> + (ENV["RAILS_ENV"] == "production") ? session[:user] : "ovirtadmin"
> + end
> +
> + protected
> + # permissions checking
> +
> + def handle_perm_error(error)
> + handle_error(:error => error, :status => :forbidden,
> + :title => "Access denied")
> + end
> +
> + def handle_partial_success_error(error)
> + failures_arr = error.failures.collect do |resource, reason|
> + if resource.respond_to?(:display_name)
> + resource.display_name + ": " + reason
> + else
> + reason
> + end
> + end
> + @successes = error.successes
> + @failures = error.failures
> + handle_error(:error => error, :status => :ok,
> + :message => error.message + ": " + failures_arr.join(", "),
> + :title => "Some actions failed")
> + end
> +
> + def handle_action_error(error)
> + handle_error(:error => error, :status => :conflict,
> + :title => "Action Error")
> + end
> +
> + def handle_general_error(error)
> + flash[:errmsg] = error.message
> + handle_error(:error => error, :status => :internal_server_error,
> + :title => "Internal Server Error")
> + end
> +
> + def handle_error(hash)
> + log_error(hash[:error]) if hash[:error]
> + msg = hash[:message] || hash[:error].message
> + title = hash[:title] || "Internal Server Error"
> + status = hash[:status] || :internal_server_error
> + respond_to do |format|
> + format.html { html_error_page(title, msg) }
> + format.json { render :json => json_error_hash(msg, status) }
> + format.xml { render :xml => xml_errors(msg), :status => status }
> + end
> + end
> +
> + def html_error_page(title, msg)
> + @title = title
> + @errmsg = msg
> + @ajax = params[:ajax]
> + @nolayout = params[:nolayout]
> + if @layout
> + render :layout => @layout
> + elsif @ajax
> + render :template => 'layouts/popup-error', :layout => 'tabs-and-content'
> + elsif @nolayout
> + render :template => 'layouts/popup-error', :layout => 'help-and-content'
> + else
> + render :template => 'layouts/popup-error', :layout => 'popup'
> + end
> + end
> +
> + # don't define find_opts for array inputs
> + def json_hash(full_items, attributes, arg_list=[], find_opts={}, id_method=:id)
> + page = params[:page].to_i
> + paginate_opts = {:page => page,
> + :order => "#{params[:sortname]} #{params[:sortorder]}",
> + :per_page => params[:rp]}
> + arg_list << find_opts.merge(paginate_opts)
> + item_list = full_items.paginate(*arg_list)
> + json_hash = {}
> + json_hash[:page] = page
> + json_hash[:total] = item_list.total_entries
> + json_hash[:rows] = item_list.collect do |item|
> + item_hash = {}
> + item_hash[:id] = item.send(id_method)
> + item_hash[:cell] = attributes.collect do |attr|
> + if attr.is_a? Array
> + value = item
> + attr.each { |attr_item| value = (value.nil? ? nil : value.send(attr_item))}
> + value
> + else
> + item.send(attr)
> + end
> + end
> + item_hash
> + end
> + json_hash
> + end
> +
> + # json_list is a helper method used to format data for paginated flexigrid tables
> + #
> + # FIXME: what is the intent of this comment? don't define find_opts for array inputs
> + def json_list(full_items, attributes, arg_list=[], find_opts={}, id_method=:id)
> + render :json => json_hash(full_items, attributes, arg_list, find_opts, id_method).to_json
> + end
> +
> + private
> + def json_error_hash(msg, status)
> + json = {}
> + json[:success] = (status == :ok)
> + json.merge!(instance_errors)
> + # There's a potential issue here: if we add :errors for an object
> + # that the view won't generate inline error messages for, the user
> + # won't get any indication what the error is. But if we set :alert
> + # unconditionally, the user will get validation errors twice: once
> + # inline in the form, and once in the flash
> + json[:alert] = msg unless json[:errors]
> + return json
> + end
> +
> + def xml_errors(msg)
> + xml = {}
> + xml[:message] = msg
> + xml.merge!(instance_errors)
> + return xml
> + end
> +
> + def instance_errors
> + hash = {}
> + instance_variables.each do |ivar|
> + val = instance_variable_get(ivar)
> + if val && val.respond_to?(:errors) && val.errors.size > 0
> + hash[:object] = ivar[1, ivar.size]
> + hash[:errors] ||= []
> + hash[:errors] += val.errors.localize_error_messages.to_a
> + end
> + end
> + return hash
> + end
> +end
> diff --git a/src/config.ru b/src/config.ru
> new file mode 100644
> index 0000000..acbfe4e
> --- /dev/null
> +++ b/src/config.ru
> @@ -0,0 +1,7 @@
> +# Rack Dispatcher
> +
> +# Require your environment file to bootstrap Rails
> +require File.dirname(__FILE__) + '/config/environment'
> +
> +# Dispatch the request
> +run ActionController::Dispatcher.new
> diff --git a/src/config/boot.rb b/src/config/boot.rb
> index cd21fb9..0ad0f78 100644
> --- a/src/config/boot.rb
> +++ b/src/config/boot.rb
> @@ -44,6 +44,7 @@ module Rails
> def load_initializer
> require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
> Rails::Initializer.run(:install_gem_spec_stubs)
> + Rails::GemDependency.add_frozen_gem_path
> end
> end
>
> @@ -67,7 +68,7 @@ module Rails
>
> class << self
> def rubygems_version
> - Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
> + Gem::RubyGemsVersion rescue nil
> end
>
> def gem_version
> @@ -82,14 +83,14 @@ module Rails
>
> def load_rubygems
> require 'rubygems'
> -
> - unless rubygems_version >= '0.9.4'
> - $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
> + min_version = '1.3.1'
> + unless rubygems_version >= min_version
> + $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
> exit 1
> end
>
> rescue LoadError
> - $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
> + $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
> exit 1
> end
>
> diff --git a/src/config/environment.rb b/src/config/environment.rb
> index 98a2fbb..9d9a66d 100644
> --- a/src/config/environment.rb
> +++ b/src/config/environment.rb
> @@ -19,9 +19,8 @@
>
> # Be sure to restart your web server when you modify this file.
>
> -# Uncomment below to force Rails into production mode when
> -# you don't control web/app server and can't set it the proper way
> -# ENV['RAILS_ENV'] ||= 'production'
> +# Specifies gem version of Rails to use when vendor/rails is not present
> +RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION
>
> # Bootstrap the Rails environment, frameworks, and default configuration
> require File.join(File.dirname(__FILE__), 'boot')
> @@ -42,7 +41,7 @@ Rails::Initializer.run do |config|
> # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
> # config.gem "aws-s3", :lib => "aws/s3"
> config.gem "cobbler"
> - config.gem "gettext", :lib => "gettext/rails"
> + config.gem "gettext", :lib => "gettext_rails"
>
> # Only load the plugins named here, in the order given. By default, all plugins
> # in vendor/plugins are loaded in alphabetical order.
> @@ -61,20 +60,6 @@ Rails::Initializer.run do |config|
> # Run "rake -D time" for a list of tasks for finding time zone names. Uncomment to use default local time.
> config.time_zone = 'UTC'
>
> - # Your secret key for verifying cookie session data integrity.
> - # If you change this key, all old sessions will become invalid!
> - # Make sure the secret is at least 30 characters and all random,
> - # no regular words or you'll be exposed to dictionary attacks.
> - config.action_controller.session = {
> - :session_key => "_ovirt_session_id",
> - :secret => "a covert ovirt phrase or some such"
> - }
> -
> - # Use the database for sessions instead of the cookie-based default,
> - # which shouldn't be used to store highly confidential information
> - # (create the session table with "rake db:sessions:create")
> - config.action_controller.session_store = :active_record_store
> -
> # Use SQL instead of Active Record's schema dumper when creating the test database.
> # This is necessary if your schema can't be completely dumped by the schema dumper,
> # like if you have constraints or database-specific column types
> @@ -83,17 +68,8 @@ Rails::Initializer.run do |config|
> # Activate observers that should always be running
> # config.active_record.observers = :cacher, :garbage_collector
> config.active_record.observers = :host_observer, :vm_observer
> -end
>
> -# Add new inflection rules using the following format
> -# (all these examples are active by default):
> -# Inflector.inflections do |inflect|
> -# inflect.plural /^(ox)$/i, '\1en'
> -# inflect.singular /^(ox)en/i, '\1'
> -# inflect.irregular 'person', 'people'
> -# inflect.uncountable %w( fish sheep )
> -# end
> -
> -# Add new mime types for use in respond_to blocks:
> -# Mime::Type.register "text/richtext", :rtf
> -# Mime::Type.register "application/x-mobile", :mobile
> + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
> + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
> + # config.i18n.default_locale = :de
> +end
> diff --git a/src/config/initializers/backtrace_silencers.rb b/src/config/initializers/backtrace_silencers.rb
> new file mode 100644
> index 0000000..c2169ed
> --- /dev/null
> +++ b/src/config/initializers/backtrace_silencers.rb
> @@ -0,0 +1,7 @@
> +# Be sure to restart your server when you modify this file.
> +
> +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
> +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
> +
> +# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code.
> +# Rails.backtrace_cleaner.remove_silencers!
> \ No newline at end of file
> diff --git a/src/config/initializers/inflections.rb b/src/config/initializers/inflections.rb
> new file mode 100644
> index 0000000..9e8b013
> --- /dev/null
> +++ b/src/config/initializers/inflections.rb
> @@ -0,0 +1,10 @@
> +# Be sure to restart your server when you modify this file.
> +
> +# Add new inflection rules using the following format
> +# (all these examples are active by default):
> +# ActiveSupport::Inflector.inflections do |inflect|
> +# inflect.plural /^(ox)$/i, '\1en'
> +# inflect.singular /^(ox)en/i, '\1'
> +# inflect.irregular 'person', 'people'
> +# inflect.uncountable %w( fish sheep )
> +# end
> diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb
> new file mode 100644
> index 0000000..72aca7e
> --- /dev/null
> +++ b/src/config/initializers/mime_types.rb
> @@ -0,0 +1,5 @@
> +# Be sure to restart your server when you modify this file.
> +
> +# Add new mime types for use in respond_to blocks:
> +# Mime::Type.register "text/richtext", :rtf
> +# Mime::Type.register_alias "text/html", :iphone
> diff --git a/src/config/initializers/new_rails_defaults.rb b/src/config/initializers/new_rails_defaults.rb
> new file mode 100644
> index 0000000..8ec3186
> --- /dev/null
> +++ b/src/config/initializers/new_rails_defaults.rb
> @@ -0,0 +1,19 @@
> +# Be sure to restart your server when you modify this file.
> +
> +# These settings change the behavior of Rails 2 apps and will be defaults
> +# for Rails 3. You can remove this initializer when Rails 3 is released.
> +
> +if defined?(ActiveRecord)
> + # Include Active Record class name as root for JSON serialized output.
> + ActiveRecord::Base.include_root_in_json = true
> +
> + # Store the full class name (including module namespace) in STI type column.
> + ActiveRecord::Base.store_full_sti_class = true
> +end
> +
> +# Use ISO 8601 format for JSON serialized times and dates.
> +ActiveSupport.use_standard_json_time_format = true
> +
> +# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
> +# if you're including raw json in an HTML page.
> +ActiveSupport.escape_html_entities_in_json = false
> \ No newline at end of file
> diff --git a/src/config/initializers/session_store.rb b/src/config/initializers/session_store.rb
> new file mode 100644
> index 0000000..64dbf51
> --- /dev/null
> +++ b/src/config/initializers/session_store.rb
> @@ -0,0 +1,15 @@
> +# Be sure to restart your server when you modify this file.
> +
> +# Your secret key for verifying cookie session data integrity.
> +# If you change this key, all old sessions will become invalid!
> +# Make sure the secret is at least 30 characters and all random,
> +# no regular words or you'll be exposed to dictionary attacks.
> +ActionController::Base.session = {
> + :key => '_rails23-app_session',
> + :secret => '41713a6b4a92b5b7af55314d2ef6fc499a177269ea91b9fdaa7d15c42e1234b70b32f52278ae26b774b38dbdfeb7d078585d10f643e81b6615d32410f192f1de'
> +}
> +
> +# Use the database for sessions instead of the cookie-based default,
> +# which shouldn't be used to store highly confidential information
> +# (create the session table with "rake db:sessions:create")
> +ActionController::Base.session_store = :active_record_store
> diff --git a/src/public/javascripts/controls.js b/src/public/javascripts/controls.js
> index 1de3b29..ca29aef 100644
> --- a/src/public/javascripts/controls.js
> +++ b/src/public/javascripts/controls.js
> @@ -1,22 +1,22 @@
> // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
> -// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
> -// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
> +// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
> +// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
> // Contributors:
> // Richard Livsey
> // Rahul Bhargava
> // Rob Wills
> -//
> +//
> // script.aculo.us is freely distributable under the terms of an MIT-style license.
> // For details, see the script.aculo.us web site: http://script.aculo.us/
>
> -// Autocompleter.Base handles all the autocompletion functionality
> +// Autocompleter.Base handles all the autocompletion functionality
> // that's independent of the data source for autocompletion. This
> // includes drawing the autocompletion menu, observing keyboard
> // and mouse events, and similar.
> //
> -// Specific autocompleters need to provide, at the very least,
> +// Specific autocompleters need to provide, at the very least,
> // a getUpdatedChoices function that will be invoked every time
> -// the text inside the monitored textbox changes. This method
> +// the text inside the monitored textbox changes. This method
> // should get the text for which to provide autocompletion by
> // invoking this.getToken(), NOT by directly accessing
> // this.element.value. This is to allow incremental tokenized
> @@ -30,23 +30,23 @@
> // will incrementally autocomplete with a comma as the token.
> // Additionally, ',' in the above example can be replaced with
> // a token array, e.g. { tokens: [',', '\n'] } which
> -// enables autocompletion on multiple tokens. This is most
> -// useful when one of the tokens is \n (a newline), as it
> +// enables autocompletion on multiple tokens. This is most
> +// useful when one of the tokens is \n (a newline), as it
> // allows smart autocompletion after linebreaks.
>
> if(typeof Effect == 'undefined')
> throw("controls.js requires including script.aculo.us' effects.js library");
>
> -var Autocompleter = { }
> +var Autocompleter = { };
> Autocompleter.Base = Class.create({
> baseInitialize: function(element, update, options) {
> - element = $(element)
> + element = $(element);
> this.element = element;
> - this.update = $(update);
> - this.hasFocus = false;
> - this.changed = false;
> - this.active = false;
> - this.index = 0;
> + this.update = $(update);
> + this.hasFocus = false;
> + this.changed = false;
> + this.active = false;
> + this.index = 0;
> this.entryCount = 0;
> this.oldElementValue = this.element.value;
>
> @@ -59,28 +59,28 @@ Autocompleter.Base = Class.create({
> this.options.tokens = this.options.tokens || [];
> this.options.frequency = this.options.frequency || 0.4;
> this.options.minChars = this.options.minChars || 1;
> - this.options.onShow = this.options.onShow ||
> - function(element, update){
> + this.options.onShow = this.options.onShow ||
> + function(element, update){
> if(!update.style.position || update.style.position=='absolute') {
> update.style.position = 'absolute';
> Position.clone(element, update, {
> - setHeight: false,
> + setHeight: false,
> offsetTop: element.offsetHeight
> });
> }
> Effect.Appear(update,{duration:0.15});
> };
> - this.options.onHide = this.options.onHide ||
> + this.options.onHide = this.options.onHide ||
> function(element, update){ new Effect.Fade(update,{duration:0.15}) };
>
> - if(typeof(this.options.tokens) == 'string')
> + if(typeof(this.options.tokens) == 'string')
> this.options.tokens = new Array(this.options.tokens);
> // Force carriage returns as token delimiters anyway
> if (!this.options.tokens.include('\n'))
> this.options.tokens.push('\n');
>
> this.observer = null;
> -
> +
> this.element.setAttribute('autocomplete','off');
>
> Element.hide(this.update);
> @@ -91,10 +91,10 @@ Autocompleter.Base = Class.create({
>
> show: function() {
> if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
> - if(!this.iefix &&
> + if(!this.iefix &&
> (Prototype.Browser.IE) &&
> (Element.getStyle(this.update, 'position')=='absolute')) {
> - new Insertion.After(this.update,
> + new Insertion.After(this.update,
> '<iframe id="' + this.update.id + '_iefix" '+
> 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
> 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
> @@ -102,7 +102,7 @@ Autocompleter.Base = Class.create({
> }
> if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
> },
> -
> +
> fixIEOverlapping: function() {
> Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
> this.iefix.style.zIndex = 1;
> @@ -150,15 +150,15 @@ Autocompleter.Base = Class.create({
> Event.stop(event);
> return;
> }
> - else
> - if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
> + else
> + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
> (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
>
> this.changed = true;
> this.hasFocus = true;
>
> if(this.observer) clearTimeout(this.observer);
> - this.observer =
> + this.observer =
> setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
> },
>
> @@ -170,35 +170,35 @@ Autocompleter.Base = Class.create({
>
> onHover: function(event) {
> var element = Event.findElement(event, 'LI');
> - if(this.index != element.autocompleteIndex)
> + if(this.index != element.autocompleteIndex)
> {
> this.index = element.autocompleteIndex;
> this.render();
> }
> Event.stop(event);
> },
> -
> +
> onClick: function(event) {
> var element = Event.findElement(event, 'LI');
> this.index = element.autocompleteIndex;
> this.selectEntry();
> this.hide();
> },
> -
> +
> onBlur: function(event) {
> // needed to make click events working
> setTimeout(this.hide.bind(this), 250);
> this.hasFocus = false;
> - this.active = false;
> - },
> -
> + this.active = false;
> + },
> +
> render: function() {
> if(this.entryCount > 0) {
> for (var i = 0; i < this.entryCount; i++)
> - this.index==i ?
> - Element.addClassName(this.getEntry(i),"selected") :
> + this.index==i ?
> + Element.addClassName(this.getEntry(i),"selected") :
> Element.removeClassName(this.getEntry(i),"selected");
> - if(this.hasFocus) {
> + if(this.hasFocus) {
> this.show();
> this.active = true;
> }
> @@ -207,27 +207,27 @@ Autocompleter.Base = Class.create({
> this.hide();
> }
> },
> -
> +
> markPrevious: function() {
> - if(this.index > 0) this.index--
> + if(this.index > 0) this.index--;
> else this.index = this.entryCount-1;
> this.getEntry(this.index).scrollIntoView(true);
> },
> -
> +
> markNext: function() {
> - if(this.index < this.entryCount-1) this.index++
> + if(this.index < this.entryCount-1) this.index++;
> else this.index = 0;
> this.getEntry(this.index).scrollIntoView(false);
> },
> -
> +
> getEntry: function(index) {
> return this.update.firstChild.childNodes[index];
> },
> -
> +
> getCurrentEntry: function() {
> return this.getEntry(this.index);
> },
> -
> +
> selectEntry: function() {
> this.active = false;
> this.updateElement(this.getCurrentEntry());
> @@ -244,7 +244,7 @@ Autocompleter.Base = Class.create({
> if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
> } else
> value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
> -
> +
> var bounds = this.getTokenBounds();
> if (bounds[0] != -1) {
> var newValue = this.element.value.substr(0, bounds[0]);
> @@ -257,7 +257,7 @@ Autocompleter.Base = Class.create({
> }
> this.oldElementValue = this.element.value;
> this.element.focus();
> -
> +
> if (this.options.afterUpdateElement)
> this.options.afterUpdateElement(this.element, selectedElement);
> },
> @@ -269,20 +269,20 @@ Autocompleter.Base = Class.create({
> Element.cleanWhitespace(this.update.down());
>
> if(this.update.firstChild && this.update.down().childNodes) {
> - this.entryCount =
> + this.entryCount =
> this.update.down().childNodes.length;
> for (var i = 0; i < this.entryCount; i++) {
> var entry = this.getEntry(i);
> entry.autocompleteIndex = i;
> this.addObservers(entry);
> }
> - } else {
> + } else {
> this.entryCount = 0;
> }
>
> this.stopIndicator();
> this.index = 0;
> -
> +
> if(this.entryCount==1 && this.options.autoSelect) {
> this.selectEntry();
> this.hide();
> @@ -298,7 +298,7 @@ Autocompleter.Base = Class.create({
> },
>
> onObserverEvent: function() {
> - this.changed = false;
> + this.changed = false;
> this.tokenBounds = null;
> if(this.getToken().length>=this.options.minChars) {
> this.getUpdatedChoices();
> @@ -358,7 +358,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
> this.options.parameters = this.options.callback ?
> this.options.callback(this.element, entry) : entry;
>
> - if(this.options.defaultParams)
> + if(this.options.defaultParams)
> this.options.parameters += '&' + this.options.defaultParams;
>
> new Ajax.Request(this.url, this.options);
> @@ -382,7 +382,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
> // - choices - How many autocompletion choices to offer
> //
> // - partialSearch - If false, the autocompleter will match entered
> -// text only at the beginning of strings in the
> +// text only at the beginning of strings in the
> // autocomplete array. Defaults to true, which will
> // match text at the beginning of any *word* in the
> // strings in the autocomplete array. If you want to
> @@ -399,7 +399,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
> // - ignoreCase - Whether to ignore case when autocompleting.
> // Defaults to true.
> //
> -// It's possible to pass in a custom function as the 'selector'
> +// It's possible to pass in a custom function as the 'selector'
> // option, if you prefer to write your own autocompletion logic.
> // In that case, the other options above will not apply unless
> // you support them.
> @@ -427,20 +427,20 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
> var entry = instance.getToken();
> var count = 0;
>
> - for (var i = 0; i < instance.options.array.length &&
> - ret.length < instance.options.choices ; i++) {
> + for (var i = 0; i < instance.options.array.length &&
> + ret.length < instance.options.choices ; i++) {
>
> var elem = instance.options.array[i];
> - var foundPos = instance.options.ignoreCase ?
> - elem.toLowerCase().indexOf(entry.toLowerCase()) :
> + var foundPos = instance.options.ignoreCase ?
> + elem.toLowerCase().indexOf(entry.toLowerCase()) :
> elem.indexOf(entry);
>
> while (foundPos != -1) {
> - if (foundPos == 0 && elem.length != entry.length) {
> - ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
> + if (foundPos == 0 && elem.length != entry.length) {
> + ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
> elem.substr(entry.length) + "</li>");
> break;
> - } else if (entry.length >= instance.options.partialChars &&
> + } else if (entry.length >= instance.options.partialChars &&
> instance.options.partialSearch && foundPos != -1) {
> if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
> partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
> @@ -450,14 +450,14 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
> }
> }
>
> - foundPos = instance.options.ignoreCase ?
> - elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
> + foundPos = instance.options.ignoreCase ?
> + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
> elem.indexOf(entry, foundPos + 1);
>
> }
> }
> if (partial.length)
> - ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
> + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
> return "<ul>" + ret.join('') + "</ul>";
> }
> }, options || { });
> @@ -474,7 +474,7 @@ Field.scrollFreeActivate = function(field) {
> setTimeout(function() {
> Field.activate(field);
> }, 1);
> -}
> +};
>
> Ajax.InPlaceEditor = Class.create({
> initialize: function(element, url, options) {
> @@ -604,7 +604,7 @@ Ajax.InPlaceEditor = Class.create({
> this.triggerCallback('onEnterHover');
> },
> getText: function() {
> - return this.element.innerHTML;
> + return this.element.innerHTML.unescapeHTML();
> },
> handleAJAXFailure: function(transport) {
> this.triggerCallback('onFailure', transport);
> @@ -780,7 +780,7 @@ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
> onSuccess: function(transport) {
> var js = transport.responseText.strip();
> if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
> - throw 'Server returned an invalid collection representation.';
> + throw('Server returned an invalid collection representation.');
> this._collection = eval(js);
> this.checkForExternalText();
> }.bind(this),
> @@ -937,7 +937,7 @@ Ajax.InPlaceCollectionEditor.DefaultOptions = {
> loadingCollectionText: 'Loading options...'
> };
>
> -// Delayed observer, like Form.Element.Observer,
> +// Delayed observer, like Form.Element.Observer,
> // but waits for delay after last key input
> // Ideal for live-search fields
>
> @@ -947,7 +947,7 @@ Form.Element.DelayedObserver = Class.create({
> this.element = $(element);
> this.callback = callback;
> this.timer = null;
> - this.lastValue = $F(this.element);
> + this.lastValue = $F(this.element);
> Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
> },
> delayedListener: function(event) {
> @@ -960,4 +960,4 @@ Form.Element.DelayedObserver = Class.create({
> this.timer = null;
> this.callback(this.element, $F(this.element));
> }
> -});
> +});
> \ No newline at end of file
> diff --git a/src/public/javascripts/dragdrop.js b/src/public/javascripts/dragdrop.js
> index e2e7d4a..07229f9 100644
> --- a/src/public/javascripts/dragdrop.js
> +++ b/src/public/javascripts/dragdrop.js
> @@ -1,6 +1,6 @@
> // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
> -// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
> -//
> +// (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
> +//
> // script.aculo.us is freely distributable under the terms of an MIT-style license.
> // For details, see the script.aculo.us web site: http://script.aculo.us/
>
> @@ -32,7 +32,7 @@ var Droppables = {
> options._containers.push($(containment));
> }
> }
> -
> +
> if(options.accept) options.accept = [options.accept].flatten();
>
> Element.makePositioned(element); // fix IE
> @@ -40,34 +40,34 @@ var Droppables = {
>
> this.drops.push(options);
> },
> -
> +
> findDeepestChild: function(drops) {
> deepest = drops[0];
> -
> +
> for (i = 1; i < drops.length; ++i)
> if (Element.isParent(drops[i].element, deepest.element))
> deepest = drops[i];
> -
> +
> return deepest;
> },
>
> isContained: function(element, drop) {
> var containmentNode;
> if(drop.tree) {
> - containmentNode = element.treeNode;
> + containmentNode = element.treeNode;
> } else {
> containmentNode = element.parentNode;
> }
> return drop._containers.detect(function(c) { return containmentNode == c });
> },
> -
> +
> isAffected: function(point, element, drop) {
> return (
> (drop.element!=element) &&
> ((!drop._containers) ||
> this.isContained(element, drop)) &&
> ((!drop.accept) ||
> - (Element.classNames(element).detect(
> + (Element.classNames(element).detect(
> function(v) { return drop.accept.include(v) } ) )) &&
> Position.within(drop.element, point[0], point[1]) );
> },
> @@ -87,12 +87,12 @@ var Droppables = {
> show: function(point, element) {
> if(!this.drops.length) return;
> var drop, affected = [];
> -
> +
> this.drops.each( function(drop) {
> if(Droppables.isAffected(point, element, drop))
> affected.push(drop);
> });
> -
> +
> if(affected.length>0)
> drop = Droppables.findDeepestChild(affected);
>
> @@ -101,7 +101,7 @@ var Droppables = {
> Position.within(drop.element, point[0], point[1]);
> if(drop.onHover)
> drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
> -
> +
> if (drop != this.last_active) Droppables.activate(drop);
> }
> },
> @@ -121,25 +121,25 @@ var Droppables = {
> if(this.last_active)
> this.deactivate(this.last_active);
> }
> -}
> +};
>
> var Draggables = {
> drags: [],
> observers: [],
> -
> +
> register: function(draggable) {
> if(this.drags.length == 0) {
> this.eventMouseUp = this.endDrag.bindAsEventListener(this);
> this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
> this.eventKeypress = this.keyPress.bindAsEventListener(this);
> -
> +
> Event.observe(document, "mouseup", this.eventMouseUp);
> Event.observe(document, "mousemove", this.eventMouseMove);
> Event.observe(document, "keypress", this.eventKeypress);
> }
> this.drags.push(draggable);
> },
> -
> +
> unregister: function(draggable) {
> this.drags = this.drags.reject(function(d) { return d==draggable });
> if(this.drags.length == 0) {
> @@ -148,24 +148,24 @@ var Draggables = {
> Event.stopObserving(document, "keypress", this.eventKeypress);
> }
> },
> -
> +
> activate: function(draggable) {
> - if(draggable.options.delay) {
> - this._timeout = setTimeout(function() {
> - Draggables._timeout = null;
> - window.focus();
> - Draggables.activeDraggable = draggable;
> - }.bind(this), draggable.options.delay);
> + if(draggable.options.delay) {
> + this._timeout = setTimeout(function() {
> + Draggables._timeout = null;
> + window.focus();
> + Draggables.activeDraggable = draggable;
> + }.bind(this), draggable.options.delay);
> } else {
> window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
> this.activeDraggable = draggable;
> }
> },
> -
> +
> deactivate: function() {
> this.activeDraggable = null;
> },
> -
> +
> updateDrag: function(event) {
> if(!this.activeDraggable) return;
> var pointer = [Event.pointerX(event), Event.pointerY(event)];
> @@ -173,36 +173,36 @@ var Draggables = {
> // the same coordinates, prevent needless redrawing (moz bug?)
> if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
> this._lastPointer = pointer;
> -
> +
> this.activeDraggable.updateDrag(event, pointer);
> },
> -
> +
> endDrag: function(event) {
> - if(this._timeout) {
> - clearTimeout(this._timeout);
> - this._timeout = null;
> + if(this._timeout) {
> + clearTimeout(this._timeout);
> + this._timeout = null;
> }
> if(!this.activeDraggable) return;
> this._lastPointer = null;
> this.activeDraggable.endDrag(event);
> this.activeDraggable = null;
> },
> -
> +
> keyPress: function(event) {
> if(this.activeDraggable)
> this.activeDraggable.keyPress(event);
> },
> -
> +
> addObserver: function(observer) {
> this.observers.push(observer);
> this._cacheObserverCallbacks();
> },
> -
> +
> removeObserver: function(element) { // element instead of observer fixes mem leaks
> this.observers = this.observers.reject( function(o) { return o.element==element });
> this._cacheObserverCallbacks();
> },
> -
> +
> notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
> if(this[eventName+'Count'] > 0)
> this.observers.each( function(o) {
> @@ -210,7 +210,7 @@ var Draggables = {
> });
> if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
> },
> -
> +
> _cacheObserverCallbacks: function() {
> ['onStart','onEnd','onDrag'].each( function(eventName) {
> Draggables[eventName+'Count'] = Draggables.observers.select(
> @@ -218,7 +218,7 @@ var Draggables = {
> ).length;
> });
> }
> -}
> +};
>
> /*--------------------------------------------------------------------------*/
>
> @@ -234,12 +234,12 @@ var Draggable = Class.create({
> },
> endeffect: function(element) {
> var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
> - new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
> + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
> queue: {scope:'_draggable', position:'end'},
> - afterFinish: function(){
> - Draggable._dragging[element] = false
> + afterFinish: function(){
> + Draggable._dragging[element] = false
> }
> - });
> + });
> },
> zindex: 1000,
> revert: false,
> @@ -250,57 +250,57 @@ var Draggable = Class.create({
> snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
> delay: 0
> };
> -
> +
> if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
> Object.extend(defaults, {
> starteffect: function(element) {
> element._opacity = Element.getOpacity(element);
> Draggable._dragging[element] = true;
> - new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
> + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
> }
> });
> -
> +
> var options = Object.extend(defaults, arguments[1] || { });
>
> this.element = $(element);
> -
> +
> if(options.handle && Object.isString(options.handle))
> this.handle = this.element.down('.'+options.handle, 0);
> -
> +
> if(!this.handle) this.handle = $(options.handle);
> if(!this.handle) this.handle = this.element;
> -
> +
> if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
> options.scroll = $(options.scroll);
> this._isScrollChild = Element.childOf(this.element, options.scroll);
> }
>
> - Element.makePositioned(this.element); // fix IE
> + Element.makePositioned(this.element); // fix IE
>
> this.options = options;
> - this.dragging = false;
> + this.dragging = false;
>
> this.eventMouseDown = this.initDrag.bindAsEventListener(this);
> Event.observe(this.handle, "mousedown", this.eventMouseDown);
> -
> +
> Draggables.register(this);
> },
> -
> +
> destroy: function() {
> Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
> Draggables.unregister(this);
> },
> -
> +
> currentDelta: function() {
> return([
> parseInt(Element.getStyle(this.element,'left') || '0'),
> parseInt(Element.getStyle(this.element,'top') || '0')]);
> },
> -
> +
> initDrag: function(event) {
> if(!Object.isUndefined(Draggable._dragging[this.element]) &&
> Draggable._dragging[this.element]) return;
> - if(Event.isLeftClick(event)) {
> + if(Event.isLeftClick(event)) {
> // abort on form elements, fixes a Firefox issue
> var src = Event.element(event);
> if((tag_name = src.tagName.toUpperCase()) && (
> @@ -309,34 +309,34 @@ var Draggable = Class.create({
> tag_name=='OPTION' ||
> tag_name=='BUTTON' ||
> tag_name=='TEXTAREA')) return;
> -
> +
> var pointer = [Event.pointerX(event), Event.pointerY(event)];
> var pos = Position.cumulativeOffset(this.element);
> this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
> -
> +
> Draggables.activate(this);
> Event.stop(event);
> }
> },
> -
> +
> startDrag: function(event) {
> this.dragging = true;
> if(!this.delta)
> this.delta = this.currentDelta();
> -
> +
> if(this.options.zindex) {
> this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
> this.element.style.zIndex = this.options.zindex;
> }
> -
> +
> if(this.options.ghosting) {
> this._clone = this.element.cloneNode(true);
> - this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
> - if (!this.element._originallyAbsolute)
> + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
> + if (!this._originallyAbsolute)
> Position.absolutize(this.element);
> this.element.parentNode.insertBefore(this._clone, this.element);
> }
> -
> +
> if(this.options.scroll) {
> if (this.options.scroll == window) {
> var where = this._getWindowScroll(this.options.scroll);
> @@ -347,15 +347,15 @@ var Draggable = Class.create({
> this.originalScrollTop = this.options.scroll.scrollTop;
> }
> }
> -
> +
> Draggables.notify('onStart', this, event);
> -
> +
> if(this.options.starteffect) this.options.starteffect(this.element);
> },
> -
> +
> updateDrag: function(event, pointer) {
> if(!this.dragging) this.startDrag(event);
> -
> +
> if(!this.options.quiet){
> Position.prepare();
> Droppables.show(pointer, this.element);
> @@ -403,9 +403,9 @@ var Draggable = Class.create({
> }
>
> if(this.options.ghosting) {
> - if (!this.element._originallyAbsolute)
> + if (!this._originallyAbsolute)
> Position.relativize(this.element);
> - delete this.element._originallyAbsolute;
> + delete this._originallyAbsolute;
> Element.remove(this._clone);
> this._clone = null;
> }
> @@ -433,7 +433,7 @@ var Draggable = Class.create({
> if(this.options.zindex)
> this.element.style.zIndex = this.originalZ;
>
> - if(this.options.endeffect)
> + if(this.options.endeffect)
> this.options.endeffect(this.element);
>
> Draggables.deactivate(this);
> @@ -468,8 +468,8 @@ var Draggable = Class.create({
> pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
> }
>
> - var p = [0,1].map(function(i){
> - return (point[i]-pos[i]-this.offset[i])
> + var p = [0,1].map(function(i){
> + return (point[i]-pos[i]-this.offset[i])
> }.bind(this));
>
> if(this.options.snap) {
> @@ -478,10 +478,10 @@ var Draggable = Class.create({
> } else {
> if(Object.isArray(this.options.snap)) {
> p = p.map( function(v, i) {
> - return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
> + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
> } else {
> p = p.map( function(v) {
> - return (v/this.options.snap).round()*this.options.snap }.bind(this))
> + return (v/this.options.snap).round()*this.options.snap }.bind(this));
> }
> }}
>
> @@ -560,7 +560,7 @@ var Draggable = Class.create({
> H = documentElement.clientHeight;
> } else {
> W = body.offsetWidth;
> - H = body.offsetHeight
> + H = body.offsetHeight;
> }
> }
> return { top: T, left: L, width: W, height: H };
> @@ -591,9 +591,9 @@ var SortableObserver = Class.create({
>
> var Sortable = {
> SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
> -
> +
> sortables: { },
> -
> +
> _findRootElement: function(element) {
> while (element.tagName.toUpperCase() != "BODY") {
> if(element.id && Sortable.sortables[element.id]) return element;
> @@ -608,7 +608,8 @@ var Sortable = {
> },
>
> destroy: function(element){
> - var s = Sortable.options(element);
> + element = $(element);
> + var s = Sortable.sortables[element.id];
>
> if(s) {
> Draggables.removeObserver(s.element);
> @@ -689,14 +690,14 @@ var Sortable = {
> tree: options.tree,
> hoverclass: options.hoverclass,
> onHover: Sortable.onHover
> - }
> + };
>
> var options_for_tree = {
> onHover: Sortable.onEmptyHover,
> overlap: options.overlap,
> containment: options.containment,
> hoverclass: options.hoverclass
> - }
> + };
>
> // fix for gecko engine
> Element.cleanWhitespace(element);
> @@ -832,7 +833,7 @@ var Sortable = {
> Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
> else
> Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
> -
> +
> Sortable._marker.show();
> },
>
> @@ -851,11 +852,11 @@ var Sortable = {
> children: [],
> position: parent.children.length,
> container: $(children[i]).down(options.treeTag)
> - }
> + };
>
> /* Get the element containing the children and recurse over it */
> if (child.container)
> - this._tree(child.container, options, child)
> + this._tree(child.container, options, child);
>
> parent.children.push (child);
> }
> @@ -880,7 +881,7 @@ var Sortable = {
> children: [],
> container: element,
> position: 0
> - }
> + };
>
> return Sortable._tree(element, options, root);
> },
> @@ -931,7 +932,7 @@ var Sortable = {
>
> if (options.tree) {
> return Sortable.tree(element, arguments[1]).children.map( function (item) {
> - return [name + Sortable._constructIndex(item) + "[id]=" +
> + return [name + Sortable._constructIndex(item) + "[id]=" +
> encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
> }).flatten().join('&');
> } else {
> @@ -940,14 +941,14 @@ var Sortable = {
> }).join('&');
> }
> }
> -}
> +};
>
> // Returns true if child is contained within element
> Element.isParent = function(child, element) {
> if (!child.parentNode || child == element) return false;
> if (child.parentNode == element) return true;
> return Element.isParent(child.parentNode, element);
> -}
> +};
>
> Element.findChildren = function(element, only, recursive, tagName) {
> if(!element.hasChildNodes()) return null;
> @@ -965,8 +966,8 @@ Element.findChildren = function(element, only, recursive, tagName) {
> });
>
> return (elements.length>0 ? elements.flatten() : []);
> -}
> +};
>
> Element.offsetSize = function (element, type) {
> return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
> -}
> +};
> \ No newline at end of file
> diff --git a/src/public/javascripts/effects.js b/src/public/javascripts/effects.js
> index b0f056b..5a639d2 100644
> --- a/src/public/javascripts/effects.js
> +++ b/src/public/javascripts/effects.js
> @@ -3,9 +3,9 @@
> // Justin Palmer (http://encytemedia.com/)
> // Mark Pilgrim (http://diveintomark.org/)
> // Martin Bialasinki
> -//
> +//
> // script.aculo.us is freely distributable under the terms of an MIT-style license.
> -// For details, see the script.aculo.us web site: http://script.aculo.us/
> +// For details, see the script.aculo.us web site: http://script.aculo.us/
>
> // converts rgb() and #xxx to #xxxxxx format,
> // returns self (or first argument) if not convertable
> @@ -32,7 +32,7 @@ Element.collectTextNodes = function(element) {
> }).flatten().join('');
> };
>
> -Element.collectTextNodesIgnoreClass = function(element, className) {
> +Element.collectTextNodesIgnoreClass = function(element, className) {
> return $A($(element).childNodes).collect( function(node) {
> return (node.nodeType==3 ? node.nodeValue :
> ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
> @@ -70,25 +70,20 @@ var Effect = {
> Transitions: {
> linear: Prototype.K,
> sinoidal: function(pos) {
> - return (-Math.cos(pos*Math.PI)/2) + 0.5;
> + return (-Math.cos(pos*Math.PI)/2) + .5;
> },
> reverse: function(pos) {
> return 1-pos;
> },
> flicker: function(pos) {
> - var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
> + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
> return pos > 1 ? 1 : pos;
> },
> wobble: function(pos) {
> - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
> + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
> },
> pulse: function(pos, pulses) {
> - pulses = pulses || 5;
> - return (
> - ((pos % (1/pulses)) * pulses).round() == 0 ?
> - ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
> - 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
> - );
> + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
> },
> spring: function(pos) {
> return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
> @@ -223,7 +218,7 @@ Effect.Queues = {
> instances: $H(),
> get: function(queueName) {
> if (!Object.isString(queueName)) return queueName;
> -
> +
> return this.instances.get(queueName) ||
> this.instances.set(queueName, new Effect.ScopedQueue());
> }
> @@ -249,18 +244,30 @@ Effect.Base = Class.create({
> this.totalTime = this.finishOn-this.startOn;
> this.totalFrames = this.options.fps*this.options.duration;
>
> - eval('this.render = function(pos){ '+
> - 'if (this.state=="idle"){this.state="running";'+
> - codeForEvent(this.options,'beforeSetup')+
> - (this.setup ? 'this.setup();':'')+
> - codeForEvent(this.options,'afterSetup')+
> - '};if (this.state=="running"){'+
> - 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
> - 'this.position=pos;'+
> - codeForEvent(this.options,'beforeUpdate')+
> - (this.update ? 'this.update(pos);':'')+
> - codeForEvent(this.options,'afterUpdate')+
> - '}}');
> + this.render = (function() {
> + function dispatch(effect, eventName) {
> + if (effect.options[eventName + 'Internal'])
> + effect.options[eventName + 'Internal'](effect);
> + if (effect.options[eventName])
> + effect.options[eventName](effect);
> + }
> +
> + return function(pos) {
> + if (this.state === "idle") {
> + this.state = "running";
> + dispatch(this, 'beforeSetup');
> + if (this.setup) this.setup();
> + dispatch(this, 'afterSetup');
> + }
> + if (this.state === "running") {
> + pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
> + this.position = pos;
> + dispatch(this, 'beforeUpdate');
> + if (this.update) this.update(pos);
> + dispatch(this, 'afterUpdate');
> + }
> + };
> + })();
>
> this.event('beforeStart');
> if (!this.options.sync)
> @@ -392,7 +399,7 @@ Effect.Move = Class.create(Effect.Base, {
>
> // for backwards compatibility
> Effect.MoveBy = function(element, toTop, toLeft) {
> - return new Effect.Move(element,
> + return new Effect.Move(element,
> Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
> };
>
> @@ -507,17 +514,16 @@ Effect.Highlight = Class.create(Effect.Base, {
>
> Effect.ScrollTo = function(element) {
> var options = arguments[1] || { },
> - scrollOffsets = document.viewport.getScrollOffsets(),
> - elementOffsets = $(element).cumulativeOffset(),
> - max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
> + scrollOffsets = document.viewport.getScrollOffsets(),
> + elementOffsets = $(element).cumulativeOffset();
>
> if (options.offset) elementOffsets[1] += options.offset;
>
> return new Effect.Tween(null,
> scrollOffsets.top,
> - elementOffsets[1] > max ? max : elementOffsets[1],
> + elementOffsets[1],
> options,
> - function(p){ scrollTo(scrollOffsets.left, p.round()) }
> + function(p){ scrollTo(scrollOffsets.left, p.round()); }
> );
> };
>
> @@ -554,7 +560,7 @@ Effect.Appear = function(element) {
>
> Effect.Puff = function(element) {
> element = $(element);
> - var oldStyle = {
> + var oldStyle = {
> opacity: element.getInlineOpacity(),
> position: element.getStyle('position'),
> top: element.style.top,
> @@ -563,12 +569,12 @@ Effect.Puff = function(element) {
> height: element.style.height
> };
> return new Effect.Parallel(
> - [ new Effect.Scale(element, 200,
> + [ new Effect.Scale(element, 200,
> { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
> new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
> Object.extend({ duration: 1.0,
> beforeSetupInternal: function(effect) {
> - Position.absolutize(effect.effects[0].element)
> + Position.absolutize(effect.effects[0].element);
> },
> afterFinishInternal: function(effect) {
> effect.effects[0].element.hide().setStyle(oldStyle); }
> @@ -580,12 +586,12 @@ Effect.BlindUp = function(element) {
> element = $(element);
> element.makeClipping();
> return new Effect.Scale(element, 0,
> - Object.extend({ scaleContent: false,
> + Object.extend({ scaleContent: false,
> scaleX: false,
> restoreAfterFinish: true,
> afterFinishInternal: function(effect) {
> effect.element.hide().undoClipping();
> - }
> + }
> }, arguments[1] || { })
> );
> };
> @@ -619,13 +625,13 @@ Effect.SwitchOff = function(element) {
> new Effect.Scale(effect.element, 1, {
> duration: 0.3, scaleFromCenter: true,
> scaleX: false, scaleContent: false, restoreAfterFinish: true,
> - beforeSetup: function(effect) {
> + beforeSetup: function(effect) {
> effect.element.makePositioned().makeClipping();
> },
> afterFinishInternal: function(effect) {
> effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
> }
> - })
> + });
> }
> }, arguments[1] || { }));
> };
> @@ -646,7 +652,7 @@ Effect.DropOut = function(element) {
> },
> afterFinishInternal: function(effect) {
> effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
> - }
> + }
> }, arguments[1] || { }));
> };
>
> @@ -674,7 +680,7 @@ Effect.Shake = function(element) {
> new Effect.Move(effect.element,
> { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
> effect.element.undoPositioned().setStyle(oldStyle);
> - }}) }}) }}) }}) }}) }});
> + }}); }}); }}); }}); }}); }});
> };
>
> Effect.SlideDown = function(element) {
> @@ -682,7 +688,7 @@ Effect.SlideDown = function(element) {
> // SlideDown need to have the content of the element wrapped in a container element with fixed height!
> var oldInnerBottom = element.down().getStyle('bottom');
> var elementDimensions = element.getDimensions();
> - return new Effect.Scale(element, 100, Object.extend({
> + return new Effect.Scale(element, 100, Object.extend({
> scaleContent: false,
> scaleX: false,
> scaleFrom: window.opera ? 0 : 1,
> @@ -742,7 +748,7 @@ Effect.Squish = function(element) {
> effect.element.makeClipping();
> },
> afterFinishInternal: function(effect) {
> - effect.element.hide().undoClipping();
> + effect.element.hide().undoClipping();
> }
> });
> };
> @@ -810,13 +816,13 @@ Effect.Grow = function(element) {
> sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
> ], Object.extend({
> beforeSetup: function(effect) {
> - effect.effects[0].element.setStyle({height: '0px'}).show();
> + effect.effects[0].element.setStyle({height: '0px'}).show();
> },
> afterFinishInternal: function(effect) {
> effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
> }
> }, options)
> - )
> + );
> }
> });
> };
> @@ -838,7 +844,7 @@ Effect.Shrink = function(element) {
>
> var dims = element.getDimensions();
> var moveX, moveY;
> -
> +
> switch (options.direction) {
> case 'top-left':
> moveX = moveY = 0;
> @@ -877,11 +883,13 @@ Effect.Shrink = function(element) {
>
> Effect.Pulsate = function(element) {
> element = $(element);
> - var options = arguments[1] || { };
> - var oldOpacity = element.getInlineOpacity();
> - var transition = options.transition || Effect.Transitions.sinoidal;
> - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
> - reverser.bind(transition);
> + var options = arguments[1] || { },
> + oldOpacity = element.getInlineOpacity(),
> + transition = options.transition || Effect.Transitions.linear,
> + reverser = function(pos){
> + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
> + };
> +
> return new Effect.Opacity(element,
> Object.extend(Object.extend({ duration: 2.0, from: 0,
> afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
> @@ -934,7 +942,7 @@ Effect.Morph = Class.create(Effect.Base, {
> effect.transforms.each(function(transform) {
> effect.element.style[transform.style] = '';
> });
> - }
> + };
> }
> }
> this.start(options);
> @@ -945,7 +953,7 @@ Effect.Morph = Class.create(Effect.Base, {
> if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
> color = color.parseColor();
> return $R(0,2).map(function(i){
> - return parseInt( color.slice(i*2+1,i*2+3), 16 )
> + return parseInt( color.slice(i*2+1,i*2+3), 16 );
> });
> }
> this.transforms = this.style.map(function(pair){
> @@ -978,7 +986,7 @@ Effect.Morph = Class.create(Effect.Base, {
> transform.unit != 'color' &&
> (isNaN(transform.originalValue) || isNaN(transform.targetValue))
> )
> - )
> + );
> });
> },
> update: function(position) {
> @@ -1074,14 +1082,14 @@ if (document.defaultView && document.defaultView.getComputedStyle) {
> Element.getStyles = function(element) {
> element = $(element);
> var css = element.currentStyle, styles;
> - styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
> - hash.set(property, css[property]);
> - return hash;
> + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
> + results[property] = css[property];
> + return results;
> });
> - if (!styles.opacity) styles.set('opacity', element.getOpacity());
> + if (!styles.opacity) styles.opacity = element.getOpacity();
> return styles;
> };
> -};
> +}
>
> Effect.Methods = {
> morph: function(element, style) {
> @@ -1090,7 +1098,7 @@ Effect.Methods = {
> return element;
> },
> visualEffect: function(element, effect, options) {
> - element = $(element)
> + element = $(element);
> var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
> new Effect[klass](element, options);
> return element;
> @@ -1109,7 +1117,7 @@ $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
> element = $(element);
> Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
> return element;
> - }
> + };
> }
> );
>
> @@ -1117,4 +1125,4 @@ $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTex
> function(f) { Effect.Methods[f] = Element[f]; }
> );
>
> -Element.addMethods(Effect.Methods);
> +Element.addMethods(Effect.Methods);
> \ No newline at end of file
> diff --git a/src/public/javascripts/prototype.js b/src/public/javascripts/prototype.js
> index 9f6c857..dfe8ab4 100644
> --- a/src/public/javascripts/prototype.js
> +++ b/src/public/javascripts/prototype.js
> @@ -1,5 +1,5 @@
> -/* Prototype JavaScript framework, version 1.6.0.1
> - * (c) 2005-2007 Sam Stephenson
> +/* Prototype JavaScript framework, version 1.6.0.3
> + * (c) 2005-2008 Sam Stephenson
> *
> * Prototype is freely distributable under the terms of an MIT-style license.
> * For details, see the Prototype web site: http://www.prototypejs.org/
> @@ -7,23 +7,26 @@
> *--------------------------------------------------------------------------*/
>
> var Prototype = {
> - Version: '1.6.0.1',
> + Version: '1.6.0.3',
>
> Browser: {
> - IE: !!(window.attachEvent && !window.opera),
> - Opera: !!window.opera,
> + IE: !!(window.attachEvent &&
> + navigator.userAgent.indexOf('Opera') === -1),
> + Opera: navigator.userAgent.indexOf('Opera') > -1,
> WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
> - Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
> + Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&
> + navigator.userAgent.indexOf('KHTML') === -1,
> MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
> },
>
> BrowserFeatures: {
> XPath: !!document.evaluate,
> + SelectorsAPI: !!document.querySelector,
> ElementExtensions: !!window.HTMLElement,
> SpecificElementExtensions:
> - document.createElement('div').__proto__ &&
> - document.createElement('div').__proto__ !==
> - document.createElement('form').__proto__
> + document.createElement('div')['__proto__'] &&
> + document.createElement('div')['__proto__'] !==
> + document.createElement('form')['__proto__']
> },
>
> ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
> @@ -83,12 +86,13 @@ Class.Methods = {
> var property = properties[i], value = source[property];
> if (ancestor && Object.isFunction(value) &&
> value.argumentNames().first() == "$super") {
> - var method = value, value = Object.extend((function(m) {
> + var method = value;
> + value = (function(m) {
> return function() { return ancestor[m].apply(this, arguments) };
> - })(property).wrap(method), {
> - valueOf: function() { return method },
> - toString: function() { return method.toString() }
> - });
> + })(property).wrap(method);
> +
> + value.valueOf = method.valueOf.bind(method);
> + value.toString = method.toString.bind(method);
> }
> this.prototype[property] = value;
> }
> @@ -110,7 +114,7 @@ Object.extend(Object, {
> try {
> if (Object.isUndefined(object)) return 'undefined';
> if (object === null) return 'null';
> - return object.inspect ? object.inspect() : object.toString();
> + return object.inspect ? object.inspect() : String(object);
> } catch (e) {
> if (e instanceof RangeError) return '...';
> throw e;
> @@ -167,11 +171,12 @@ Object.extend(Object, {
> },
>
> isElement: function(object) {
> - return object && object.nodeType == 1;
> + return !!(object && object.nodeType == 1);
> },
>
> isArray: function(object) {
> - return object && object.constructor === Array;
> + return object != null && typeof object == "object" &&
> + 'splice' in object && 'join' in object;
> },
>
> isHash: function(object) {
> @@ -197,7 +202,8 @@ Object.extend(Object, {
>
> Object.extend(Function.prototype, {
> argumentNames: function() {
> - var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
> + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
> + .replace(/\s+/g, '').split(',');
> return names.length == 1 && !names[0] ? [] : names;
> },
>
> @@ -231,6 +237,11 @@ Object.extend(Function.prototype, {
> }, timeout);
> },
>
> + defer: function() {
> + var args = [0.01].concat($A(arguments));
> + return this.delay.apply(this, args);
> + },
> +
> wrap: function(wrapper) {
> var __method = this;
> return function() {
> @@ -247,8 +258,6 @@ Object.extend(Function.prototype, {
> }
> });
>
> -Function.prototype.defer = Function.prototype.delay.curry(0.01);
> -
> Date.prototype.toJSON = function() {
> return '"' + this.getUTCFullYear() + '-' +
> (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
> @@ -529,7 +538,7 @@ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.proto
> return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
> },
> unescapeHTML: function() {
> - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
> + return this.stripTags().replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
> }
> });
>
> @@ -546,7 +555,7 @@ Object.extend(String.prototype.escapeHTML, {
> text: document.createTextNode('')
> });
>
> -with (String.prototype.escapeHTML) div.appendChild(text);
> +String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
>
> var Template = Class.create({
> initialize: function(template, pattern) {
> @@ -578,7 +587,7 @@ var Template = Class.create({
> }
>
> return before + String.interpret(ctx);
> - }.bind(this));
> + });
> }
> });
> Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
> @@ -588,10 +597,9 @@ var $break = { };
> var Enumerable = {
> each: function(iterator, context) {
> var index = 0;
> - iterator = iterator.bind(context);
> try {
> this._each(function(value) {
> - iterator(value, index++);
> + iterator.call(context, value, index++);
> });
> } catch (e) {
> if (e != $break) throw e;
> @@ -600,47 +608,46 @@ var Enumerable = {
> },
>
> eachSlice: function(number, iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> var index = -number, slices = [], array = this.toArray();
> + if (number < 1) return array;
> while ((index += number) < array.length)
> slices.push(array.slice(index, index+number));
> return slices.collect(iterator, context);
> },
>
> all: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var result = true;
> this.each(function(value, index) {
> - result = result && !!iterator(value, index);
> + result = result && !!iterator.call(context, value, index);
> if (!result) throw $break;
> });
> return result;
> },
>
> any: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var result = false;
> this.each(function(value, index) {
> - if (result = !!iterator(value, index))
> + if (result = !!iterator.call(context, value, index))
> throw $break;
> });
> return result;
> },
>
> collect: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var results = [];
> this.each(function(value, index) {
> - results.push(iterator(value, index));
> + results.push(iterator.call(context, value, index));
> });
> return results;
> },
>
> detect: function(iterator, context) {
> - iterator = iterator.bind(context);
> var result;
> this.each(function(value, index) {
> - if (iterator(value, index)) {
> + if (iterator.call(context, value, index)) {
> result = value;
> throw $break;
> }
> @@ -649,17 +656,16 @@ var Enumerable = {
> },
>
> findAll: function(iterator, context) {
> - iterator = iterator.bind(context);
> var results = [];
> this.each(function(value, index) {
> - if (iterator(value, index))
> + if (iterator.call(context, value, index))
> results.push(value);
> });
> return results;
> },
>
> grep: function(filter, iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var results = [];
>
> if (Object.isString(filter))
> @@ -667,7 +673,7 @@ var Enumerable = {
>
> this.each(function(value, index) {
> if (filter.match(value))
> - results.push(iterator(value, index));
> + results.push(iterator.call(context, value, index));
> });
> return results;
> },
> @@ -695,9 +701,8 @@ var Enumerable = {
> },
>
> inject: function(memo, iterator, context) {
> - iterator = iterator.bind(context);
> this.each(function(value, index) {
> - memo = iterator(memo, value, index);
> + memo = iterator.call(context, memo, value, index);
> });
> return memo;
> },
> @@ -710,10 +715,10 @@ var Enumerable = {
> },
>
> max: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var result;
> this.each(function(value, index) {
> - value = iterator(value, index);
> + value = iterator.call(context, value, index);
> if (result == null || value >= result)
> result = value;
> });
> @@ -721,10 +726,10 @@ var Enumerable = {
> },
>
> min: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var result;
> this.each(function(value, index) {
> - value = iterator(value, index);
> + value = iterator.call(context, value, index);
> if (result == null || value < result)
> result = value;
> });
> @@ -732,10 +737,10 @@ var Enumerable = {
> },
>
> partition: function(iterator, context) {
> - iterator = iterator ? iterator.bind(context) : Prototype.K;
> + iterator = iterator || Prototype.K;
> var trues = [], falses = [];
> this.each(function(value, index) {
> - (iterator(value, index) ?
> + (iterator.call(context, value, index) ?
> trues : falses).push(value);
> });
> return [trues, falses];
> @@ -750,19 +755,20 @@ var Enumerable = {
> },
>
> reject: function(iterator, context) {
> - iterator = iterator.bind(context);
> var results = [];
> this.each(function(value, index) {
> - if (!iterator(value, index))
> + if (!iterator.call(context, value, index))
> results.push(value);
> });
> return results;
> },
>
> sortBy: function(iterator, context) {
> - iterator = iterator.bind(context);
> return this.map(function(value, index) {
> - return {value: value, criteria: iterator(value, index)};
> + return {
> + value: value,
> + criteria: iterator.call(context, value, index)
> + };
> }).sort(function(left, right) {
> var a = left.criteria, b = right.criteria;
> return a < b ? -1 : a > b ? 1 : 0;
> @@ -806,20 +812,24 @@ Object.extend(Enumerable, {
> function $A(iterable) {
> if (!iterable) return [];
> if (iterable.toArray) return iterable.toArray();
> - var length = iterable.length, results = new Array(length);
> + var length = iterable.length || 0, results = new Array(length);
> while (length--) results[length] = iterable[length];
> return results;
> }
>
> if (Prototype.Browser.WebKit) {
> - function $A(iterable) {
> + $A = function(iterable) {
> if (!iterable) return [];
> - if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
> - iterable.toArray) return iterable.toArray();
> - var length = iterable.length, results = new Array(length);
> + // In Safari, only use the `toArray` method if it's not a NodeList.
> + // A NodeList is a function, has an function `item` property, and a numeric
> + // `length` property. Adapted from Google Doctype.
> + if (!(typeof iterable === 'function' && typeof iterable.length ===
> + 'number' && typeof iterable.item === 'function') && iterable.toArray)
> + return iterable.toArray();
> + var length = iterable.length || 0, results = new Array(length);
> while (length--) results[length] = iterable[length];
> return results;
> - }
> + };
> }
>
> Array.from = $A;
> @@ -962,8 +972,8 @@ Object.extend(Number.prototype, {
> return this + 1;
> },
>
> - times: function(iterator) {
> - $R(0, this, true).each(iterator);
> + times: function(iterator, context) {
> + $R(0, this, true).each(iterator, context);
> return this;
> },
>
> @@ -1010,7 +1020,9 @@ var Hash = Class.create(Enumerable, (function() {
> },
>
> get: function(key) {
> - return this._object[key];
> + // simulating poorly supported hasOwnProperty
> + if (this._object[key] !== Object.prototype[key])
> + return this._object[key];
> },
>
> unset: function(key) {
> @@ -1050,14 +1062,14 @@ var Hash = Class.create(Enumerable, (function() {
> },
>
> toQueryString: function() {
> - return this.map(function(pair) {
> + return this.inject([], function(results, pair) {
> var key = encodeURIComponent(pair.key), values = pair.value;
>
> if (values && typeof values == 'object') {
> if (Object.isArray(values))
> - return values.map(toQueryPair.curry(key)).join('&');
> - }
> - return toQueryPair(key, values);
> + return results.concat(values.map(toQueryPair.curry(key)));
> + } else results.push(toQueryPair(key, values));
> + return results;
> }).join('&');
> },
>
> @@ -1298,7 +1310,7 @@ Ajax.Request = Class.create(Ajax.Base, {
>
> var contentType = response.getHeader('Content-type');
> if (this.options.evalJS == 'force'
> - || (this.options.evalJS && contentType
> + || (this.options.evalJS && this.isSameOrigin() && contentType
> && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
> this.evalResponse();
> }
> @@ -1316,9 +1328,18 @@ Ajax.Request = Class.create(Ajax.Base, {
> }
> },
>
> + isSameOrigin: function() {
> + var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
> + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
> + protocol: location.protocol,
> + domain: document.domain,
> + port: location.port ? ':' + location.port : ''
> + }));
> + },
> +
> getHeader: function(name) {
> try {
> - return this.transport.getResponseHeader(name);
> + return this.transport.getResponseHeader(name) || null;
> } catch (e) { return null }
> },
>
> @@ -1391,7 +1412,8 @@ Ajax.Response = Class.create({
> if (!json) return null;
> json = decodeURIComponent(escape(json));
> try {
> - return json.evalJSON(this.request.options.sanitizeJSON);
> + return json.evalJSON(this.request.options.sanitizeJSON ||
> + !this.request.isSameOrigin());
> } catch (e) {
> this.request.dispatchException(e);
> }
> @@ -1404,7 +1426,8 @@ Ajax.Response = Class.create({
> this.responseText.blank())
> return null;
> try {
> - return this.responseText.evalJSON(options.sanitizeJSON);
> + return this.responseText.evalJSON(options.sanitizeJSON ||
> + !this.request.isSameOrigin());
> } catch (e) {
> this.request.dispatchException(e);
> }
> @@ -1546,6 +1569,7 @@ if (!Node.ELEMENT_NODE) {
> return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
> };
> Object.extend(this.Element, element || { });
> + if (element) this.Element.prototype = element.prototype;
> }).call(window);
>
> Element.cache = { };
> @@ -1562,12 +1586,14 @@ Element.Methods = {
> },
>
> hide: function(element) {
> - $(element).style.display = 'none';
> + element = $(element);
> + element.style.display = 'none';
> return element;
> },
>
> show: function(element) {
> - $(element).style.display = '';
> + element = $(element);
> + element.style.display = '';
> return element;
> },
>
> @@ -1608,24 +1634,28 @@ Element.Methods = {
> Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
> insertions = {bottom:insertions};
>
> - var content, t, range;
> + var content, insert, tagName, childNodes;
>
> - for (position in insertions) {
> + for (var position in insertions) {
> content = insertions[position];
> position = position.toLowerCase();
> - t = Element._insertionTranslations[position];
> + insert = Element._insertionTranslations[position];
>
> if (content && content.toElement) content = content.toElement();
> if (Object.isElement(content)) {
> - t.insert(element, content);
> + insert(element, content);
> continue;
> }
>
> content = Object.toHTML(content);
>
> - range = element.ownerDocument.createRange();
> - t.initializeRange(element, range);
> - t.insert(element, range.createContextualFragment(content.stripScripts()));
> + tagName = ((position == 'before' || position == 'after')
> + ? element.parentNode : element).tagName.toUpperCase();
> +
> + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
> +
> + if (position == 'top' || position == 'after') childNodes.reverse();
> + childNodes.each(insert.curry(element));
>
> content.evalScripts.bind(content).defer();
> }
> @@ -1670,7 +1700,7 @@ Element.Methods = {
> },
>
> descendants: function(element) {
> - return $(element).getElementsBySelector("*");
> + return $(element).select("*");
> },
>
> firstDescendant: function(element) {
> @@ -1709,32 +1739,31 @@ Element.Methods = {
> element = $(element);
> if (arguments.length == 1) return $(element.parentNode);
> var ancestors = element.ancestors();
> - return expression ? Selector.findElement(ancestors, expression, index) :
> - ancestors[index || 0];
> + return Object.isNumber(expression) ? ancestors[expression] :
> + Selector.findElement(ancestors, expression, index);
> },
>
> down: function(element, expression, index) {
> element = $(element);
> if (arguments.length == 1) return element.firstDescendant();
> - var descendants = element.descendants();
> - return expression ? Selector.findElement(descendants, expression, index) :
> - descendants[index || 0];
> + return Object.isNumber(expression) ? element.descendants()[expression] :
> + Element.select(element, expression)[index || 0];
> },
>
> previous: function(element, expression, index) {
> element = $(element);
> if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
> var previousSiblings = element.previousSiblings();
> - return expression ? Selector.findElement(previousSiblings, expression, index) :
> - previousSiblings[index || 0];
> + return Object.isNumber(expression) ? previousSiblings[expression] :
> + Selector.findElement(previousSiblings, expression, index);
> },
>
> next: function(element, expression, index) {
> element = $(element);
> if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
> var nextSiblings = element.nextSiblings();
> - return expression ? Selector.findElement(nextSiblings, expression, index) :
> - nextSiblings[index || 0];
> + return Object.isNumber(expression) ? nextSiblings[expression] :
> + Selector.findElement(nextSiblings, expression, index);
> },
>
> select: function() {
> @@ -1848,23 +1877,16 @@ Element.Methods = {
>
> descendantOf: function(element, ancestor) {
> element = $(element), ancestor = $(ancestor);
> - var originalAncestor = ancestor;
>
> if (element.compareDocumentPosition)
> return (element.compareDocumentPosition(ancestor) & 8) === 8;
>
> - if (element.sourceIndex && !Prototype.Browser.Opera) {
> - var e = element.sourceIndex, a = ancestor.sourceIndex,
> - nextAncestor = ancestor.nextSibling;
> - if (!nextAncestor) {
> - do { ancestor = ancestor.parentNode; }
> - while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
> - }
> - if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
> - }
> + if (ancestor.contains)
> + return ancestor.contains(element) && ancestor !== element;
>
> while (element = element.parentNode)
> - if (element == originalAncestor) return true;
> + if (element == ancestor) return true;
> +
> return false;
> },
>
> @@ -1879,7 +1901,7 @@ Element.Methods = {
> element = $(element);
> style = style == 'float' ? 'cssFloat' : style.camelize();
> var value = element.style[style];
> - if (!value) {
> + if (!value || value == 'auto') {
> var css = document.defaultView.getComputedStyle(element, null);
> value = css ? css[style] : null;
> }
> @@ -1918,7 +1940,7 @@ Element.Methods = {
>
> getDimensions: function(element) {
> element = $(element);
> - var display = $(element).getStyle('display');
> + var display = element.getStyle('display');
> if (display != 'none' && display != null) // Safari bug
> return {width: element.offsetWidth, height: element.offsetHeight};
>
> @@ -1947,7 +1969,7 @@ Element.Methods = {
> element.style.position = 'relative';
> // Opera returns the offset relative to the positioning context, when an
> // element is position relative but top and left have not been defined
> - if (window.opera) {
> + if (Prototype.Browser.Opera) {
> element.style.top = 0;
> element.style.left = 0;
> }
> @@ -2002,9 +2024,9 @@ Element.Methods = {
> valueL += element.offsetLeft || 0;
> element = element.offsetParent;
> if (element) {
> - if (element.tagName == 'BODY') break;
> + if (element.tagName.toUpperCase() == 'BODY') break;
> var p = Element.getStyle(element, 'position');
> - if (p == 'relative' || p == 'absolute') break;
> + if (p !== 'static') break;
> }
> } while (element);
> return Element._returnOffset(valueL, valueT);
> @@ -2012,7 +2034,7 @@ Element.Methods = {
>
> absolutize: function(element) {
> element = $(element);
> - if (element.getStyle('position') == 'absolute') return;
> + if (element.getStyle('position') == 'absolute') return element;
> // Position.prepare(); // To be done manually by Scripty when it needs it.
>
> var offsets = element.positionedOffset();
> @@ -2036,7 +2058,7 @@ Element.Methods = {
>
> relativize: function(element) {
> element = $(element);
> - if (element.getStyle('position') == 'relative') return;
> + if (element.getStyle('position') == 'relative') return element;
> // Position.prepare(); // To be done manually by Scripty when it needs it.
>
> element.style.position = 'relative';
> @@ -2087,7 +2109,7 @@ Element.Methods = {
>
> element = forElement;
> do {
> - if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
> + if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
> valueT -= element.scrollTop || 0;
> valueL -= element.scrollLeft || 0;
> }
> @@ -2153,46 +2175,6 @@ Element._attributeTranslations = {
> }
> };
>
> -
> -if (!document.createRange || Prototype.Browser.Opera) {
> - Element.Methods.insert = function(element, insertions) {
> - element = $(element);
> -
> - if (Object.isString(insertions) || Object.isNumber(insertions) ||
> - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
> - insertions = { bottom: insertions };
> -
> - var t = Element._insertionTranslations, content, position, pos, tagName;
> -
> - for (position in insertions) {
> - content = insertions[position];
> - position = position.toLowerCase();
> - pos = t[position];
> -
> - if (content && content.toElement) content = content.toElement();
> - if (Object.isElement(content)) {
> - pos.insert(element, content);
> - continue;
> - }
> -
> - content = Object.toHTML(content);
> - tagName = ((position == 'before' || position == 'after')
> - ? element.parentNode : element).tagName.toUpperCase();
> -
> - if (t.tags[tagName]) {
> - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
> - if (position == 'top' || position == 'after') fragments.reverse();
> - fragments.each(pos.insert.curry(element));
> - }
> - else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
> -
> - content.evalScripts.bind(content).defer();
> - }
> -
> - return element;
> - };
> -}
> -
> if (Prototype.Browser.Opera) {
> Element.Methods.getStyle = Element.Methods.getStyle.wrap(
> function(proceed, element, style) {
> @@ -2237,12 +2219,36 @@ if (Prototype.Browser.Opera) {
> }
>
> else if (Prototype.Browser.IE) {
> - $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
> + // IE doesn't report offsets correctly for static elements, so we change them
> + // to "relative" to get the values, then change them back.
> + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
> + function(proceed, element) {
> + element = $(element);
> + // IE throws an error if element is not in document
> + try { element.offsetParent }
> + catch(e) { return $(document.body) }
> + var position = element.getStyle('position');
> + if (position !== 'static') return proceed(element);
> + element.setStyle({ position: 'relative' });
> + var value = proceed(element);
> + element.setStyle({ position: position });
> + return value;
> + }
> + );
> +
> + $w('positionedOffset viewportOffset').each(function(method) {
> Element.Methods[method] = Element.Methods[method].wrap(
> function(proceed, element) {
> element = $(element);
> + try { element.offsetParent }
> + catch(e) { return Element._returnOffset(0,0) }
> var position = element.getStyle('position');
> - if (position != 'static') return proceed(element);
> + if (position !== 'static') return proceed(element);
> + // Trigger hasLayout on the offset parent so that IE6 reports
> + // accurate offsetTop and offsetLeft values for position: fixed.
> + var offsetParent = element.getOffsetParent();
> + if (offsetParent && offsetParent.getStyle('position') === 'fixed')
> + offsetParent.setStyle({ zoom: 1 });
> element.setStyle({ position: 'relative' });
> var value = proceed(element);
> element.setStyle({ position: position });
> @@ -2251,6 +2257,14 @@ else if (Prototype.Browser.IE) {
> );
> });
>
> + Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
> + function(proceed, element) {
> + try { element.offsetParent }
> + catch(e) { return Element._returnOffset(0,0) }
> + return proceed(element);
> + }
> + );
> +
> Element.Methods.getStyle = function(element, style) {
> element = $(element);
> style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
> @@ -2324,7 +2338,10 @@ else if (Prototype.Browser.IE) {
> };
>
> Element._attributeTranslations.write = {
> - names: Object.clone(Element._attributeTranslations.read.names),
> + names: Object.extend({
> + cellpadding: 'cellPadding',
> + cellspacing: 'cellSpacing'
> + }, Element._attributeTranslations.read.names),
> values: {
> checked: function(element, value) {
> element.checked = !!value;
> @@ -2339,7 +2356,7 @@ else if (Prototype.Browser.IE) {
> Element._attributeTranslations.has = {};
>
> $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
> - 'encType maxLength readOnly longDesc').each(function(attr) {
> + 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
> Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
> Element._attributeTranslations.has[attr.toLowerCase()] = attr;
> });
> @@ -2392,7 +2409,7 @@ else if (Prototype.Browser.WebKit) {
> (value < 0.00001) ? 0 : value;
>
> if (value == 1)
> - if(element.tagName == 'IMG' && element.width) {
> + if(element.tagName.toUpperCase() == 'IMG' && element.width) {
> element.width++; element.width--;
> } else try {
> var n = document.createTextNode(' ');
> @@ -2444,7 +2461,7 @@ if (Prototype.Browser.IE || Prototype.Browser.Opera) {
> };
> }
>
> -if (document.createElement('div').outerHTML) {
> +if ('outerHTML' in document.createElement('div')) {
> Element.Methods.replace = function(element, content) {
> element = $(element);
>
> @@ -2482,45 +2499,25 @@ Element._returnOffset = function(l, t) {
>
> Element._getContentFromAnonymousElement = function(tagName, html) {
> var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
> - div.innerHTML = t[0] + html + t[1];
> - t[2].times(function() { div = div.firstChild });
> + if (t) {
> + div.innerHTML = t[0] + html + t[1];
> + t[2].times(function() { div = div.firstChild });
> + } else div.innerHTML = html;
> return $A(div.childNodes);
> };
>
> Element._insertionTranslations = {
> - before: {
> - adjacency: 'beforeBegin',
> - insert: function(element, node) {
> - element.parentNode.insertBefore(node, element);
> - },
> - initializeRange: function(element, range) {
> - range.setStartBefore(element);
> - }
> + before: function(element, node) {
> + element.parentNode.insertBefore(node, element);
> },
> - top: {
> - adjacency: 'afterBegin',
> - insert: function(element, node) {
> - element.insertBefore(node, element.firstChild);
> - },
> - initializeRange: function(element, range) {
> - range.selectNodeContents(element);
> - range.collapse(true);
> - }
> + top: function(element, node) {
> + element.insertBefore(node, element.firstChild);
> },
> - bottom: {
> - adjacency: 'beforeEnd',
> - insert: function(element, node) {
> - element.appendChild(node);
> - }
> + bottom: function(element, node) {
> + element.appendChild(node);
> },
> - after: {
> - adjacency: 'afterEnd',
> - insert: function(element, node) {
> - element.parentNode.insertBefore(node, element.nextSibling);
> - },
> - initializeRange: function(element, range) {
> - range.setStartAfter(element);
> - }
> + after: function(element, node) {
> + element.parentNode.insertBefore(node, element.nextSibling);
> },
> tags: {
> TABLE: ['<table>', '</table>', 1],
> @@ -2532,7 +2529,6 @@ Element._insertionTranslations = {
> };
>
> (function() {
> - this.bottom.initializeRange = this.top.initializeRange;
> Object.extend(this.tags, {
> THEAD: this.tags.TBODY,
> TFOOT: this.tags.TBODY,
> @@ -2544,7 +2540,7 @@ Element.Methods.Simulated = {
> hasAttribute: function(element, attribute) {
> attribute = Element._attributeTranslations.has[attribute] || attribute;
> var node = $(element).getAttributeNode(attribute);
> - return node && node.specified;
> + return !!(node && node.specified);
> }
> };
>
> @@ -2553,9 +2549,9 @@ Element.Methods.ByTag = { };
> Object.extend(Element, Element.Methods);
>
> if (!Prototype.BrowserFeatures.ElementExtensions &&
> - document.createElement('div').__proto__) {
> + document.createElement('div')['__proto__']) {
> window.HTMLElement = { };
> - window.HTMLElement.prototype = document.createElement('div').__proto__;
> + window.HTMLElement.prototype = document.createElement('div')['__proto__'];
> Prototype.BrowserFeatures.ElementExtensions = true;
> }
>
> @@ -2570,7 +2566,7 @@ Element.extend = (function() {
> element.nodeType != 1 || element == window) return element;
>
> var methods = Object.clone(Methods),
> - tagName = element.tagName, property, value;
> + tagName = element.tagName.toUpperCase(), property, value;
>
> // extend methods for specific tags
> if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
> @@ -2666,7 +2662,7 @@ Element.addMethods = function(methods) {
> if (window[klass]) return window[klass];
>
> window[klass] = { };
> - window[klass].prototype = document.createElement(tagName).__proto__;
> + window[klass].prototype = document.createElement(tagName)['__proto__'];
> return window[klass];
> }
>
> @@ -2692,12 +2688,18 @@ Element.addMethods = function(methods) {
>
> document.viewport = {
> getDimensions: function() {
> - var dimensions = { };
> - var B = Prototype.Browser;
> + var dimensions = { }, B = Prototype.Browser;
> $w('width height').each(function(d) {
> var D = d.capitalize();
> - dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
> - (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
> + if (B.WebKit && !document.evaluate) {
> + // Safari <3.0 needs self.innerWidth/Height
> + dimensions[d] = self['inner' + D];
> + } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
> + // Opera <9.5 needs document.body.clientWidth/Height
> + dimensions[d] = document.body['client' + D]
> + } else {
> + dimensions[d] = document.documentElement['client' + D];
> + }
> });
> return dimensions;
> },
> @@ -2716,14 +2718,24 @@ document.viewport = {
> window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
> }
> };
> -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
> +/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
> * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
> * license. Please see http://www.yui-ext.com/ for more information. */
>
> var Selector = Class.create({
> initialize: function(expression) {
> this.expression = expression.strip();
> - this.compileMatcher();
> +
> + if (this.shouldUseSelectorsAPI()) {
> + this.mode = 'selectorsAPI';
> + } else if (this.shouldUseXPath()) {
> + this.mode = 'xpath';
> + this.compileXPathMatcher();
> + } else {
> + this.mode = "normal";
> + this.compileMatcher();
> + }
> +
> },
>
> shouldUseXPath: function() {
> @@ -2738,16 +2750,29 @@ var Selector = Class.create({
>
> // XPath can't do namespaced attributes, nor can it read
> // the "checked" property from DOM nodes
> - if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
> + if ((/(\[[\w-]*?:|:checked)/).test(e))
> return false;
>
> return true;
> },
>
> - compileMatcher: function() {
> - if (this.shouldUseXPath())
> - return this.compileXPathMatcher();
> + shouldUseSelectorsAPI: function() {
> + if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
> +
> + if (!Selector._div) Selector._div = new Element('div');
> +
> + // Make sure the browser treats the selector as valid. Test on an
> + // isolated element to minimize cost of this check.
> + try {
> + Selector._div.querySelector(this.expression);
> + } catch(e) {
> + return false;
> + }
>
> + return true;
> + },
> +
> + compileMatcher: function() {
> var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
> c = Selector.criteria, le, p, m;
>
> @@ -2765,7 +2790,7 @@ var Selector = Class.create({
> p = ps[i];
> if (m = e.match(p)) {
> this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
> - new Template(c[i]).evaluate(m));
> + new Template(c[i]).evaluate(m));
> e = e.replace(m[0], '');
> break;
> }
> @@ -2804,8 +2829,27 @@ var Selector = Class.create({
>
> findElements: function(root) {
> root = root || document;
> - if (this.xpath) return document._getElementsByXPath(this.xpath, root);
> - return this.matcher(root);
> + var e = this.expression, results;
> +
> + switch (this.mode) {
> + case 'selectorsAPI':
> + // querySelectorAll queries document-wide, then filters to descendants
> + // of the context element. That's not what we want.
> + // Add an explicit context to the selector if necessary.
> + if (root !== document) {
> + var oldId = root.id, id = $(root).identify();
> + e = "#" + id + " " + e;
> + }
> +
> + results = $A(root.querySelectorAll(e)).map(Element.extend);
> + root.id = oldId;
> +
> + return results;
> + case 'xpath':
> + return document._getElementsByXPath(this.xpath, root);
> + default:
> + return this.matcher(root);
> + }
> },
>
> match: function(element) {
> @@ -2896,10 +2940,10 @@ Object.extend(Selector, {
> 'first-child': '[not(preceding-sibling::*)]',
> 'last-child': '[not(following-sibling::*)]',
> 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
> - 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
> + 'empty': "[count(*) = 0 and (count(text()) = 0)]",
> 'checked': "[@checked]",
> - 'disabled': "[@disabled]",
> - 'enabled': "[not(@disabled)]",
> + 'disabled': "[(@disabled) and (@type!='hidden')]",
> + 'enabled': "[not(@disabled) and (@type!='hidden')]",
> 'not': function(m) {
> var e = m[6], p = Selector.patterns,
> x = Selector.xpath, le, v;
> @@ -2959,13 +3003,13 @@ Object.extend(Selector, {
> },
>
> criteria: {
> - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
> - className: 'n = h.className(n, r, "#{1}", c); c = false;',
> - id: 'n = h.id(n, r, "#{1}", c); c = false;',
> - attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
> + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
> + className: 'n = h.className(n, r, "#{1}", c); c = false;',
> + id: 'n = h.id(n, r, "#{1}", c); c = false;',
> + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
> attr: function(m) {
> m[3] = (m[5] || m[6]);
> - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
> + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
> },
> pseudo: function(m) {
> if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
> @@ -2989,8 +3033,9 @@ Object.extend(Selector, {
> tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
> id: /^#([\w\-\*]+)(\b|$)/,
> className: /^\.([\w\-\*]+)(\b|$)/,
> - pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
> - attrPresence: /^\[([\w]+)\]/,
> + pseudo:
> +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
> + attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
> attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
> },
>
> @@ -3014,7 +3059,7 @@ Object.extend(Selector, {
>
> attr: function(element, matches) {
> var nodeValue = Element.readAttribute(element, matches[1]);
> - return Selector.operators[matches[2]](nodeValue, matches[3]);
> + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
> }
> },
>
> @@ -3029,14 +3074,15 @@ Object.extend(Selector, {
>
> // marks an array of nodes for counting
> mark: function(nodes) {
> + var _true = Prototype.emptyFunction;
> for (var i = 0, node; node = nodes[i]; i++)
> - node._counted = true;
> + node._countedByPrototype = _true;
> return nodes;
> },
>
> unmark: function(nodes) {
> for (var i = 0, node; node = nodes[i]; i++)
> - node._counted = undefined;
> + node._countedByPrototype = undefined;
> return nodes;
> },
>
> @@ -3044,15 +3090,15 @@ Object.extend(Selector, {
> // "ofType" flag indicates whether we're indexing for nth-of-type
> // rather than nth-child
> index: function(parentNode, reverse, ofType) {
> - parentNode._counted = true;
> + parentNode._countedByPrototype = Prototype.emptyFunction;
> if (reverse) {
> for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
> var node = nodes[i];
> - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
> + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
> }
> } else {
> for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
> - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
> + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
> }
> },
>
> @@ -3061,8 +3107,8 @@ Object.extend(Selector, {
> if (nodes.length == 0) return nodes;
> var results = [], n;
> for (var i = 0, l = nodes.length; i < l; i++)
> - if (!(n = nodes[i])._counted) {
> - n._counted = true;
> + if (!(n = nodes[i])._countedByPrototype) {
> + n._countedByPrototype = Prototype.emptyFunction;
> results.push(Element.extend(n));
> }
> return Selector.handlers.unmark(results);
> @@ -3102,7 +3148,7 @@ Object.extend(Selector, {
>
> nextElementSibling: function(node) {
> while (node = node.nextSibling)
> - if (node.nodeType == 1) return node;
> + if (node.nodeType == 1) return node;
> return null;
> },
>
> @@ -3114,7 +3160,7 @@ Object.extend(Selector, {
>
> // TOKEN FUNCTIONS
> tagName: function(nodes, root, tagName, combinator) {
> - tagName = tagName.toUpperCase();
> + var uTagName = tagName.toUpperCase();
> var results = [], h = Selector.handlers;
> if (nodes) {
> if (combinator) {
> @@ -3127,7 +3173,7 @@ Object.extend(Selector, {
> if (tagName == "*") return nodes;
> }
> for (var i = 0, node; node = nodes[i]; i++)
> - if (node.tagName.toUpperCase() == tagName) results.push(node);
> + if (node.tagName.toUpperCase() === uTagName) results.push(node);
> return results;
> } else return root.getElementsByTagName(tagName);
> },
> @@ -3174,16 +3220,18 @@ Object.extend(Selector, {
> return results;
> },
>
> - attrPresence: function(nodes, root, attr) {
> + attrPresence: function(nodes, root, attr, combinator) {
> if (!nodes) nodes = root.getElementsByTagName("*");
> + if (nodes && combinator) nodes = this[combinator](nodes);
> var results = [];
> for (var i = 0, node; node = nodes[i]; i++)
> if (Element.hasAttribute(node, attr)) results.push(node);
> return results;
> },
>
> - attr: function(nodes, root, attr, value, operator) {
> + attr: function(nodes, root, attr, value, operator, combinator) {
> if (!nodes) nodes = root.getElementsByTagName("*");
> + if (nodes && combinator) nodes = this[combinator](nodes);
> var handler = Selector.operators[operator], results = [];
> for (var i = 0, node; node = nodes[i]; i++) {
> var nodeValue = Element.readAttribute(node, attr);
> @@ -3262,7 +3310,7 @@ Object.extend(Selector, {
> var h = Selector.handlers, results = [], indexed = [], m;
> h.mark(nodes);
> for (var i = 0, node; node = nodes[i]; i++) {
> - if (!node.parentNode._counted) {
> + if (!node.parentNode._countedByPrototype) {
> h.index(node.parentNode, reverse, ofType);
> indexed.push(node.parentNode);
> }
> @@ -3289,7 +3337,7 @@ Object.extend(Selector, {
> 'empty': function(nodes, value, root) {
> for (var i = 0, results = [], node; node = nodes[i]; i++) {
> // IE treats comments as element nodes
> - if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
> + if (node.tagName == '!' || node.firstChild) continue;
> results.push(node);
> }
> return results;
> @@ -3300,14 +3348,15 @@ Object.extend(Selector, {
> var exclusions = new Selector(selector).findElements(root);
> h.mark(exclusions);
> for (var i = 0, results = [], node; node = nodes[i]; i++)
> - if (!node._counted) results.push(node);
> + if (!node._countedByPrototype) results.push(node);
> h.unmark(exclusions);
> return results;
> },
>
> 'enabled': function(nodes, value, root) {
> for (var i = 0, results = [], node; node = nodes[i]; i++)
> - if (!node.disabled) results.push(node);
> + if (!node.disabled && (!node.type || node.type !== 'hidden'))
> + results.push(node);
> return results;
> },
>
> @@ -3327,18 +3376,29 @@ Object.extend(Selector, {
> operators: {
> '=': function(nv, v) { return nv == v; },
> '!=': function(nv, v) { return nv != v; },
> - '^=': function(nv, v) { return nv.startsWith(v); },
> + '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
> + '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
> + '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
> '$=': function(nv, v) { return nv.endsWith(v); },
> '*=': function(nv, v) { return nv.include(v); },
> '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
> - '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
> + '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
> + '-').include('-' + (v || "").toUpperCase() + '-'); }
> + },
> +
> + split: function(expression) {
> + var expressions = [];
> + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
> + expressions.push(m[1].strip());
> + });
> + return expressions;
> },
>
> matchElements: function(elements, expression) {
> - var matches = new Selector(expression).findElements(), h = Selector.handlers;
> + var matches = $$(expression), h = Selector.handlers;
> h.mark(matches);
> for (var i = 0, results = [], element; element = elements[i]; i++)
> - if (element._counted) results.push(element);
> + if (element._countedByPrototype) results.push(element);
> h.unmark(matches);
> return results;
> },
> @@ -3351,11 +3411,7 @@ Object.extend(Selector, {
> },
>
> findChildElements: function(element, expressions) {
> - var exprs = expressions.join(',');
> - expressions = [];
> - exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
> - expressions.push(m[1].strip());
> - });
> + expressions = Selector.split(expressions.join(','));
> var results = [], h = Selector.handlers;
> for (var i = 0, l = expressions.length, selector; i < l; i++) {
> selector = new Selector(expressions[i].strip());
> @@ -3366,13 +3422,22 @@ Object.extend(Selector, {
> });
>
> if (Prototype.Browser.IE) {
> - // IE returns comment nodes on getElementsByTagName("*").
> - // Filter them out.
> - Selector.handlers.concat = function(a, b) {
> - for (var i = 0, node; node = b[i]; i++)
> - if (node.tagName !== "!") a.push(node);
> - return a;
> - };
> + Object.extend(Selector.handlers, {
> + // IE returns comment nodes on getElementsByTagName("*").
> + // Filter them out.
> + concat: function(a, b) {
> + for (var i = 0, node; node = b[i]; i++)
> + if (node.tagName !== "!") a.push(node);
> + return a;
> + },
> +
> + // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
> + unmark: function(nodes) {
> + for (var i = 0, node; node = nodes[i]; i++)
> + node.removeAttribute('_countedByPrototype');
> + return nodes;
> + }
> + });
> }
>
> function $$() {
> @@ -3392,7 +3457,7 @@ var Form = {
> var data = elements.inject({ }, function(result, element) {
> if (!element.disabled && element.name) {
> key = element.name; value = $(element).getValue();
> - if (value != null && (element.type != 'submit' || (!submitted &&
> + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
> submit !== false && (!submit || key == submit) && (submitted = true)))) {
> if (key in result) {
> // a key is already present; construct an array of values
> @@ -3553,7 +3618,6 @@ Form.Element.Methods = {
>
> disable: function(element) {
> element = $(element);
> - element.blur();
> element.disabled = true;
> return element;
> },
> @@ -3593,22 +3657,22 @@ Form.Element.Serializers = {
> else element.value = value;
> },
>
> - select: function(element, index) {
> - if (Object.isUndefined(index))
> + select: function(element, value) {
> + if (Object.isUndefined(value))
> return this[element.type == 'select-one' ?
> 'selectOne' : 'selectMany'](element);
> else {
> - var opt, value, single = !Object.isArray(index);
> + var opt, currentValue, single = !Object.isArray(value);
> for (var i = 0, length = element.length; i < length; i++) {
> opt = element.options[i];
> - value = this.optionValue(opt);
> + currentValue = this.optionValue(opt);
> if (single) {
> - if (value == index) {
> + if (currentValue == value) {
> opt.selected = true;
> return;
> }
> }
> - else opt.selected = index.include(value);
> + else opt.selected = value.include(currentValue);
> }
> }
> },
> @@ -3779,8 +3843,23 @@ Event.Methods = (function() {
> isRightClick: function(event) { return isButton(event, 2) },
>
> element: function(event) {
> - var node = Event.extend(event).target;
> - return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
> + event = Event.extend(event);
> +
> + var node = event.target,
> + type = event.type,
> + currentTarget = event.currentTarget;
> +
> + if (currentTarget && currentTarget.tagName) {
> + // Firefox screws up the "click" event when moving between radio buttons
> + // via arrow keys. It also screws up the "load" and "error" events on images,
> + // reporting the document as the target instead of the original image.
> + if (type === 'load' || type === 'error' ||
> + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
> + && currentTarget.type === 'radio'))
> + node = currentTarget;
> + }
> + if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
> + return Element.extend(node);
> },
>
> findElement: function(event, expression) {
> @@ -3791,11 +3870,15 @@ Event.Methods = (function() {
> },
>
> pointer: function(event) {
> + var docElement = document.documentElement,
> + body = document.body || { scrollLeft: 0, scrollTop: 0 };
> return {
> x: event.pageX || (event.clientX +
> - (document.documentElement.scrollLeft || document.body.scrollLeft)),
> + (docElement.scrollLeft || body.scrollLeft) -
> + (docElement.clientLeft || 0)),
> y: event.pageY || (event.clientY +
> - (document.documentElement.scrollTop || document.body.scrollTop))
> + (docElement.scrollTop || body.scrollTop) -
> + (docElement.clientTop || 0))
> };
> },
>
> @@ -3840,7 +3923,7 @@ Event.extend = (function() {
> };
>
> } else {
> - Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
> + Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
> Object.extend(Event.prototype, methods);
> return Prototype.K;
> }
> @@ -3850,9 +3933,9 @@ Object.extend(Event, (function() {
> var cache = Event.cache;
>
> function getEventID(element) {
> - if (element._eventID) return element._eventID;
> + if (element._prototypeEventID) return element._prototypeEventID[0];
> arguments.callee.id = arguments.callee.id || 1;
> - return element._eventID = ++arguments.callee.id;
> + return element._prototypeEventID = [++arguments.callee.id];
> }
>
> function getDOMEventName(eventName) {
> @@ -3880,7 +3963,7 @@ Object.extend(Event, (function() {
> return false;
>
> Event.extend(event);
> - handler.call(element, event)
> + handler.call(element, event);
> };
>
> wrapper.handler = handler;
> @@ -3905,10 +3988,20 @@ Object.extend(Event, (function() {
> cache[id][eventName] = null;
> }
>
> +
> + // Internet Explorer needs to remove event handlers on page unload
> + // in order to avoid memory leaks.
> if (window.attachEvent) {
> window.attachEvent("onunload", destroyCache);
> }
>
> + // Safari has a dummy event handler on page unload so that it won't
> + // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
> + // object when page is returned to via the back button using its bfcache.
> + if (Prototype.Browser.WebKit) {
> + window.addEventListener('unload', Prototype.emptyFunction, false);
> + }
> +
> return {
> observe: function(element, eventName, handler) {
> element = $(element);
> @@ -3962,11 +4055,12 @@ Object.extend(Event, (function() {
> if (element == document && document.createEvent && !element.dispatchEvent)
> element = document.documentElement;
>
> + var event;
> if (document.createEvent) {
> - var event = document.createEvent("HTMLEvents");
> + event = document.createEvent("HTMLEvents");
> event.initEvent("dataavailable", true, true);
> } else {
> - var event = document.createEventObject();
> + event = document.createEventObject();
> event.eventType = "ondataavailable";
> }
>
> @@ -3995,20 +4089,21 @@ Element.addMethods({
> Object.extend(document, {
> fire: Element.Methods.fire.methodize(),
> observe: Element.Methods.observe.methodize(),
> - stopObserving: Element.Methods.stopObserving.methodize()
> + stopObserving: Element.Methods.stopObserving.methodize(),
> + loaded: false
> });
>
> (function() {
> /* Support for the DOMContentLoaded event is based on work by Dan Webb,
> Matthias Miller, Dean Edwards and John Resig. */
>
> - var timer, fired = false;
> + var timer;
>
> function fireContentLoadedEvent() {
> - if (fired) return;
> + if (document.loaded) return;
> if (timer) window.clearInterval(timer);
> document.fire("dom:loaded");
> - fired = true;
> + document.loaded = true;
> }
>
> if (document.addEventListener) {
>
ACK
More information about the ovirt-devel
mailing list