Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] Upgrade server to run on Rails 2.3.2/F11
Note that one of the 8 patches (#6) will be sent separately in reply to this email, as some of the replaced lines are too long, so git won't let me send the email. However, there is nothing wrong with that patch, and it should be applied in the sequence listed below. Note also that I assume this will be tested on a clean f11 install, rather than an upgrade of an existing ovirt server (since if it was on F11, it was broken anyway). [PATCH server 1/8] Update core app config for Rails 2.3.2 [PATCH server 2/8] test_helper and unit test updates for Rails 2.3.2 [PATCH server 3/8] Update functional tests to work with Rails 2.3.2 [PATCH server 4/8] Switch from old connection style to current. [PATCH server 5/8] No need to track schema.rb and logs right now. [PATCH server 6/8] Update will_paginate to latest version. [PATCH server 7/8] Install render_component plugin. [PATCH server 8/8] Wrapper for mongrel to run with rails 2.3.2
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 1/8] Update core app config for Rails 2.3.2
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) { -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 2/8] test_helper and unit test updates for Rails 2.3.2
Rails 2.3.2 changes test_helper use ActiveSupport::TestCase rather than Test::Unit::TestCase, so unit test need to be updated accordingly as well. Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- src/test/test_helper.rb | 2 +- src/test/unit/active_record_env_test.rb | 2 +- src/test/unit/host_browser_awaken_test.rb | 2 +- src/test/unit/host_test.rb | 2 +- src/test/unit/nic_test.rb | 2 +- src/test/unit/permission_test.rb | 2 +- src/test/unit/pool_test.rb | 2 +- src/test/unit/quota_test.rb | 2 +- src/test/unit/storage_pool_test.rb | 2 +- src/test/unit/storage_volume_test.rb | 2 +- src/test/unit/task_test.rb | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/test_helper.rb b/src/test/test_helper.rb index fc84648..009dfc8 100644 --- a/src/test/test_helper.rb +++ b/src/test/test_helper.rb @@ -21,7 +21,7 @@ ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require 'test_help' -class Test::Unit::TestCase +class ActiveSupport::TestCase # Transactional fixtures accelerate your tests by wrapping each test method # in a transaction that's rolled back on completion. This ensures that the # test database remains unchanged so your fixtures don't have to be reloaded diff --git a/src/test/unit/active_record_env_test.rb b/src/test/unit/active_record_env_test.rb index 26fa139..a9a9c5a 100644 --- a/src/test/unit/active_record_env_test.rb +++ b/src/test/unit/active_record_env_test.rb @@ -20,7 +20,7 @@ require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../../dutils/active_record_env' -class ActiveRecordEnvTest < Test::Unit::TestCase +class ActiveRecordEnvTest < ActiveSupport::TestCase fixtures :pools, :hosts, :vms, :boot_types, :networks, :nics, :ip_addresses, :privileges, :roles, :permissions, :quotas, :storage_pools, :storage_volumes, :tasks diff --git a/src/test/unit/host_browser_awaken_test.rb b/src/test/unit/host_browser_awaken_test.rb index d251e90..a7cf31c 100644 --- a/src/test/unit/host_browser_awaken_test.rb +++ b/src/test/unit/host_browser_awaken_test.rb @@ -31,7 +31,7 @@ require 'host-browser' # +HostBrowserAwakenTest+ ensures that the host-browser daemon works correctly # during the identify phase of operation. # -class HostBrowserAwakenTest < Test::Unit::TestCase +class HostBrowserAwakenTest < ActiveSupport::TestCase def setup @session = flexmock('session') diff --git a/src/test/unit/host_test.rb b/src/test/unit/host_test.rb index 338fbaf..4d40990 100644 --- a/src/test/unit/host_test.rb +++ b/src/test/unit/host_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class HostTest < Test::Unit::TestCase +class HostTest < ActiveSupport::TestCase fixtures :hosts fixtures :pools fixtures :vms diff --git a/src/test/unit/nic_test.rb b/src/test/unit/nic_test.rb index 07f54c6..46fab10 100644 --- a/src/test/unit/nic_test.rb +++ b/src/test/unit/nic_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class NicTest < Test::Unit::TestCase +class NicTest < ActiveSupport::TestCase fixtures :ip_addresses fixtures :nics fixtures :hosts diff --git a/src/test/unit/permission_test.rb b/src/test/unit/permission_test.rb index 2ac78d5..344e615 100644 --- a/src/test/unit/permission_test.rb +++ b/src/test/unit/permission_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class PermissionTest < Test::Unit::TestCase +class PermissionTest < ActiveSupport::TestCase fixtures :privileges, :roles, :permissions fixtures :pools diff --git a/src/test/unit/pool_test.rb b/src/test/unit/pool_test.rb index c9a5554..bf9164d 100644 --- a/src/test/unit/pool_test.rb +++ b/src/test/unit/pool_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class PoolTest < Test::Unit::TestCase +class PoolTest < ActiveSupport::TestCase fixtures :pools def setup diff --git a/src/test/unit/quota_test.rb b/src/test/unit/quota_test.rb index 5903cc8..ec2f5cd 100644 --- a/src/test/unit/quota_test.rb +++ b/src/test/unit/quota_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class QuotaTest < Test::Unit::TestCase +class QuotaTest < ActiveSupport::TestCase fixtures :quotas fixtures :pools diff --git a/src/test/unit/storage_pool_test.rb b/src/test/unit/storage_pool_test.rb index 4e18a8c..bd969ad 100644 --- a/src/test/unit/storage_pool_test.rb +++ b/src/test/unit/storage_pool_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class StoragePoolTest < Test::Unit::TestCase +class StoragePoolTest < ActiveSupport::TestCase fixtures :storage_pools fixtures :pools fixtures :vms diff --git a/src/test/unit/storage_volume_test.rb b/src/test/unit/storage_volume_test.rb index f0e144e..4ec8760 100644 --- a/src/test/unit/storage_volume_test.rb +++ b/src/test/unit/storage_volume_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class StorageVolumeTest < Test::Unit::TestCase +class StorageVolumeTest < ActiveSupport::TestCase fixtures :storage_volumes fixtures :storage_pools fixtures :vms diff --git a/src/test/unit/task_test.rb b/src/test/unit/task_test.rb index 761e739..0f94052 100644 --- a/src/test/unit/task_test.rb +++ b/src/test/unit/task_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' -class TaskTest < Test::Unit::TestCase +class TaskTest < ActiveSupport::TestCase fixtures :pools, :hosts, :vms, :privileges, :roles, :permissions, :tasks def setup -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 3/8] Update functional tests to work with Rails 2.3.2
Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- .../functional/managed_node_configuration_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/test/functional/managed_node_configuration_test.rb b/src/test/functional/managed_node_configuration_test.rb index a0a66e9..8759b02 100644 --- a/src/test/functional/managed_node_configuration_test.rb +++ b/src/test/functional/managed_node_configuration_test.rb @@ -23,7 +23,7 @@ require 'managed_node_configuration' # Performs unit tests on the +ManagedNodeConfiguration+ class. # -class ManagedNodeConfigurationTest < Test::Unit::TestCase +class ManagedNodeConfigurationTest < ActiveSupport::TestCase fixtures :bonding_types fixtures :bondings fixtures :bondings_nics -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 4/8] Switch from old connection style to current.
establish_connetion has been deprecated, so switch to setup_connection. Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- src/app/helpers/ldap_connection.rb | 2 +- src/script/grant_admin_privileges | 2 +- src/vendor/plugins/active_ldap/init.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/helpers/ldap_connection.rb b/src/app/helpers/ldap_connection.rb index af8b41b..b8cb0e3 100644 --- a/src/app/helpers/ldap_connection.rb +++ b/src/app/helpers/ldap_connection.rb @@ -31,7 +31,7 @@ class LDAPConnection host = @@config[ENV['RAILS_ENV']]["host"] if host == nil port = @@config[ENV['RAILS_ENV']]["port"] if port == nil - ActiveLdap::Base.establish_connection(:host => host, + ActiveLdap::Base.setup_connection(:host => host, :port => port, :base => base) if LDAPConnection.connected? == false end diff --git a/src/script/grant_admin_privileges b/src/script/grant_admin_privileges index cdf36e7..a0e340a 100755 --- a/src/script/grant_admin_privileges +++ b/src/script/grant_admin_privileges @@ -9,7 +9,7 @@ ldap_config = YAML::load(File.open(File.dirname(__FILE__) +"/../config/ldap.yml" uid = ARGV[0] base, host = ldap_config["production"]["base"], ldap_config["production"]["host"] -ActiveLdap::Base.establish_connection :base => base, :host => host, :try_sasl => false +ActiveLdap::Base.setup_connection :base => base, :host => host, :try_sasl => false if Account.exists?("uid=#{uid}") puts "Creating an admin account for #{uid}..." diff --git a/src/vendor/plugins/active_ldap/init.rb b/src/vendor/plugins/active_ldap/init.rb index 3b4291e..e8395cf 100644 --- a/src/vendor/plugins/active_ldap/init.rb +++ b/src/vendor/plugins/active_ldap/init.rb @@ -13,7 +13,7 @@ ldap_configuration_file = File.join(RAILS_ROOT, 'config', 'ldap.yml') if File.exist?(ldap_configuration_file) configurations = YAML.load(ERB.new(IO.read(ldap_configuration_file)).result) ActiveLdap::Base.configurations = configurations - ActiveLdap::Base.establish_connection + ActiveLdap::Base.setup_connection else ActiveLdap::Base.class_eval do format = _("You should run 'script/generator scaffold_active_ldap' to make %s.") -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 5/8] No need to track schema.rb and logs right now.
Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- .gitignore | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/.gitignore b/.gitignore index 0fd1d9b..9cfdd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ missing stamp-h1 ovirt-server*.gz ovirt-server.spec +schema.rb +log/ -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 7/8] Install render_component plugin.
This is no longer in core rails (and we should look at removing it's use altogether asap). Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- src/vendor/plugins/render_component/README | 37 +++++ src/vendor/plugins/render_component/Rakefile | 22 +++ src/vendor/plugins/render_component/init.rb | 2 + .../plugins/render_component/lib/components.rb | 141 ++++++++++++++++++++ .../plugins/render_component/test/abstract_unit.rb | 8 + .../render_component/test/components_test.rb | 136 +++++++++++++++++++ 6 files changed, 346 insertions(+), 0 deletions(-) create mode 100644 src/vendor/plugins/render_component/README create mode 100644 src/vendor/plugins/render_component/Rakefile create mode 100644 src/vendor/plugins/render_component/init.rb create mode 100644 src/vendor/plugins/render_component/lib/components.rb create mode 100644 src/vendor/plugins/render_component/test/abstract_unit.rb create mode 100644 src/vendor/plugins/render_component/test/components_test.rb diff --git a/src/vendor/plugins/render_component/README b/src/vendor/plugins/render_component/README new file mode 100644 index 0000000..a6a318a --- /dev/null +++ b/src/vendor/plugins/render_component/README @@ -0,0 +1,37 @@ +Components allow you to call other actions for their rendered response while executing another action. You can either delegate +the entire response rendering or you can mix a partial response in with your other content. + + class WeblogController < ActionController::Base + # Performs a method and then lets hello_world output its render + def delegate_action + do_other_stuff_before_hello_world + render_component :controller => "greeter", :action => "hello_world", :params => { :person => "david" } + end + end + + class GreeterController < ActionController::Base + def hello_world + render :text => "#{params[:person]} says, Hello World!" + end + end + +The same can be done in a view to do a partial rendering: + + Let's see a greeting: + <%= render_component :controller => "greeter", :action => "hello_world" %> + +It is also possible to specify the controller as a class constant, bypassing the inflector +code to compute the controller class at runtime: + +<%= render_component :controller => GreeterController, :action => "hello_world" %> + +== When to use components + +Components should be used with care. They're significantly slower than simply splitting reusable parts into partials and +conceptually more complicated. Don't use components as a way of separating concerns inside a single application. Instead, +reserve components to those rare cases where you truly have reusable view and controller elements that can be employed +across many applications at once. + +So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters. + +Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license \ No newline at end of file diff --git a/src/vendor/plugins/render_component/Rakefile b/src/vendor/plugins/render_component/Rakefile new file mode 100644 index 0000000..d99407b --- /dev/null +++ b/src/vendor/plugins/render_component/Rakefile @@ -0,0 +1,22 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the components plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the components plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'Components' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end \ No newline at end of file diff --git a/src/vendor/plugins/render_component/init.rb b/src/vendor/plugins/render_component/init.rb new file mode 100644 index 0000000..b428c52 --- /dev/null +++ b/src/vendor/plugins/render_component/init.rb @@ -0,0 +1,2 @@ +require 'components' +ActionController::Base.send :include, Components diff --git a/src/vendor/plugins/render_component/lib/components.rb b/src/vendor/plugins/render_component/lib/components.rb new file mode 100644 index 0000000..9263cde --- /dev/null +++ b/src/vendor/plugins/render_component/lib/components.rb @@ -0,0 +1,141 @@ +module Components + def self.included(base) #:nodoc: + base.class_eval do + include InstanceMethods + extend ClassMethods + helper HelperMethods + + # If this controller was instantiated to process a component request, + # +parent_controller+ points to the instantiator of this controller. + attr_accessor :parent_controller + + alias_method_chain :process_cleanup, :render_component + alias_method_chain :session=, :render_component + alias_method_chain :flash, :render_component + alias_method_chain :perform_action, :render_component + alias_method_chain :send_response, :render_component + + alias_method :component_request?, :parent_controller + end + end + + module ClassMethods + # Track parent controller to identify component requests + def process_with_components(request, response, parent_controller = nil) #:nodoc: + controller = new + controller.parent_controller = parent_controller + controller.process(request, response) + end + end + + module HelperMethods + def render_component(options) + @controller.__send__(:render_component_as_string, options) + end + end + + module InstanceMethods + def perform_action_with_render_component + perform_action_without_render_component + remove_instance_variable(:@_flash) if defined? @_flash + end + + # Extracts the action_name from the request parameters and performs that action. + def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc: + flash.discard if component_request? + process_without_components(request, response, method, *arguments) + end + + def send_response_with_render_component + response.prepare! unless component_request? + response + end + + protected + # Renders the component specified as the response for the current method + def render_component(options) #:doc: + component_logging(options) do + render_for_text(component_response(options, true).body, response.status) + end + end + + # Returns the component response as a string + def render_component_as_string(options) #:doc: + component_logging(options) do + response = component_response(options, false) + + if redirected = response.redirected_to + render_component_as_string(redirected) + else + response.body + end + end + end + + def flash_with_render_component(refresh = false) #:nodoc: + if !defined?(@_flash) || refresh + @_flash + if defined?(@parent_controller) + @parent_controller.flash + else + flash_without_render_component + end + end + @_flash + end + + private + def component_response(options, reuse_response) + klass = component_class(options) + request = request_for_component(klass.controller_path, options) + new_response = reuse_response ? response : response.dup + + klass.process_with_components(request, new_response, self) + end + + # determine the controller class for the component request + def component_class(options) + if controller = options[:controller] + controller.is_a?(Class) ? controller : "#{controller.camelize}Controller".constantize + else + self.class + end + end + + # Create a new request object based on the current request. + # The new request inherits the session from the current request, + # bypassing any session options set for the component controller's class + def request_for_component(controller_path, options) + new_request = request.dup + new_request.session = request.session + + new_request.instance_variable_set( + :@parameters, + (options[:params] || {}).with_indifferent_access.update( + "controller" => controller_path, "action" => options[:action], "id" => options[:id] + ) + ) + + new_request + end + + def component_logging(options) + if logger + logger.info "Start rendering component (#{options.inspect}): " + result = yield + logger.info "\n\nEnd of component rendering" + result + else + yield + end + end + + def session_with_render_component=(options = {}) + session_without_render_component=(options) unless component_request? + end + + def process_cleanup_with_render_component + process_cleanup_without_render_component unless component_request? + end + end +end diff --git a/src/vendor/plugins/render_component/test/abstract_unit.rb b/src/vendor/plugins/render_component/test/abstract_unit.rb new file mode 100644 index 0000000..f022971 --- /dev/null +++ b/src/vendor/plugins/render_component/test/abstract_unit.rb @@ -0,0 +1,8 @@ +require 'rubygems' +require 'test/unit' +require 'action_controller' +require 'action_controller/test_process' +ActionController::Routing::Routes.reload rescue nil + +$: << File.dirname(__FILE__) + "/../lib" +require File.dirname(__FILE__) + "/../init" diff --git a/src/vendor/plugins/render_component/test/components_test.rb b/src/vendor/plugins/render_component/test/components_test.rb new file mode 100644 index 0000000..53fcd7f --- /dev/null +++ b/src/vendor/plugins/render_component/test/components_test.rb @@ -0,0 +1,136 @@ +require File.dirname(__FILE__) + '/abstract_unit' + +class CallerController < ActionController::Base + def calling_from_controller + render_component(:controller => "callee", :action => "being_called") + end + + def calling_from_controller_with_params + render_component(:controller => "callee", :action => "being_called", :params => { "name" => "David" }) + end + + def calling_from_controller_with_different_status_code + render_component(:controller => "callee", :action => "blowing_up") + end + + def calling_from_template + render :inline => "Ring, ring: <%= render_component(:controller => 'callee', :action => 'being_called') %>" + end + + def internal_caller + render :inline => "Are you there? <%= render_component(:action => 'internal_callee') %>" + end + + def internal_callee + render :text => "Yes, ma'am" + end + + def set_flash + render_component(:controller => "callee", :action => "set_flash") + end + + def use_flash + render_component(:controller => "callee", :action => "use_flash") + end + + def calling_redirected + render_component(:controller => "callee", :action => "redirected") + end + + def calling_redirected_as_string + render :inline => "<%= render_component(:controller => 'callee', :action => 'redirected') %>" + end + + def rescue_action(e) raise end +end + +class CalleeController < ActionController::Base + def being_called + render :text => "#{params[:name] || "Lady"} of the House, speaking" + end + + def blowing_up + render :text => "It's game over, man, just game over, man!", :status => 500 + end + + def set_flash + flash[:notice] = 'My stoney baby' + render :text => 'flash is set' + end + + def use_flash + render :text => flash[:notice] || 'no flash' + end + + def redirected + redirect_to :controller => "callee", :action => "being_called" + end + + def rescue_action(e) raise end +end + +class ComponentsTest < ActionController::TestCase + tests CallerController + + def test_calling_from_controller + get :calling_from_controller + assert_equal "Lady of the House, speaking", @response.body + end + + def test_calling_from_controller_with_params + get :calling_from_controller_with_params + assert_equal "David of the House, speaking", @response.body + end + + def test_calling_from_controller_with_different_status_code + get :calling_from_controller_with_different_status_code + assert_equal 500, @response.response_code + end + + def test_calling_from_template + get :calling_from_template + assert_equal "Ring, ring: Lady of the House, speaking", @response.body + end + + def test_etag_is_set_for_parent_template_when_calling_from_template + get :calling_from_template + expected_etag = etag_for("Ring, ring: Lady of the House, speaking") + assert_equal expected_etag, @response.headers['ETag'] + end + + def test_internal_calling + get :internal_caller + assert_equal "Are you there? Yes, ma'am", @response.body + end + + def test_flash + get :set_flash + assert_equal 'My stoney baby', flash[:notice] + get :use_flash + assert_equal 'My stoney baby', @response.body + get :use_flash + assert_equal 'no flash', @response.body + end + + def test_component_redirect_redirects + get :calling_redirected + + assert_redirected_to :controller=>"callee", :action => "being_called" + end + + def test_component_multiple_redirect_redirects + test_component_redirect_redirects + test_internal_calling + end + + def test_component_as_string_redirect_renders_redirected_action + get :calling_redirected_as_string + + assert_equal "Lady of the House, speaking", @response.body + end + + protected + def etag_for(text) + %("#{Digest::MD5.hexdigest(text)}") + end +end -- 1.6.2.5
Jason Guiditta
2009-Jul-20 14:58 UTC
[Ovirt-devel] [PATCH server 8/8] Wrapper for mongrel to run with rails 2.3.2
This file is needed because we use --prefix when running mongrel_rails (which I think we need for now). It wraps the AbstractRequest call that is no longer in rails (but triggered by --prefix) so mongrel does not constantly die. It is possible there is a way now in rails itself to specify something in place of this flag, but I have not been able to find where to put that setting yet. Signed-off-by: Jason Guiditta <jason.guiditta at gmail.com> --- src/config/initializers/abstract_request.rb | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) create mode 100644 src/config/initializers/abstract_request.rb diff --git a/src/config/initializers/abstract_request.rb b/src/config/initializers/abstract_request.rb new file mode 100644 index 0000000..938106d --- /dev/null +++ b/src/config/initializers/abstract_request.rb @@ -0,0 +1,14 @@ +#This is a (hopefully) temporary workaround to help +#mongrel, since it hooks into the now-gone AbstracRequest +#class. + +module ActionController + class AbstractRequest < ActionController::Request + def self.relative_url_root=(path) + ActionController::Base.relative_url_root=(path) + end + def self.relative_url_root + ActionController::Base.relative_url_root + end + end +end -- 1.6.2.5
Jason Guiditta
2009-Jul-20 15:03 UTC
[Ovirt-devel] [PATCH server 6/8] Update will_paginate to latest version.
The file git send-email doesnt like is attached -------------- next part -------------- A non-text attachment was scrubbed... Name: 0006-Update-will_paginate-to-latest-version.patch Type: text/x-patch Size: 63525 bytes Desc: not available URL: <http://listman.redhat.com/archives/ovirt-devel/attachments/20090720/f0e4d361/attachment.bin>
Jason Guiditta
2009-Jul-20 17:01 UTC
[Ovirt-devel] Upgrade server to run on Rails 2.3.2/F11
On Mon, 2009-07-20 at 10:58 -0400, Jason Guiditta wrote:> Note that one of the 8 patches (#6) will be sent separately in reply > to this email, as some of the replaced lines are too long, so git > won't let me send the email. However, there is nothing wrong with > that patch, and it should be applied in the sequence listed below. > > Note also that I assume this will be tested on a clean f11 install, rather > than an upgrade of an existing ovirt server (since if it was on F11, > it was broken anyway). > > [PATCH server 1/8] Update core app config for Rails 2.3.2 > [PATCH server 2/8] test_helper and unit test updates for Rails 2.3.2 > [PATCH server 3/8] Update functional tests to work with Rails 2.3.2 > [PATCH server 4/8] Switch from old connection style to current. > [PATCH server 5/8] No need to track schema.rb and logs right now. > [PATCH server 6/8] Update will_paginate to latest version. > [PATCH server 7/8] Install render_component plugin. > [PATCH server 8/8] Wrapper for mongrel to run with rails 2.3.2 > > _______________________________________________ > Ovirt-devel mailing list > Ovirt-devel at redhat.com > https://www.redhat.com/mailman/listinfo/ovirt-develSet is now pushed to next. Thanks to all for testing. -j