Description:
fixed incompatibilities with rail 2.1 git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@276 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r137:d08ca3e34dfe - - 7 files changed: 4074 inserted, 2107 deleted

@@ -1,112 +1,113
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 SYSTEM_MODE_CONF_KEY = 'system.mode'
3 SYSTEM_MODE_CONF_KEY = 'system.mode'
4
4
5 before_filter :authenticate, :except => [:index, :login]
5 before_filter :authenticate, :except => [:index, :login]
6 before_filter :check_viewability, :except => [:index, :login]
6 before_filter :check_viewability, :except => [:index, :login]
7
7
8 #
8 #
9 # COMMENT OUT: filter in each action instead
9 # COMMENT OUT: filter in each action instead
10 #
10 #
11 # before_filter :verify_time_limit, :only => [:submit]
11 # before_filter :verify_time_limit, :only => [:submit]
12
12
13 verify :method => :post, :only => [:submit],
13 verify :method => :post, :only => [:submit],
14 :redirect_to => { :action => :index }
14 :redirect_to => { :action => :index }
15
15
16 - caches_action :index, :login
16 + # COMMENT OUT, only need when having high load
17 + # caches_action :index, :login
17
18
18 def index
19 def index
19 redirect_to :action => 'login'
20 redirect_to :action => 'login'
20 end
21 end
21
22
22 def login
23 def login
23 saved_notice = flash[:notice]
24 saved_notice = flash[:notice]
24 reset_session
25 reset_session
25 flash[:notice] = saved_notice
26 flash[:notice] = saved_notice
26
27
27 #
28 #
28 # These are for site administrator login
29 # These are for site administrator login
29 #
30 #
30 @countries = Country.find(:all, :include => :sites)
31 @countries = Country.find(:all, :include => :sites)
31 @country_select = @countries.collect { |c| [c.name, c.id] }
32 @country_select = @countries.collect { |c| [c.name, c.id] }
32
33
33 @country_select_with_all = [['Any',0]]
34 @country_select_with_all = [['Any',0]]
34 @countries.each do |country|
35 @countries.each do |country|
35 @country_select_with_all << [country.name, country.id]
36 @country_select_with_all << [country.name, country.id]
36 end
37 end
37
38
38 @site_select = []
39 @site_select = []
39 @countries.each do |country|
40 @countries.each do |country|
40 country.sites.each do |site|
41 country.sites.each do |site|
41 @site_select << ["#{site.name}, #{country.name}", site.id]
42 @site_select << ["#{site.name}, #{country.name}", site.id]
42 end
43 end
43 end
44 end
44
45
45 render :action => 'login', :layout => 'empty'
46 render :action => 'login', :layout => 'empty'
46 end
47 end
47
48
48 def list
49 def list
49 prepare_list_information
50 prepare_list_information
50 end
51 end
51
52
52 def help
53 def help
53 @user = User.find(session[:user_id])
54 @user = User.find(session[:user_id])
54 end
55 end
55
56
56 def submit
57 def submit
57 user = User.find(session[:user_id])
58 user = User.find(session[:user_id])
58
59
59 @submission = Submission.new(params[:submission])
60 @submission = Submission.new(params[:submission])
60 @submission.user = user
61 @submission.user = user
61 @submission.language_id = 0
62 @submission.language_id = 0
62 if params['file']!=''
63 if params['file']!=''
63 @submission.source = params['file'].read
64 @submission.source = params['file'].read
64 @submission.source_filename = params['file'].original_filename
65 @submission.source_filename = params['file'].original_filename
65 end
66 end
66 @submission.submitted_at = Time.new.gmtime
67 @submission.submitted_at = Time.new.gmtime
67
68
68 if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
69 if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
69 user.site!=nil and user.site.finished?
70 user.site!=nil and user.site.finished?
70 @submission.errors.add_to_base "The contest is over."
71 @submission.errors.add_to_base "The contest is over."
71 prepare_list_information
72 prepare_list_information
72 render :action => 'list' and return
73 render :action => 'list' and return
73 end
74 end
74
75
75 if @submission.valid?
76 if @submission.valid?
76 if @submission.save == false
77 if @submission.save == false
77 flash[:notice] = 'Error saving your submission'
78 flash[:notice] = 'Error saving your submission'
78 elsif Task.create(:submission_id => @submission.id,
79 elsif Task.create(:submission_id => @submission.id,
79 :status => Task::STATUS_INQUEUE) == false
80 :status => Task::STATUS_INQUEUE) == false
80 flash[:notice] = 'Error adding your submission to task queue'
81 flash[:notice] = 'Error adding your submission to task queue'
81 end
82 end
82 else
83 else
83 prepare_list_information
84 prepare_list_information
84 render :action => 'list' and return
85 render :action => 'list' and return
85 end
86 end
86 redirect_to :action => 'list'
87 redirect_to :action => 'list'
87 end
88 end
88
89
89 def source
90 def source
90 submission = Submission.find(params[:id])
91 submission = Submission.find(params[:id])
91 if submission.user_id == session[:user_id]
92 if submission.user_id == session[:user_id]
92 if submission.problem.output_only
93 if submission.problem.output_only
93 fname = submission.source_filename
94 fname = submission.source_filename
94 else
95 else
95 fname = submission.problem.name + '.' + submission.language.ext
96 fname = submission.problem.name + '.' + submission.language.ext
96 end
97 end
97 send_data(submission.source,
98 send_data(submission.source,
98 {:filename => fname,
99 {:filename => fname,
99 :type => 'text/plain'})
100 :type => 'text/plain'})
100 else
101 else
101 flash[:notice] = 'Error viewing source'
102 flash[:notice] = 'Error viewing source'
102 redirect_to :action => 'list'
103 redirect_to :action => 'list'
103 end
104 end
104 end
105 end
105
106
106 def compiler_msg
107 def compiler_msg
107 @submission = Submission.find(params[:id])
108 @submission = Submission.find(params[:id])
108 if @submission.user_id == session[:user_id]
109 if @submission.user_id == session[:user_id]
109 render :action => 'compiler_msg', :layout => 'empty'
110 render :action => 'compiler_msg', :layout => 'empty'
110 else
111 else
111 flash[:notice] = 'Error viewing source'
112 flash[:notice] = 'Error viewing source'
112 redirect_to :action => 'list'
113 redirect_to :action => 'list'
@@ -1,18 +1,18
1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
2 <td>
2 <td>
3 - <%= "#{problem_counter + 1}" %>
3 + <%= "#{problem_counter}" %>
4 </td>
4 </td>
5 <td>
5 <td>
6 <%= "#{problem.full_name} (#{problem.name})" %>
6 <%= "#{problem.full_name} (#{problem.name})" %>
7 <%= link_to '[desc]', problem.url, :popup => true if (problem.url!=nil) and (problem.url!='') %>
7 <%= link_to '[desc]', problem.url, :popup => true if (problem.url!=nil) and (problem.url!='') %>
8 </td>
8 </td>
9 <td align="center">
9 <td align="center">
10 - <%= @prob_submissions[problem_counter][:count] %>
10 + <%= @prob_submissions[problem_counter-1][:count] %>
11 </td>
11 </td>
12 <td>
12 <td>
13 <%= render :partial => 'submission_short',
13 <%= render :partial => 'submission_short',
14 :locals => {
14 :locals => {
15 - :submission => @prob_submissions[problem_counter][:submission],
15 + :submission => @prob_submissions[problem_counter-1][:submission],
16 :problem_name => problem.name }%>
16 :problem_name => problem.name }%>
17 </td>
17 </td>
18 </tr>
18 </tr>
@@ -1,45 +1,109
1 - # Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
1 + # Don't change this file!
2 + # Configure your app in config/environment.rb and config/environments/*.rb
3 +
4 + RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
2
5
3 - unless defined?(RAILS_ROOT)
6 + module Rails
4 - root_path = File.join(File.dirname(__FILE__), '..')
7 + class << self
5 -
8 + def boot!
6 - unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
9 + unless booted?
7 - require 'pathname'
10 + preinitialize
8 - root_path = Pathname.new(root_path).cleanpath(true).to_s
11 + pick_boot.run
12 + end
9 end
13 end
10
14
11 - RAILS_ROOT = root_path
15 + def booted?
16 + defined? Rails::Initializer
17 + end
18 +
19 + def pick_boot
20 + (vendor_rails? ? VendorBoot : GemBoot).new
12 end
21 end
13
22
14 - unless defined?(Rails::Initializer)
23 + def vendor_rails?
15 - if File.directory?("#{RAILS_ROOT}/vendor/rails")
24 + File.exist?("#{RAILS_ROOT}/vendor/rails")
16 - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
25 + end
17 - else
18 - require 'rubygems'
19
26
20 - environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join
27 + def preinitialize
21 - environment_without_comments =~ /[^#]RAILS_GEM_VERSION = '([\d.]+)'/
28 + load(preinitializer_path) if File.exist?(preinitializer_path)
22 - rails_gem_version = $1
29 + end
23 -
24 - if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version
25 - # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems
26 - rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last
27
30
28 - if rails_gem
31 + def preinitializer_path
29 - gem "rails", "=#{rails_gem.version.version}"
32 + "#{RAILS_ROOT}/config/preinitializer.rb"
30 - require rails_gem.full_gem_path + '/lib/initializer'
33 + end
31 - else
32 - STDERR.puts %(Cannot find gem for Rails ~>#{version}.0:
33 - Install the missing gem with 'gem install -v=#{version} rails', or
34 - change environment.rb to define RAILS_GEM_VERSION with your desired version.
35 - )
36 - exit 1
37 end
34 end
38 - else
35 +
39 - gem "rails"
36 + class Boot
40 - require 'initializer'
37 + def run
38 + load_initializer
39 + Rails::Initializer.run(:set_load_path)
40 + end
41 + end
42 +
43 + class VendorBoot < Boot
44 + def load_initializer
45 + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
46 + Rails::Initializer.run(:install_gem_spec_stubs)
41 end
47 end
42 end
48 end
43
49
44 - Rails::Initializer.run(:set_load_path)
50 + class GemBoot < Boot
51 + def load_initializer
52 + self.class.load_rubygems
53 + load_rails_gem
54 + require 'initializer'
55 + end
56 +
57 + def load_rails_gem
58 + if version = self.class.gem_version
59 + gem 'rails', version
60 + else
61 + gem 'rails'
62 + end
63 + rescue Gem::LoadError => load_error
64 + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
65 + exit 1
66 + end
67 +
68 + class << self
69 + def rubygems_version
70 + Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
45 end
71 end
72 +
73 + def gem_version
74 + if defined? RAILS_GEM_VERSION
75 + RAILS_GEM_VERSION
76 + elsif ENV.include?('RAILS_GEM_VERSION')
77 + ENV['RAILS_GEM_VERSION']
78 + else
79 + parse_gem_version(read_environment_rb)
80 + end
81 + end
82 +
83 + def load_rubygems
84 + require 'rubygems'
85 +
86 + unless rubygems_version >= '0.9.4'
87 + $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
88 + exit 1
89 + end
90 +
91 + rescue LoadError
92 + $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
93 + exit 1
94 + end
95 +
96 + def parse_gem_version(text)
97 + $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
98 + end
99 +
100 + private
101 + def read_environment_rb
102 + File.read("#{RAILS_ROOT}/config/environment.rb")
103 + end
104 + end
105 + end
106 + end
107 +
108 + # All that for this:
109 + Rails.boot!
This diff has been collapsed as it changes many lines, (834 lines changed) Show them Hide them
@@ -1,833 +1,963
1 - // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 - // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
2 + // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 - // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
3 + // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
4 // Contributors:
4 // Contributors:
5 // Richard Livsey
5 // Richard Livsey
6 // Rahul Bhargava
6 // Rahul Bhargava
7 // Rob Wills
7 // Rob Wills
8 //
8 //
9 // script.aculo.us is freely distributable under the terms of an MIT-style license.
9 // script.aculo.us is freely distributable under the terms of an MIT-style license.
10 // For details, see the script.aculo.us web site: http://script.aculo.us/
10 // For details, see the script.aculo.us web site: http://script.aculo.us/
11
11
12 // Autocompleter.Base handles all the autocompletion functionality
12 // Autocompleter.Base handles all the autocompletion functionality
13 // that's independent of the data source for autocompletion. This
13 // that's independent of the data source for autocompletion. This
14 // includes drawing the autocompletion menu, observing keyboard
14 // includes drawing the autocompletion menu, observing keyboard
15 // and mouse events, and similar.
15 // and mouse events, and similar.
16 //
16 //
17 // Specific autocompleters need to provide, at the very least,
17 // Specific autocompleters need to provide, at the very least,
18 // a getUpdatedChoices function that will be invoked every time
18 // a getUpdatedChoices function that will be invoked every time
19 // the text inside the monitored textbox changes. This method
19 // the text inside the monitored textbox changes. This method
20 // should get the text for which to provide autocompletion by
20 // should get the text for which to provide autocompletion by
21 // invoking this.getToken(), NOT by directly accessing
21 // invoking this.getToken(), NOT by directly accessing
22 // this.element.value. This is to allow incremental tokenized
22 // this.element.value. This is to allow incremental tokenized
23 // autocompletion. Specific auto-completion logic (AJAX, etc)
23 // autocompletion. Specific auto-completion logic (AJAX, etc)
24 // belongs in getUpdatedChoices.
24 // belongs in getUpdatedChoices.
25 //
25 //
26 // Tokenized incremental autocompletion is enabled automatically
26 // Tokenized incremental autocompletion is enabled automatically
27 // when an autocompleter is instantiated with the 'tokens' option
27 // when an autocompleter is instantiated with the 'tokens' option
28 // in the options parameter, e.g.:
28 // in the options parameter, e.g.:
29 // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
29 // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
30 // will incrementally autocomplete with a comma as the token.
30 // will incrementally autocomplete with a comma as the token.
31 // Additionally, ',' in the above example can be replaced with
31 // Additionally, ',' in the above example can be replaced with
32 // a token array, e.g. { tokens: [',', '\n'] } which
32 // a token array, e.g. { tokens: [',', '\n'] } which
33 // enables autocompletion on multiple tokens. This is most
33 // enables autocompletion on multiple tokens. This is most
34 // useful when one of the tokens is \n (a newline), as it
34 // useful when one of the tokens is \n (a newline), as it
35 // allows smart autocompletion after linebreaks.
35 // allows smart autocompletion after linebreaks.
36
36
37 if(typeof Effect == 'undefined')
37 if(typeof Effect == 'undefined')
38 throw("controls.js requires including script.aculo.us' effects.js library");
38 throw("controls.js requires including script.aculo.us' effects.js library");
39
39
40 var Autocompleter = {}
40 var Autocompleter = { }
41 - Autocompleter.Base = function() {};
41 + Autocompleter.Base = Class.create({
42 - Autocompleter.Base.prototype = {
43 baseInitialize: function(element, update, options) {
42 baseInitialize: function(element, update, options) {
44 - this.element = $(element);
43 + element = $(element)
44 + this.element = element;
45 this.update = $(update);
45 this.update = $(update);
46 this.hasFocus = false;
46 this.hasFocus = false;
47 this.changed = false;
47 this.changed = false;
48 this.active = false;
48 this.active = false;
49 this.index = 0;
49 this.index = 0;
50 this.entryCount = 0;
50 this.entryCount = 0;
51 + this.oldElementValue = this.element.value;
51
52
52 if(this.setOptions)
53 if(this.setOptions)
53 this.setOptions(options);
54 this.setOptions(options);
54 else
55 else
55 this.options = options || {};
56 this.options = options || { };
56
57
57 this.options.paramName = this.options.paramName || this.element.name;
58 this.options.paramName = this.options.paramName || this.element.name;
58 this.options.tokens = this.options.tokens || [];
59 this.options.tokens = this.options.tokens || [];
59 this.options.frequency = this.options.frequency || 0.4;
60 this.options.frequency = this.options.frequency || 0.4;
60 this.options.minChars = this.options.minChars || 1;
61 this.options.minChars = this.options.minChars || 1;
61 this.options.onShow = this.options.onShow ||
62 this.options.onShow = this.options.onShow ||
62 function(element, update){
63 function(element, update){
63 if(!update.style.position || update.style.position=='absolute') {
64 if(!update.style.position || update.style.position=='absolute') {
64 update.style.position = 'absolute';
65 update.style.position = 'absolute';
65 Position.clone(element, update, {
66 Position.clone(element, update, {
66 setHeight: false,
67 setHeight: false,
67 offsetTop: element.offsetHeight
68 offsetTop: element.offsetHeight
68 });
69 });
69 }
70 }
70 Effect.Appear(update,{duration:0.15});
71 Effect.Appear(update,{duration:0.15});
71 };
72 };
72 this.options.onHide = this.options.onHide ||
73 this.options.onHide = this.options.onHide ||
73 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
74 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
74
75
75 if(typeof(this.options.tokens) == 'string')
76 if(typeof(this.options.tokens) == 'string')
76 this.options.tokens = new Array(this.options.tokens);
77 this.options.tokens = new Array(this.options.tokens);
78 + // Force carriage returns as token delimiters anyway
79 + if (!this.options.tokens.include('\n'))
80 + this.options.tokens.push('\n');
77
81
78 this.observer = null;
82 this.observer = null;
79
83
80 this.element.setAttribute('autocomplete','off');
84 this.element.setAttribute('autocomplete','off');
81
85
82 Element.hide(this.update);
86 Element.hide(this.update);
83
87
84 - Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
88 + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
85 - Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
89 + Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86 },
90 },
87
91
88 show: function() {
92 show: function() {
89 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
93 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90 if(!this.iefix &&
94 if(!this.iefix &&
91 - (navigator.appVersion.indexOf('MSIE')>0) &&
95 + (Prototype.Browser.IE) &&
92 - (navigator.userAgent.indexOf('Opera')<0) &&
93 (Element.getStyle(this.update, 'position')=='absolute')) {
96 (Element.getStyle(this.update, 'position')=='absolute')) {
94 new Insertion.After(this.update,
97 new Insertion.After(this.update,
95 '<iframe id="' + this.update.id + '_iefix" '+
98 '<iframe id="' + this.update.id + '_iefix" '+
96 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
99 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
97 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
100 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
98 this.iefix = $(this.update.id+'_iefix');
101 this.iefix = $(this.update.id+'_iefix');
99 }
102 }
100 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
103 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
101 },
104 },
102
105
103 fixIEOverlapping: function() {
106 fixIEOverlapping: function() {
104 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
107 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
105 this.iefix.style.zIndex = 1;
108 this.iefix.style.zIndex = 1;
106 this.update.style.zIndex = 2;
109 this.update.style.zIndex = 2;
107 Element.show(this.iefix);
110 Element.show(this.iefix);
108 },
111 },
109
112
110 hide: function() {
113 hide: function() {
111 this.stopIndicator();
114 this.stopIndicator();
112 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
115 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
113 if(this.iefix) Element.hide(this.iefix);
116 if(this.iefix) Element.hide(this.iefix);
114 },
117 },
115
118
116 startIndicator: function() {
119 startIndicator: function() {
117 if(this.options.indicator) Element.show(this.options.indicator);
120 if(this.options.indicator) Element.show(this.options.indicator);
118 },
121 },
119
122
120 stopIndicator: function() {
123 stopIndicator: function() {
121 if(this.options.indicator) Element.hide(this.options.indicator);
124 if(this.options.indicator) Element.hide(this.options.indicator);
122 },
125 },
123
126
124 onKeyPress: function(event) {
127 onKeyPress: function(event) {
125 if(this.active)
128 if(this.active)
126 switch(event.keyCode) {
129 switch(event.keyCode) {
127 case Event.KEY_TAB:
130 case Event.KEY_TAB:
128 case Event.KEY_RETURN:
131 case Event.KEY_RETURN:
129 this.selectEntry();
132 this.selectEntry();
130 Event.stop(event);
133 Event.stop(event);
131 case Event.KEY_ESC:
134 case Event.KEY_ESC:
132 this.hide();
135 this.hide();
133 this.active = false;
136 this.active = false;
134 Event.stop(event);
137 Event.stop(event);
135 return;
138 return;
136 case Event.KEY_LEFT:
139 case Event.KEY_LEFT:
137 case Event.KEY_RIGHT:
140 case Event.KEY_RIGHT:
138 return;
141 return;
139 case Event.KEY_UP:
142 case Event.KEY_UP:
140 this.markPrevious();
143 this.markPrevious();
141 this.render();
144 this.render();
142 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145 + Event.stop(event);
143 return;
146 return;
144 case Event.KEY_DOWN:
147 case Event.KEY_DOWN:
145 this.markNext();
148 this.markNext();
146 this.render();
149 this.render();
147 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150 + Event.stop(event);
148 return;
151 return;
149 }
152 }
150 else
153 else
151 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
154 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152 - (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
155 + (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
153
156
154 this.changed = true;
157 this.changed = true;
155 this.hasFocus = true;
158 this.hasFocus = true;
156
159
157 if(this.observer) clearTimeout(this.observer);
160 if(this.observer) clearTimeout(this.observer);
158 this.observer =
161 this.observer =
159 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
162 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
160 },
163 },
161
164
162 activate: function() {
165 activate: function() {
163 this.changed = false;
166 this.changed = false;
164 this.hasFocus = true;
167 this.hasFocus = true;
165 this.getUpdatedChoices();
168 this.getUpdatedChoices();
166 },
169 },
167
170
168 onHover: function(event) {
171 onHover: function(event) {
169 var element = Event.findElement(event, 'LI');
172 var element = Event.findElement(event, 'LI');
170 if(this.index != element.autocompleteIndex)
173 if(this.index != element.autocompleteIndex)
171 {
174 {
172 this.index = element.autocompleteIndex;
175 this.index = element.autocompleteIndex;
173 this.render();
176 this.render();
174 }
177 }
175 Event.stop(event);
178 Event.stop(event);
176 },
179 },
177
180
178 onClick: function(event) {
181 onClick: function(event) {
179 var element = Event.findElement(event, 'LI');
182 var element = Event.findElement(event, 'LI');
180 this.index = element.autocompleteIndex;
183 this.index = element.autocompleteIndex;
181 this.selectEntry();
184 this.selectEntry();
182 this.hide();
185 this.hide();
183 },
186 },
184
187
185 onBlur: function(event) {
188 onBlur: function(event) {
186 // needed to make click events working
189 // needed to make click events working
187 setTimeout(this.hide.bind(this), 250);
190 setTimeout(this.hide.bind(this), 250);
188 this.hasFocus = false;
191 this.hasFocus = false;
189 this.active = false;
192 this.active = false;
190 },
193 },
191
194
192 render: function() {
195 render: function() {
193 if(this.entryCount > 0) {
196 if(this.entryCount > 0) {
194 for (var i = 0; i < this.entryCount; i++)
197 for (var i = 0; i < this.entryCount; i++)
195 this.index==i ?
198 this.index==i ?
196 Element.addClassName(this.getEntry(i),"selected") :
199 Element.addClassName(this.getEntry(i),"selected") :
197 Element.removeClassName(this.getEntry(i),"selected");
200 Element.removeClassName(this.getEntry(i),"selected");
198 -
199 if(this.hasFocus) {
201 if(this.hasFocus) {
200 this.show();
202 this.show();
201 this.active = true;
203 this.active = true;
202 }
204 }
203 } else {
205 } else {
204 this.active = false;
206 this.active = false;
205 this.hide();
207 this.hide();
206 }
208 }
207 },
209 },
208
210
209 markPrevious: function() {
211 markPrevious: function() {
210 if(this.index > 0) this.index--
212 if(this.index > 0) this.index--
211 else this.index = this.entryCount-1;
213 else this.index = this.entryCount-1;
212 this.getEntry(this.index).scrollIntoView(true);
214 this.getEntry(this.index).scrollIntoView(true);
213 },
215 },
214
216
215 markNext: function() {
217 markNext: function() {
216 if(this.index < this.entryCount-1) this.index++
218 if(this.index < this.entryCount-1) this.index++
217 else this.index = 0;
219 else this.index = 0;
218 this.getEntry(this.index).scrollIntoView(false);
220 this.getEntry(this.index).scrollIntoView(false);
219 },
221 },
220
222
221 getEntry: function(index) {
223 getEntry: function(index) {
222 return this.update.firstChild.childNodes[index];
224 return this.update.firstChild.childNodes[index];
223 },
225 },
224
226
225 getCurrentEntry: function() {
227 getCurrentEntry: function() {
226 return this.getEntry(this.index);
228 return this.getEntry(this.index);
227 },
229 },
228
230
229 selectEntry: function() {
231 selectEntry: function() {
230 this.active = false;
232 this.active = false;
231 this.updateElement(this.getCurrentEntry());
233 this.updateElement(this.getCurrentEntry());
232 },
234 },
233
235
234 updateElement: function(selectedElement) {
236 updateElement: function(selectedElement) {
235 if (this.options.updateElement) {
237 if (this.options.updateElement) {
236 this.options.updateElement(selectedElement);
238 this.options.updateElement(selectedElement);
237 return;
239 return;
238 }
240 }
239 var value = '';
241 var value = '';
240 if (this.options.select) {
242 if (this.options.select) {
241 - var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243 + var nodes = $(selectedElement).select('.' + this.options.select) || [];
242 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
244 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243 } else
245 } else
244 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
246 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
247
246 - var lastTokenPos = this.findLastToken();
248 + var bounds = this.getTokenBounds();
247 - if (lastTokenPos != -1) {
249 + if (bounds[0] != -1) {
248 - var newValue = this.element.value.substr(0, lastTokenPos + 1);
250 + var newValue = this.element.value.substr(0, bounds[0]);
249 - var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
251 + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250 if (whitespace)
252 if (whitespace)
251 newValue += whitespace[0];
253 newValue += whitespace[0];
252 - this.element.value = newValue + value;
254 + this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253 } else {
255 } else {
254 this.element.value = value;
256 this.element.value = value;
255 }
257 }
258 + this.oldElementValue = this.element.value;
256 this.element.focus();
259 this.element.focus();
257
260
258 if (this.options.afterUpdateElement)
261 if (this.options.afterUpdateElement)
259 this.options.afterUpdateElement(this.element, selectedElement);
262 this.options.afterUpdateElement(this.element, selectedElement);
260 },
263 },
261
264
262 updateChoices: function(choices) {
265 updateChoices: function(choices) {
263 if(!this.changed && this.hasFocus) {
266 if(!this.changed && this.hasFocus) {
264 this.update.innerHTML = choices;
267 this.update.innerHTML = choices;
265 Element.cleanWhitespace(this.update);
268 Element.cleanWhitespace(this.update);
266 Element.cleanWhitespace(this.update.down());
269 Element.cleanWhitespace(this.update.down());
267
270
268 if(this.update.firstChild && this.update.down().childNodes) {
271 if(this.update.firstChild && this.update.down().childNodes) {
269 this.entryCount =
272 this.entryCount =
270 this.update.down().childNodes.length;
273 this.update.down().childNodes.length;
271 for (var i = 0; i < this.entryCount; i++) {
274 for (var i = 0; i < this.entryCount; i++) {
272 var entry = this.getEntry(i);
275 var entry = this.getEntry(i);
273 entry.autocompleteIndex = i;
276 entry.autocompleteIndex = i;
274 this.addObservers(entry);
277 this.addObservers(entry);
275 }
278 }
276 } else {
279 } else {
277 this.entryCount = 0;
280 this.entryCount = 0;
278 }
281 }
279
282
280 this.stopIndicator();
283 this.stopIndicator();
281 this.index = 0;
284 this.index = 0;
282
285
283 if(this.entryCount==1 && this.options.autoSelect) {
286 if(this.entryCount==1 && this.options.autoSelect) {
284 this.selectEntry();
287 this.selectEntry();
285 this.hide();
288 this.hide();
286 } else {
289 } else {
287 this.render();
290 this.render();
288 }
291 }
289 }
292 }
290 },
293 },
291
294
292 addObservers: function(element) {
295 addObservers: function(element) {
293 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
296 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
294 Event.observe(element, "click", this.onClick.bindAsEventListener(this));
297 Event.observe(element, "click", this.onClick.bindAsEventListener(this));
295 },
298 },
296
299
297 onObserverEvent: function() {
300 onObserverEvent: function() {
298 this.changed = false;
301 this.changed = false;
302 + this.tokenBounds = null;
299 if(this.getToken().length>=this.options.minChars) {
303 if(this.getToken().length>=this.options.minChars) {
300 - this.startIndicator();
301 this.getUpdatedChoices();
304 this.getUpdatedChoices();
302 } else {
305 } else {
303 this.active = false;
306 this.active = false;
304 this.hide();
307 this.hide();
305 }
308 }
309 + this.oldElementValue = this.element.value;
306 },
310 },
307
311
308 getToken: function() {
312 getToken: function() {
309 - var tokenPos = this.findLastToken();
313 + var bounds = this.getTokenBounds();
310 - if (tokenPos != -1)
314 + return this.element.value.substring(bounds[0], bounds[1]).strip();
311 - var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
312 - else
313 - var ret = this.element.value;
314 -
315 - return /\n/.test(ret) ? '' : ret;
316 },
315 },
317
316
318 - findLastToken: function() {
317 + getTokenBounds: function() {
319 - var lastTokenPos = -1;
318 + if (null != this.tokenBounds) return this.tokenBounds;
319 + var value = this.element.value;
320 + if (value.strip().empty()) return [-1, 0];
321 + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
322 + var offset = (diff == this.oldElementValue.length ? 1 : 0);
323 + var prevTokenPos = -1, nextTokenPos = value.length;
324 + var tp;
325 + for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
326 + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
327 + if (tp > prevTokenPos) prevTokenPos = tp;
328 + tp = value.indexOf(this.options.tokens[index], diff + offset);
329 + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
330 + }
331 + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
332 + }
333 + });
320
334
321 - for (var i=0; i<this.options.tokens.length; i++) {
335 + Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
322 - var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
336 + var boundary = Math.min(newS.length, oldS.length);
323 - if (thisTokenPos > lastTokenPos)
337 + for (var index = 0; index < boundary; ++index)
324 - lastTokenPos = thisTokenPos;
338 + if (newS[index] != oldS[index])
325 - }
339 + return index;
326 - return lastTokenPos;
340 + return boundary;
327 - }
341 + };
328 - }
329
342
330 - Ajax.Autocompleter = Class.create();
343 + Ajax.Autocompleter = Class.create(Autocompleter.Base, {
331 - Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
332 initialize: function(element, update, url, options) {
344 initialize: function(element, update, url, options) {
333 this.baseInitialize(element, update, options);
345 this.baseInitialize(element, update, options);
334 this.options.asynchronous = true;
346 this.options.asynchronous = true;
335 this.options.onComplete = this.onComplete.bind(this);
347 this.options.onComplete = this.onComplete.bind(this);
336 this.options.defaultParams = this.options.parameters || null;
348 this.options.defaultParams = this.options.parameters || null;
337 this.url = url;
349 this.url = url;
338 },
350 },
339
351
340 getUpdatedChoices: function() {
352 getUpdatedChoices: function() {
341 - entry = encodeURIComponent(this.options.paramName) + '=' +
353 + this.startIndicator();
354 +
355 + var entry = encodeURIComponent(this.options.paramName) + '=' +
342 encodeURIComponent(this.getToken());
356 encodeURIComponent(this.getToken());
343
357
344 this.options.parameters = this.options.callback ?
358 this.options.parameters = this.options.callback ?
345 this.options.callback(this.element, entry) : entry;
359 this.options.callback(this.element, entry) : entry;
346
360
347 if(this.options.defaultParams)
361 if(this.options.defaultParams)
348 this.options.parameters += '&' + this.options.defaultParams;
362 this.options.parameters += '&' + this.options.defaultParams;
349
363
350 new Ajax.Request(this.url, this.options);
364 new Ajax.Request(this.url, this.options);
351 },
365 },
352
366
353 onComplete: function(request) {
367 onComplete: function(request) {
354 this.updateChoices(request.responseText);
368 this.updateChoices(request.responseText);
355 }
369 }
356 -
357 });
370 });
358
371
359 // The local array autocompleter. Used when you'd prefer to
372 // The local array autocompleter. Used when you'd prefer to
360 // inject an array of autocompletion options into the page, rather
373 // inject an array of autocompletion options into the page, rather
361 // than sending out Ajax queries, which can be quite slow sometimes.
374 // than sending out Ajax queries, which can be quite slow sometimes.
362 //
375 //
363 // The constructor takes four parameters. The first two are, as usual,
376 // The constructor takes four parameters. The first two are, as usual,
364 // the id of the monitored textbox, and id of the autocompletion menu.
377 // the id of the monitored textbox, and id of the autocompletion menu.
365 // The third is the array you want to autocomplete from, and the fourth
378 // The third is the array you want to autocomplete from, and the fourth
366 // is the options block.
379 // is the options block.
367 //
380 //
368 // Extra local autocompletion options:
381 // Extra local autocompletion options:
369 // - choices - How many autocompletion choices to offer
382 // - choices - How many autocompletion choices to offer
370 //
383 //
371 // - partialSearch - If false, the autocompleter will match entered
384 // - partialSearch - If false, the autocompleter will match entered
372 // text only at the beginning of strings in the
385 // text only at the beginning of strings in the
373 // autocomplete array. Defaults to true, which will
386 // autocomplete array. Defaults to true, which will
374 // match text at the beginning of any *word* in the
387 // match text at the beginning of any *word* in the
375 // strings in the autocomplete array. If you want to
388 // strings in the autocomplete array. If you want to
376 // search anywhere in the string, additionally set
389 // search anywhere in the string, additionally set
377 // the option fullSearch to true (default: off).
390 // the option fullSearch to true (default: off).
378 //
391 //
379 // - fullSsearch - Search anywhere in autocomplete array strings.
392 // - fullSsearch - Search anywhere in autocomplete array strings.
380 //
393 //
381 // - partialChars - How many characters to enter before triggering
394 // - partialChars - How many characters to enter before triggering
382 // a partial match (unlike minChars, which defines
395 // a partial match (unlike minChars, which defines
383 // how many characters are required to do any match
396 // how many characters are required to do any match
384 // at all). Defaults to 2.
397 // at all). Defaults to 2.
385 //
398 //
386 // - ignoreCase - Whether to ignore case when autocompleting.
399 // - ignoreCase - Whether to ignore case when autocompleting.
387 // Defaults to true.
400 // Defaults to true.
388 //
401 //
389 // It's possible to pass in a custom function as the 'selector'
402 // It's possible to pass in a custom function as the 'selector'
390 // option, if you prefer to write your own autocompletion logic.
403 // option, if you prefer to write your own autocompletion logic.
391 // In that case, the other options above will not apply unless
404 // In that case, the other options above will not apply unless
392 // you support them.
405 // you support them.
393
406
394 - Autocompleter.Local = Class.create();
407 + Autocompleter.Local = Class.create(Autocompleter.Base, {
395 - Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
396 initialize: function(element, update, array, options) {
408 initialize: function(element, update, array, options) {
397 this.baseInitialize(element, update, options);
409 this.baseInitialize(element, update, options);
398 this.options.array = array;
410 this.options.array = array;
399 },
411 },
400
412
401 getUpdatedChoices: function() {
413 getUpdatedChoices: function() {
402 this.updateChoices(this.options.selector(this));
414 this.updateChoices(this.options.selector(this));
403 },
415 },
404
416
405 setOptions: function(options) {
417 setOptions: function(options) {
406 this.options = Object.extend({
418 this.options = Object.extend({
407 choices: 10,
419 choices: 10,
408 partialSearch: true,
420 partialSearch: true,
409 partialChars: 2,
421 partialChars: 2,
410 ignoreCase: true,
422 ignoreCase: true,
411 fullSearch: false,
423 fullSearch: false,
412 selector: function(instance) {
424 selector: function(instance) {
413 var ret = []; // Beginning matches
425 var ret = []; // Beginning matches
414 var partial = []; // Inside matches
426 var partial = []; // Inside matches
415 var entry = instance.getToken();
427 var entry = instance.getToken();
416 var count = 0;
428 var count = 0;
417
429
418 for (var i = 0; i < instance.options.array.length &&
430 for (var i = 0; i < instance.options.array.length &&
419 ret.length < instance.options.choices ; i++) {
431 ret.length < instance.options.choices ; i++) {
420
432
421 var elem = instance.options.array[i];
433 var elem = instance.options.array[i];
422 var foundPos = instance.options.ignoreCase ?
434 var foundPos = instance.options.ignoreCase ?
423 elem.toLowerCase().indexOf(entry.toLowerCase()) :
435 elem.toLowerCase().indexOf(entry.toLowerCase()) :
424 elem.indexOf(entry);
436 elem.indexOf(entry);
425
437
426 while (foundPos != -1) {
438 while (foundPos != -1) {
427 if (foundPos == 0 && elem.length != entry.length) {
439 if (foundPos == 0 && elem.length != entry.length) {
428 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
440 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
429 elem.substr(entry.length) + "</li>");
441 elem.substr(entry.length) + "</li>");
430 break;
442 break;
431 } else if (entry.length >= instance.options.partialChars &&
443 } else if (entry.length >= instance.options.partialChars &&
432 instance.options.partialSearch && foundPos != -1) {
444 instance.options.partialSearch && foundPos != -1) {
433 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
445 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
434 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
446 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
435 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
447 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
436 foundPos + entry.length) + "</li>");
448 foundPos + entry.length) + "</li>");
437 break;
449 break;
438 }
450 }
439 }
451 }
440
452
441 foundPos = instance.options.ignoreCase ?
453 foundPos = instance.options.ignoreCase ?
442 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
454 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
443 elem.indexOf(entry, foundPos + 1);
455 elem.indexOf(entry, foundPos + 1);
444
456
445 }
457 }
446 }
458 }
447 if (partial.length)
459 if (partial.length)
448 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
460 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
449 return "<ul>" + ret.join('') + "</ul>";
461 return "<ul>" + ret.join('') + "</ul>";
450 }
462 }
451 }, options || {});
463 }, options || { });
452 }
464 }
453 });
465 });
454
466
455 - // AJAX in-place editor
467 + // AJAX in-place editor and collection editor
456 - //
468 + // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
457 - // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
458
469
459 // Use this if you notice weird scrolling problems on some browsers,
470 // Use this if you notice weird scrolling problems on some browsers,
460 // the DOM might be a bit confused when this gets called so do this
471 // the DOM might be a bit confused when this gets called so do this
461 // waits 1 ms (with setTimeout) until it does the activation
472 // waits 1 ms (with setTimeout) until it does the activation
462 Field.scrollFreeActivate = function(field) {
473 Field.scrollFreeActivate = function(field) {
463 setTimeout(function() {
474 setTimeout(function() {
464 Field.activate(field);
475 Field.activate(field);
465 }, 1);
476 }, 1);
466 }
477 }
467
478
468 - Ajax.InPlaceEditor = Class.create();
479 + Ajax.InPlaceEditor = Class.create({
469 - Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470 - Ajax.InPlaceEditor.prototype = {
471 initialize: function(element, url, options) {
480 initialize: function(element, url, options) {
472 this.url = url;
481 this.url = url;
473 - this.element = $(element);
482 + this.element = element = $(element);
474 -
483 + this.prepareOptions();
475 - this.options = Object.extend({
484 + this._controls = { };
476 - paramName: "value",
485 + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
477 - okButton: true,
486 + Object.extend(this.options, options || { });
478 - okText: "ok",
487 + if (!this.options.formId && this.element.id) {
479 - cancelLink: true,
488 + this.options.formId = this.element.id + '-inplaceeditor';
480 - cancelText: "cancel",
489 + if ($(this.options.formId))
481 - savingText: "Saving...",
490 + this.options.formId = '';
482 - clickToEditText: "Click to edit",
491 + }
483 - okText: "ok",
492 + if (this.options.externalControl)
484 - rows: 1,
493 + this.options.externalControl = $(this.options.externalControl);
485 - onComplete: function(transport, element) {
494 + if (!this.options.externalControl)
486 - new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
495 + this.options.externalControlOnly = false;
487 - },
496 + this._originalBackground = this.element.getStyle('background-color') || 'transparent';
488 - onFailure: function(transport) {
497 + this.element.title = this.options.clickToEditText;
489 - alert("Error communicating with the server: " + transport.responseText.stripTags());
498 + this._boundCancelHandler = this.handleFormCancellation.bind(this);
490 - },
499 + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
491 - callback: function(form) {
500 + this._boundFailureHandler = this.handleAJAXFailure.bind(this);
492 - return Form.serialize(form);
501 + this._boundSubmitHandler = this.handleFormSubmission.bind(this);
502 + this._boundWrapperHandler = this.wrapUp.bind(this);
503 + this.registerListeners();
493 },
504 },
494 - handleLineBreaks: true,
505 + checkForEscapeOrReturn: function(e) {
495 - loadingText: 'Loading...',
506 + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
496 - savingClassName: 'inplaceeditor-saving',
507 + if (Event.KEY_ESC == e.keyCode)
497 - loadingClassName: 'inplaceeditor-loading',
508 + this.handleFormCancellation(e);
498 - formClassName: 'inplaceeditor-form',
509 + else if (Event.KEY_RETURN == e.keyCode)
499 - highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
510 + this.handleFormSubmission(e);
500 - highlightendcolor: "#FFFFFF",
511 + },
501 - externalControl: null,
512 + createControl: function(mode, handler, extraClasses) {
502 - submitOnBlur: false,
513 + var control = this.options[mode + 'Control'];
503 - ajaxOptions: {},
514 + var text = this.options[mode + 'Text'];
504 - evalScripts: false
515 + if ('button' == control) {
505 - }, options || {});
516 + var btn = document.createElement('input');
506 -
517 + btn.type = 'submit';
507 - if(!this.options.formId && this.element.id) {
518 + btn.value = text;
508 - this.options.formId = this.element.id + "-inplaceeditor";
519 + btn.className = 'editor_' + mode + '_button';
509 - if ($(this.options.formId)) {
520 + if ('cancel' == mode)
510 - // there's already a form with that name, don't specify an id
521 + btn.onclick = this._boundCancelHandler;
511 - this.options.formId = null;
522 + this._form.appendChild(btn);
512 - }
523 + this._controls[mode] = btn;
513 - }
524 + } else if ('link' == control) {
514 -
525 + var link = document.createElement('a');
515 - if (this.options.externalControl) {
526 + link.href = '#';
516 - this.options.externalControl = $(this.options.externalControl);
527 + link.appendChild(document.createTextNode(text));
517 - }
528 + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
518 -
529 + link.className = 'editor_' + mode + '_link';
519 - this.originalBackground = Element.getStyle(this.element, 'background-color');
530 + if (extraClasses)
520 - if (!this.originalBackground) {
531 + link.className += ' ' + extraClasses;
521 - this.originalBackground = "transparent";
532 + this._form.appendChild(link);
522 - }
533 + this._controls[mode] = link;
523 -
524 - this.element.title = this.options.clickToEditText;
525 -
526 - this.onclickListener = this.enterEditMode.bindAsEventListener(this);
527 - this.mouseoverListener = this.enterHover.bindAsEventListener(this);
528 - this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
529 - Event.observe(this.element, 'click', this.onclickListener);
530 - Event.observe(this.element, 'mouseover', this.mouseoverListener);
531 - Event.observe(this.element, 'mouseout', this.mouseoutListener);
532 - if (this.options.externalControl) {
533 - Event.observe(this.options.externalControl, 'click', this.onclickListener);
534 - Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
535 - Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
536 }
534 }
537 },
535 },
538 - enterEditMode: function(evt) {
536 + createEditField: function() {
539 - if (this.saving) return;
537 + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
540 - if (this.editing) return;
538 + var fld;
541 - this.editing = true;
539 + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
542 - this.onEnterEditMode();
540 + fld = document.createElement('input');
543 - if (this.options.externalControl) {
541 + fld.type = 'text';
544 - Element.hide(this.options.externalControl);
542 + var size = this.options.size || this.options.cols || 0;
543 + if (0 < size) fld.size = size;
544 + } else {
545 + fld = document.createElement('textarea');
546 + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
547 + fld.cols = this.options.cols || 40;
545 }
548 }
546 - Element.hide(this.element);
549 + fld.name = this.options.paramName;
547 - this.createForm();
550 + fld.value = text; // No HTML breaks conversion anymore
548 - this.element.parentNode.insertBefore(this.form, this.element);
551 + fld.className = 'editor_field';
549 - if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
552 + if (this.options.submitOnBlur)
550 - // stop the event to avoid a page refresh in Safari
553 + fld.onblur = this._boundSubmitHandler;
551 - if (evt) {
554 + this._controls.editor = fld;
552 - Event.stop(evt);
555 + if (this.options.loadTextURL)
553 - }
556 + this.loadExternalText();
554 - return false;
557 + this._form.appendChild(this._controls.editor);
555 },
558 },
556 createForm: function() {
559 createForm: function() {
557 - this.form = document.createElement("form");
560 + var ipe = this;
558 - this.form.id = this.options.formId;
561 + function addText(mode, condition) {
559 - Element.addClassName(this.form, this.options.formClassName)
562 + var text = ipe.options['text' + mode + 'Controls'];
560 - this.form.onsubmit = this.onSubmit.bind(this);
563 + if (!text || condition === false) return;
561 -
564 + ipe._form.appendChild(document.createTextNode(text));
565 + };
566 + this._form = $(document.createElement('form'));
567 + this._form.id = this.options.formId;
568 + this._form.addClassName(this.options.formClassName);
569 + this._form.onsubmit = this._boundSubmitHandler;
562 this.createEditField();
570 this.createEditField();
563 -
571 + if ('textarea' == this._controls.editor.tagName.toLowerCase())
564 - if (this.options.textarea) {
572 + this._form.appendChild(document.createElement('br'));
565 - var br = document.createElement("br");
573 + if (this.options.onFormCustomization)
566 - this.form.appendChild(br);
574 + this.options.onFormCustomization(this, this._form);
567 - }
575 + addText('Before', this.options.okControl || this.options.cancelControl);
568 -
576 + this.createControl('ok', this._boundSubmitHandler);
569 - if (this.options.okButton) {
577 + addText('Between', this.options.okControl && this.options.cancelControl);
570 - okButton = document.createElement("input");
578 + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
571 - okButton.type = "submit";
579 + addText('After', this.options.okControl || this.options.cancelControl);
572 - okButton.value = this.options.okText;
573 - okButton.className = 'editor_ok_button';
574 - this.form.appendChild(okButton);
575 - }
576 -
577 - if (this.options.cancelLink) {
578 - cancelLink = document.createElement("a");
579 - cancelLink.href = "#";
580 - cancelLink.appendChild(document.createTextNode(this.options.cancelText));
581 - cancelLink.onclick = this.onclickCancel.bind(this);
582 - cancelLink.className = 'editor_cancel';
583 - this.form.appendChild(cancelLink);
584 - }
585 - },
586 - hasHTMLLineBreaks: function(string) {
587 - if (!this.options.handleLineBreaks) return false;
588 - return string.match(/<br/i) || string.match(/<p>/i);
589 - },
590 - convertHTMLLineBreaks: function(string) {
591 - return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
592 },
580 },
593 - createEditField: function() {
581 + destroy: function() {
594 - var text;
582 + if (this._oldInnerHTML)
595 - if(this.options.loadTextURL) {
583 + this.element.innerHTML = this._oldInnerHTML;
596 - text = this.options.loadingText;
584 + this.leaveEditMode();
597 - } else {
585 + this.unregisterListeners();
598 - text = this.getText();
586 + },
599 - }
587 + enterEditMode: function(e) {
600 -
588 + if (this._saving || this._editing) return;
601 - var obj = this;
589 + this._editing = true;
602 -
590 + this.triggerCallback('onEnterEditMode');
603 - if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
591 + if (this.options.externalControl)
604 - this.options.textarea = false;
592 + this.options.externalControl.hide();
605 - var textField = document.createElement("input");
593 + this.element.hide();
606 - textField.obj = this;
594 + this.createForm();
607 - textField.type = "text";
595 + this.element.parentNode.insertBefore(this._form, this.element);
608 - textField.name = this.options.paramName;
596 + if (!this.options.loadTextURL)
609 - textField.value = text;
597 + this.postProcessEditField();
610 - textField.style.backgroundColor = this.options.highlightcolor;
598 + if (e) Event.stop(e);
611 - textField.className = 'editor_field';
599 + },
612 - var size = this.options.size || this.options.cols || 0;
600 + enterHover: function(e) {
613 - if (size != 0) textField.size = size;
601 + if (this.options.hoverClassName)
614 - if (this.options.submitOnBlur)
602 + this.element.addClassName(this.options.hoverClassName);
615 - textField.onblur = this.onSubmit.bind(this);
603 + if (this._saving) return;
616 - this.editField = textField;
604 + this.triggerCallback('onEnterHover');
617 - } else {
618 - this.options.textarea = true;
619 - var textArea = document.createElement("textarea");
620 - textArea.obj = this;
621 - textArea.name = this.options.paramName;
622 - textArea.value = this.convertHTMLLineBreaks(text);
623 - textArea.rows = this.options.rows;
624 - textArea.cols = this.options.cols || 40;
625 - textArea.className = 'editor_field';
626 - if (this.options.submitOnBlur)
627 - textArea.onblur = this.onSubmit.bind(this);
628 - this.editField = textArea;
629 - }
630 -
631 - if(this.options.loadTextURL) {
632 - this.loadExternalText();
633 - }
634 - this.form.appendChild(this.editField);
635 },
605 },
636 getText: function() {
606 getText: function() {
637 return this.element.innerHTML;
607 return this.element.innerHTML;
638 },
608 },
639 - loadExternalText: function() {
609 + handleAJAXFailure: function(transport) {
640 - Element.addClassName(this.form, this.options.loadingClassName);
610 + this.triggerCallback('onFailure', transport);
641 - this.editField.disabled = true;
611 + if (this._oldInnerHTML) {
642 - new Ajax.Request(
612 + this.element.innerHTML = this._oldInnerHTML;
643 - this.options.loadTextURL,
613 + this._oldInnerHTML = null;
644 - Object.extend({
614 + }
645 - asynchronous: true,
615 + },
646 - onComplete: this.onLoadedExternalText.bind(this)
616 + handleFormCancellation: function(e) {
647 - }, this.options.ajaxOptions)
617 + this.wrapUp();
648 - );
618 + if (e) Event.stop(e);
649 },
619 },
650 - onLoadedExternalText: function(transport) {
620 + handleFormSubmission: function(e) {
651 - Element.removeClassName(this.form, this.options.loadingClassName);
621 + var form = this._form;
652 - this.editField.disabled = false;
622 + var value = $F(this._controls.editor);
653 - this.editField.value = transport.responseText.stripTags();
623 + this.prepareSubmission();
654 - Field.scrollFreeActivate(this.editField);
624 + var params = this.options.callback(form, value) || '';
655 - },
625 + if (Object.isString(params))
656 - onclickCancel: function() {
626 + params = params.toQueryParams();
657 - this.onComplete();
627 + params.editorId = this.element.id;
658 - this.leaveEditMode();
628 + if (this.options.htmlResponse) {
659 - return false;
629 + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
660 - },
630 + Object.extend(options, {
661 - onFailure: function(transport) {
631 + parameters: params,
662 - this.options.onFailure(transport);
632 + onComplete: this._boundWrapperHandler,
663 - if (this.oldInnerHTML) {
633 + onFailure: this._boundFailureHandler
664 - this.element.innerHTML = this.oldInnerHTML;
634 + });
665 - this.oldInnerHTML = null;
635 + new Ajax.Updater({ success: this.element }, this.url, options);
636 + } else {
637 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
638 + Object.extend(options, {
639 + parameters: params,
640 + onComplete: this._boundWrapperHandler,
641 + onFailure: this._boundFailureHandler
642 + });
643 + new Ajax.Request(this.url, options);
666 }
644 }
667 - return false;
645 + if (e) Event.stop(e);
668 },
646 },
669 - onSubmit: function() {
647 + leaveEditMode: function() {
670 - // onLoading resets these so we need to save them away for the Ajax call
648 + this.element.removeClassName(this.options.savingClassName);
671 - var form = this.form;
649 + this.removeForm();
672 - var value = this.editField.value;
650 + this.leaveHover();
673 -
651 + this.element.style.backgroundColor = this._originalBackground;
674 - // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
652 + this.element.show();
675 - // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
653 + if (this.options.externalControl)
676 - // to be displayed indefinitely
654 + this.options.externalControl.show();
677 - this.onLoading();
655 + this._saving = false;
678 -
656 + this._editing = false;
679 - if (this.options.evalScripts) {
657 + this._oldInnerHTML = null;
680 - new Ajax.Request(
658 + this.triggerCallback('onLeaveEditMode');
681 - this.url, Object.extend({
659 + },
682 - parameters: this.options.callback(form, value),
660 + leaveHover: function(e) {
683 - onComplete: this.onComplete.bind(this),
661 + if (this.options.hoverClassName)
684 - onFailure: this.onFailure.bind(this),
662 + this.element.removeClassName(this.options.hoverClassName);
685 - asynchronous:true,
663 + if (this._saving) return;
686 - evalScripts:true
664 + this.triggerCallback('onLeaveHover');
687 - }, this.options.ajaxOptions));
688 - } else {
689 - new Ajax.Updater(
690 - { success: this.element,
691 - // don't update on failure (this could be an option)
692 - failure: null },
693 - this.url, Object.extend({
694 - parameters: this.options.callback(form, value),
695 - onComplete: this.onComplete.bind(this),
696 - onFailure: this.onFailure.bind(this)
697 - }, this.options.ajaxOptions));
698 - }
699 - // stop the event to avoid a page refresh in Safari
700 - if (arguments.length > 1) {
701 - Event.stop(arguments[0]);
702 - }
703 - return false;
704 },
665 },
705 - onLoading: function() {
666 + loadExternalText: function() {
706 - this.saving = true;
667 + this._form.addClassName(this.options.loadingClassName);
668 + this._controls.editor.disabled = true;
669 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
670 + Object.extend(options, {
671 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
672 + onComplete: Prototype.emptyFunction,
673 + onSuccess: function(transport) {
674 + this._form.removeClassName(this.options.loadingClassName);
675 + var text = transport.responseText;
676 + if (this.options.stripLoadedTextTags)
677 + text = text.stripTags();
678 + this._controls.editor.value = text;
679 + this._controls.editor.disabled = false;
680 + this.postProcessEditField();
681 + }.bind(this),
682 + onFailure: this._boundFailureHandler
683 + });
684 + new Ajax.Request(this.options.loadTextURL, options);
685 + },
686 + postProcessEditField: function() {
687 + var fpc = this.options.fieldPostCreation;
688 + if (fpc)
689 + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
690 + },
691 + prepareOptions: function() {
692 + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
693 + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
694 + [this._extraDefaultOptions].flatten().compact().each(function(defs) {
695 + Object.extend(this.options, defs);
696 + }.bind(this));
697 + },
698 + prepareSubmission: function() {
699 + this._saving = true;
707 this.removeForm();
700 this.removeForm();
708 this.leaveHover();
701 this.leaveHover();
709 this.showSaving();
702 this.showSaving();
710 },
703 },
711 - showSaving: function() {
704 + registerListeners: function() {
712 - this.oldInnerHTML = this.element.innerHTML;
705 + this._listeners = { };
713 - this.element.innerHTML = this.options.savingText;
706 + var listener;
714 - Element.addClassName(this.element, this.options.savingClassName);
707 + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
715 - this.element.style.backgroundColor = this.originalBackground;
708 + listener = this[pair.value].bind(this);
716 - Element.show(this.element);
709 + this._listeners[pair.key] = listener;
710 + if (!this.options.externalControlOnly)
711 + this.element.observe(pair.key, listener);
712 + if (this.options.externalControl)
713 + this.options.externalControl.observe(pair.key, listener);
714 + }.bind(this));
717 },
715 },
718 removeForm: function() {
716 removeForm: function() {
719 - if(this.form) {
717 + if (!this._form) return;
720 - if (this.form.parentNode) Element.remove(this.form);
718 + this._form.remove();
721 - this.form = null;
719 + this._form = null;
720 + this._controls = { };
721 + },
722 + showSaving: function() {
723 + this._oldInnerHTML = this.element.innerHTML;
724 + this.element.innerHTML = this.options.savingText;
725 + this.element.addClassName(this.options.savingClassName);
726 + this.element.style.backgroundColor = this._originalBackground;
727 + this.element.show();
728 + },
729 + triggerCallback: function(cbName, arg) {
730 + if ('function' == typeof this.options[cbName]) {
731 + this.options[cbName](this, arg);
722 }
732 }
723 },
733 },
724 - enterHover: function() {
734 + unregisterListeners: function() {
725 - if (this.saving) return;
735 + $H(this._listeners).each(function(pair) {
726 - this.element.style.backgroundColor = this.options.highlightcolor;
736 + if (!this.options.externalControlOnly)
727 - if (this.effect) {
737 + this.element.stopObserving(pair.key, pair.value);
728 - this.effect.cancel();
738 + if (this.options.externalControl)
739 + this.options.externalControl.stopObserving(pair.key, pair.value);
740 + }.bind(this));
741 + },
742 + wrapUp: function(transport) {
743 + this.leaveEditMode();
744 + // Can't use triggerCallback due to backward compatibility: requires
745 + // binding + direct element
746 + this._boundComplete(transport, this.element);
729 }
747 }
730 - Element.addClassName(this.element, this.options.hoverClassName)
748 + });
749 +
750 + Object.extend(Ajax.InPlaceEditor.prototype, {
751 + dispose: Ajax.InPlaceEditor.prototype.destroy
752 + });
753 +
754 + Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
755 + initialize: function($super, element, url, options) {
756 + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
757 + $super(element, url, options);
731 },
758 },
732 - leaveHover: function() {
759 +
733 - if (this.options.backgroundColor) {
760 + createEditField: function() {
734 - this.element.style.backgroundColor = this.oldBackground;
761 + var list = document.createElement('select');
735 - }
762 + list.name = this.options.paramName;
736 - Element.removeClassName(this.element, this.options.hoverClassName)
763 + list.size = 1;
737 - if (this.saving) return;
764 + this._controls.editor = list;
738 - this.effect = new Effect.Highlight(this.element, {
765 + this._collection = this.options.collection || [];
739 - startcolor: this.options.highlightcolor,
766 + if (this.options.loadCollectionURL)
740 - endcolor: this.options.highlightendcolor,
767 + this.loadCollection();
741 - restorecolor: this.originalBackground
768 + else
769 + this.checkForExternalText();
770 + this._form.appendChild(this._controls.editor);
771 + },
772 +
773 + loadCollection: function() {
774 + this._form.addClassName(this.options.loadingClassName);
775 + this.showLoadingText(this.options.loadingCollectionText);
776 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
777 + Object.extend(options, {
778 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
779 + onComplete: Prototype.emptyFunction,
780 + onSuccess: function(transport) {
781 + var js = transport.responseText.strip();
782 + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
783 + throw 'Server returned an invalid collection representation.';
784 + this._collection = eval(js);
785 + this.checkForExternalText();
786 + }.bind(this),
787 + onFailure: this.onFailure
742 });
788 });
789 + new Ajax.Request(this.options.loadCollectionURL, options);
743 },
790 },
744 - leaveEditMode: function() {
791 +
745 - Element.removeClassName(this.element, this.options.savingClassName);
792 + showLoadingText: function(text) {
746 - this.removeForm();
793 + this._controls.editor.disabled = true;
747 - this.leaveHover();
794 + var tempOption = this._controls.editor.firstChild;
748 - this.element.style.backgroundColor = this.originalBackground;
795 + if (!tempOption) {
749 - Element.show(this.element);
796 + tempOption = document.createElement('option');
750 - if (this.options.externalControl) {
797 + tempOption.value = '';
751 - Element.show(this.options.externalControl);
798 + this._controls.editor.appendChild(tempOption);
799 + tempOption.selected = true;
752 }
800 }
753 - this.editing = false;
801 + tempOption.update((text || '').stripScripts().stripTags());
754 - this.saving = false;
802 + },
755 - this.oldInnerHTML = null;
803 +
756 - this.onLeaveEditMode();
804 + checkForExternalText: function() {
805 + this._text = this.getText();
806 + if (this.options.loadTextURL)
807 + this.loadExternalText();
808 + else
809 + this.buildOptionList();
810 + },
811 +
812 + loadExternalText: function() {
813 + this.showLoadingText(this.options.loadingText);
814 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
815 + Object.extend(options, {
816 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
817 + onComplete: Prototype.emptyFunction,
818 + onSuccess: function(transport) {
819 + this._text = transport.responseText.strip();
820 + this.buildOptionList();
821 + }.bind(this),
822 + onFailure: this.onFailure
823 + });
824 + new Ajax.Request(this.options.loadTextURL, options);
757 },
825 },
758 - onComplete: function(transport) {
826 +
759 - this.leaveEditMode();
827 + buildOptionList: function() {
760 - this.options.onComplete.bind(this)(transport, this.element);
828 + this._form.removeClassName(this.options.loadingClassName);
761 - },
829 + this._collection = this._collection.map(function(entry) {
762 - onEnterEditMode: function() {},
830 + return 2 === entry.length ? entry : [entry, entry].flatten();
763 - onLeaveEditMode: function() {},
831 + });
764 - dispose: function() {
832 + var marker = ('value' in this.options) ? this.options.value : this._text;
765 - if (this.oldInnerHTML) {
833 + var textFound = this._collection.any(function(entry) {
766 - this.element.innerHTML = this.oldInnerHTML;
834 + return entry[0] == marker;
835 + }.bind(this));
836 + this._controls.editor.update('');
837 + var option;
838 + this._collection.each(function(entry, index) {
839 + option = document.createElement('option');
840 + option.value = entry[0];
841 + option.selected = textFound ? entry[0] == marker : 0 == index;
842 + option.appendChild(document.createTextNode(entry[1]));
843 + this._controls.editor.appendChild(option);
844 + }.bind(this));
845 + this._controls.editor.disabled = false;
846 + Field.scrollFreeActivate(this._controls.editor);
767 }
847 }
768 - this.leaveEditMode();
848 + });
769 - Event.stopObserving(this.element, 'click', this.onclickListener);
849 +
770 - Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
850 + //**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
771 - Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
851 + //**** This only exists for a while, in order to let ****
772 - if (this.options.externalControl) {
852 + //**** users adapt to the new API. Read up on the new ****
773 - Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
853 + //**** API and convert your code to it ASAP! ****
774 - Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
854 +
775 - Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
855 + Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
776 - }
856 + if (!options) return;
777 - }
857 + function fallback(name, expr) {
858 + if (name in options || expr === undefined) return;
859 + options[name] = expr;
860 + };
861 + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
862 + options.cancelLink == options.cancelButton == false ? false : undefined)));
863 + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
864 + options.okLink == options.okButton == false ? false : undefined)));
865 + fallback('highlightColor', options.highlightcolor);
866 + fallback('highlightEndColor', options.highlightendcolor);
778 };
867 };
779
868
780 - Ajax.InPlaceCollectionEditor = Class.create();
869 + Object.extend(Ajax.InPlaceEditor, {
781 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
870 + DefaultOptions: {
782 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
871 + ajaxOptions: { },
783 - createEditField: function() {
872 + autoRows: 3, // Use when multi-line w/ rows == 1
784 - if (!this.cached_selectTag) {
873 + cancelControl: 'link', // 'link'|'button'|false
785 - var selectTag = document.createElement("select");
874 + cancelText: 'cancel',
786 - var collection = this.options.collection || [];
875 + clickToEditText: 'Click to edit',
787 - var optionTag;
876 + externalControl: null, // id|elt
788 - collection.each(function(e,i) {
877 + externalControlOnly: false,
789 - optionTag = document.createElement("option");
878 + fieldPostCreation: 'activate', // 'activate'|'focus'|false
790 - optionTag.value = (e instanceof Array) ? e[0] : e;
879 + formClassName: 'inplaceeditor-form',
791 - if((typeof this.options.value == 'undefined') &&
880 + formId: null, // id|elt
792 - ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
881 + highlightColor: '#ffff99',
793 - if(this.options.value==optionTag.value) optionTag.selected = true;
882 + highlightEndColor: '#ffffff',
794 - optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
883 + hoverClassName: '',
795 - selectTag.appendChild(optionTag);
884 + htmlResponse: true,
796 - }.bind(this));
885 + loadingClassName: 'inplaceeditor-loading',
797 - this.cached_selectTag = selectTag;
886 + loadingText: 'Loading...',
887 + okControl: 'button', // 'link'|'button'|false
888 + okText: 'ok',
889 + paramName: 'value',
890 + rows: 1, // If 1 and multi-line, uses autoRows
891 + savingClassName: 'inplaceeditor-saving',
892 + savingText: 'Saving...',
893 + size: 0,
894 + stripLoadedTextTags: false,
895 + submitOnBlur: false,
896 + textAfterControls: '',
897 + textBeforeControls: '',
898 + textBetweenControls: ''
899 + },
900 + DefaultCallbacks: {
901 + callback: function(form) {
902 + return Form.serialize(form);
903 + },
904 + onComplete: function(transport, element) {
905 + // For backward compatibility, this one is bound to the IPE, and passes
906 + // the element directly. It was too often customized, so we don't break it.
907 + new Effect.Highlight(element, {
908 + startcolor: this.options.highlightColor, keepBackgroundImage: true });
909 + },
910 + onEnterEditMode: null,
911 + onEnterHover: function(ipe) {
912 + ipe.element.style.backgroundColor = ipe.options.highlightColor;
913 + if (ipe._effect)
914 + ipe._effect.cancel();
915 + },
916 + onFailure: function(transport, ipe) {
917 + alert('Error communication with the server: ' + transport.responseText.stripTags());
918 + },
919 + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
920 + onLeaveEditMode: null,
921 + onLeaveHover: function(ipe) {
922 + ipe._effect = new Effect.Highlight(ipe.element, {
923 + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
924 + restorecolor: ipe._originalBackground, keepBackgroundImage: true
925 + });
798 }
926 }
799 -
927 + },
800 - this.editField = this.cached_selectTag;
928 + Listeners: {
801 - if(this.options.loadTextURL) this.loadExternalText();
929 + click: 'enterEditMode',
802 - this.form.appendChild(this.editField);
930 + keydown: 'checkForEscapeOrReturn',
803 - this.options.callback = function(form, value) {
931 + mouseover: 'enterHover',
804 - return "value=" + encodeURIComponent(value);
932 + mouseout: 'leaveHover'
805 - }
806 }
933 }
807 });
934 });
808
935
936 + Ajax.InPlaceCollectionEditor.DefaultOptions = {
937 + loadingCollectionText: 'Loading options...'
938 + };
939 +
809 // Delayed observer, like Form.Element.Observer,
940 // Delayed observer, like Form.Element.Observer,
810 // but waits for delay after last key input
941 // but waits for delay after last key input
811 // Ideal for live-search fields
942 // Ideal for live-search fields
812
943
813 - Form.Element.DelayedObserver = Class.create();
944 + Form.Element.DelayedObserver = Class.create({
814 - Form.Element.DelayedObserver.prototype = {
815 initialize: function(element, delay, callback) {
945 initialize: function(element, delay, callback) {
816 this.delay = delay || 0.5;
946 this.delay = delay || 0.5;
817 this.element = $(element);
947 this.element = $(element);
818 this.callback = callback;
948 this.callback = callback;
819 this.timer = null;
949 this.timer = null;
820 this.lastValue = $F(this.element);
950 this.lastValue = $F(this.element);
821 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
951 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
822 },
952 },
823 delayedListener: function(event) {
953 delayedListener: function(event) {
824 if(this.lastValue == $F(this.element)) return;
954 if(this.lastValue == $F(this.element)) return;
825 if(this.timer) clearTimeout(this.timer);
955 if(this.timer) clearTimeout(this.timer);
826 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
956 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
827 this.lastValue = $F(this.element);
957 this.lastValue = $F(this.element);
828 },
958 },
829 onTimerEvent: function() {
959 onTimerEvent: function() {
830 this.timer = null;
960 this.timer = null;
831 this.callback(this.element, $F(this.element));
961 this.callback(this.element, $F(this.element));
832 }
962 }
833 - };
963 + });
@@ -1,781 +1,811
1 - // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 - // (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
2 + // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
3 //
3 //
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
6
6
7 - if(typeof Effect == 'undefined')
7 + if(Object.isUndefined(Effect))
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
9
9
10 var Droppables = {
10 var Droppables = {
11 drops: [],
11 drops: [],
12
12
13 remove: function(element) {
13 remove: function(element) {
14 this.drops = this.drops.reject(function(d) { return d.element==$(element) });
14 this.drops = this.drops.reject(function(d) { return d.element==$(element) });
15 },
15 },
16
16
17 add: function(element) {
17 add: function(element) {
18 element = $(element);
18 element = $(element);
19 var options = Object.extend({
19 var options = Object.extend({
20 greedy: true,
20 greedy: true,
21 hoverclass: null,
21 hoverclass: null,
22 tree: false
22 tree: false
23 }, arguments[1] || {});
23 }, arguments[1] || { });
24
24
25 // cache containers
25 // cache containers
26 if(options.containment) {
26 if(options.containment) {
27 options._containers = [];
27 options._containers = [];
28 var containment = options.containment;
28 var containment = options.containment;
29 - if((typeof containment == 'object') &&
29 + if(Object.isArray(containment)) {
30 - (containment.constructor == Array)) {
31 containment.each( function(c) { options._containers.push($(c)) });
30 containment.each( function(c) { options._containers.push($(c)) });
32 } else {
31 } else {
33 options._containers.push($(containment));
32 options._containers.push($(containment));
34 }
33 }
35 }
34 }
36
35
37 if(options.accept) options.accept = [options.accept].flatten();
36 if(options.accept) options.accept = [options.accept].flatten();
38
37
39 Element.makePositioned(element); // fix IE
38 Element.makePositioned(element); // fix IE
40 options.element = element;
39 options.element = element;
41
40
42 this.drops.push(options);
41 this.drops.push(options);
43 },
42 },
44
43
45 findDeepestChild: function(drops) {
44 findDeepestChild: function(drops) {
46 deepest = drops[0];
45 deepest = drops[0];
47
46
48 for (i = 1; i < drops.length; ++i)
47 for (i = 1; i < drops.length; ++i)
49 if (Element.isParent(drops[i].element, deepest.element))
48 if (Element.isParent(drops[i].element, deepest.element))
50 deepest = drops[i];
49 deepest = drops[i];
51
50
52 return deepest;
51 return deepest;
53 },
52 },
54
53
55 isContained: function(element, drop) {
54 isContained: function(element, drop) {
56 var containmentNode;
55 var containmentNode;
57 if(drop.tree) {
56 if(drop.tree) {
58 containmentNode = element.treeNode;
57 containmentNode = element.treeNode;
59 } else {
58 } else {
60 containmentNode = element.parentNode;
59 containmentNode = element.parentNode;
61 }
60 }
62 return drop._containers.detect(function(c) { return containmentNode == c });
61 return drop._containers.detect(function(c) { return containmentNode == c });
63 },
62 },
64
63
65 isAffected: function(point, element, drop) {
64 isAffected: function(point, element, drop) {
66 return (
65 return (
67 (drop.element!=element) &&
66 (drop.element!=element) &&
68 ((!drop._containers) ||
67 ((!drop._containers) ||
69 this.isContained(element, drop)) &&
68 this.isContained(element, drop)) &&
70 ((!drop.accept) ||
69 ((!drop.accept) ||
71 (Element.classNames(element).detect(
70 (Element.classNames(element).detect(
72 function(v) { return drop.accept.include(v) } ) )) &&
71 function(v) { return drop.accept.include(v) } ) )) &&
73 Position.within(drop.element, point[0], point[1]) );
72 Position.within(drop.element, point[0], point[1]) );
74 },
73 },
75
74
76 deactivate: function(drop) {
75 deactivate: function(drop) {
77 if(drop.hoverclass)
76 if(drop.hoverclass)
78 Element.removeClassName(drop.element, drop.hoverclass);
77 Element.removeClassName(drop.element, drop.hoverclass);
79 this.last_active = null;
78 this.last_active = null;
80 },
79 },
81
80
82 activate: function(drop) {
81 activate: function(drop) {
83 if(drop.hoverclass)
82 if(drop.hoverclass)
84 Element.addClassName(drop.element, drop.hoverclass);
83 Element.addClassName(drop.element, drop.hoverclass);
85 this.last_active = drop;
84 this.last_active = drop;
86 },
85 },
87
86
88 show: function(point, element) {
87 show: function(point, element) {
89 if(!this.drops.length) return;
88 if(!this.drops.length) return;
90 - var affected = [];
89 + var drop, affected = [];
91
90
92 - if(this.last_active) this.deactivate(this.last_active);
93 this.drops.each( function(drop) {
91 this.drops.each( function(drop) {
94 if(Droppables.isAffected(point, element, drop))
92 if(Droppables.isAffected(point, element, drop))
95 affected.push(drop);
93 affected.push(drop);
96 });
94 });
97
95
98 - if(affected.length>0) {
96 + if(affected.length>0)
99 drop = Droppables.findDeepestChild(affected);
97 drop = Droppables.findDeepestChild(affected);
98 +
99 + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
100 + if (drop) {
100 Position.within(drop.element, point[0], point[1]);
101 Position.within(drop.element, point[0], point[1]);
101 if(drop.onHover)
102 if(drop.onHover)
102 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103
104
104 - Droppables.activate(drop);
105 + if (drop != this.last_active) Droppables.activate(drop);
105 }
106 }
106 },
107 },
107
108
108 fire: function(event, element) {
109 fire: function(event, element) {
109 if(!this.last_active) return;
110 if(!this.last_active) return;
110 Position.prepare();
111 Position.prepare();
111
112
112 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 - if (this.last_active.onDrop)
114 + if (this.last_active.onDrop) {
114 this.last_active.onDrop(element, this.last_active.element, event);
115 this.last_active.onDrop(element, this.last_active.element, event);
116 + return true;
117 + }
115 },
118 },
116
119
117 reset: function() {
120 reset: function() {
118 if(this.last_active)
121 if(this.last_active)
119 this.deactivate(this.last_active);
122 this.deactivate(this.last_active);
120 }
123 }
121 }
124 }
122
125
123 var Draggables = {
126 var Draggables = {
124 drags: [],
127 drags: [],
125 observers: [],
128 observers: [],
126
129
127 register: function(draggable) {
130 register: function(draggable) {
128 if(this.drags.length == 0) {
131 if(this.drags.length == 0) {
129 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
132 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
130 this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
133 this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
131 this.eventKeypress = this.keyPress.bindAsEventListener(this);
134 this.eventKeypress = this.keyPress.bindAsEventListener(this);
132
135
133 Event.observe(document, "mouseup", this.eventMouseUp);
136 Event.observe(document, "mouseup", this.eventMouseUp);
134 Event.observe(document, "mousemove", this.eventMouseMove);
137 Event.observe(document, "mousemove", this.eventMouseMove);
135 Event.observe(document, "keypress", this.eventKeypress);
138 Event.observe(document, "keypress", this.eventKeypress);
136 }
139 }
137 this.drags.push(draggable);
140 this.drags.push(draggable);
138 },
141 },
139
142
140 unregister: function(draggable) {
143 unregister: function(draggable) {
141 this.drags = this.drags.reject(function(d) { return d==draggable });
144 this.drags = this.drags.reject(function(d) { return d==draggable });
142 if(this.drags.length == 0) {
145 if(this.drags.length == 0) {
143 Event.stopObserving(document, "mouseup", this.eventMouseUp);
146 Event.stopObserving(document, "mouseup", this.eventMouseUp);
144 Event.stopObserving(document, "mousemove", this.eventMouseMove);
147 Event.stopObserving(document, "mousemove", this.eventMouseMove);
145 Event.stopObserving(document, "keypress", this.eventKeypress);
148 Event.stopObserving(document, "keypress", this.eventKeypress);
146 }
149 }
147 },
150 },
148
151
149 activate: function(draggable) {
152 activate: function(draggable) {
150 if(draggable.options.delay) {
153 if(draggable.options.delay) {
151 this._timeout = setTimeout(function() {
154 this._timeout = setTimeout(function() {
152 Draggables._timeout = null;
155 Draggables._timeout = null;
153 window.focus();
156 window.focus();
154 Draggables.activeDraggable = draggable;
157 Draggables.activeDraggable = draggable;
155 }.bind(this), draggable.options.delay);
158 }.bind(this), draggable.options.delay);
156 } else {
159 } else {
157 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
160 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
158 this.activeDraggable = draggable;
161 this.activeDraggable = draggable;
159 }
162 }
160 },
163 },
161
164
162 deactivate: function() {
165 deactivate: function() {
163 this.activeDraggable = null;
166 this.activeDraggable = null;
164 },
167 },
165
168
166 updateDrag: function(event) {
169 updateDrag: function(event) {
167 if(!this.activeDraggable) return;
170 if(!this.activeDraggable) return;
168 var pointer = [Event.pointerX(event), Event.pointerY(event)];
171 var pointer = [Event.pointerX(event), Event.pointerY(event)];
169 // Mozilla-based browsers fire successive mousemove events with
172 // Mozilla-based browsers fire successive mousemove events with
170 // the same coordinates, prevent needless redrawing (moz bug?)
173 // the same coordinates, prevent needless redrawing (moz bug?)
171 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
174 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
172 this._lastPointer = pointer;
175 this._lastPointer = pointer;
173
176
174 this.activeDraggable.updateDrag(event, pointer);
177 this.activeDraggable.updateDrag(event, pointer);
175 },
178 },
176
179
177 endDrag: function(event) {
180 endDrag: function(event) {
178 if(this._timeout) {
181 if(this._timeout) {
179 clearTimeout(this._timeout);
182 clearTimeout(this._timeout);
180 this._timeout = null;
183 this._timeout = null;
181 }
184 }
182 if(!this.activeDraggable) return;
185 if(!this.activeDraggable) return;
183 this._lastPointer = null;
186 this._lastPointer = null;
184 this.activeDraggable.endDrag(event);
187 this.activeDraggable.endDrag(event);
185 this.activeDraggable = null;
188 this.activeDraggable = null;
186 },
189 },
187
190
188 keyPress: function(event) {
191 keyPress: function(event) {
189 if(this.activeDraggable)
192 if(this.activeDraggable)
190 this.activeDraggable.keyPress(event);
193 this.activeDraggable.keyPress(event);
191 },
194 },
192
195
193 addObserver: function(observer) {
196 addObserver: function(observer) {
194 this.observers.push(observer);
197 this.observers.push(observer);
195 this._cacheObserverCallbacks();
198 this._cacheObserverCallbacks();
196 },
199 },
197
200
198 removeObserver: function(element) { // element instead of observer fixes mem leaks
201 removeObserver: function(element) { // element instead of observer fixes mem leaks
199 this.observers = this.observers.reject( function(o) { return o.element==element });
202 this.observers = this.observers.reject( function(o) { return o.element==element });
200 this._cacheObserverCallbacks();
203 this._cacheObserverCallbacks();
201 },
204 },
202
205
203 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
206 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
204 if(this[eventName+'Count'] > 0)
207 if(this[eventName+'Count'] > 0)
205 this.observers.each( function(o) {
208 this.observers.each( function(o) {
206 if(o[eventName]) o[eventName](eventName, draggable, event);
209 if(o[eventName]) o[eventName](eventName, draggable, event);
207 });
210 });
208 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
211 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
209 },
212 },
210
213
211 _cacheObserverCallbacks: function() {
214 _cacheObserverCallbacks: function() {
212 ['onStart','onEnd','onDrag'].each( function(eventName) {
215 ['onStart','onEnd','onDrag'].each( function(eventName) {
213 Draggables[eventName+'Count'] = Draggables.observers.select(
216 Draggables[eventName+'Count'] = Draggables.observers.select(
214 function(o) { return o[eventName]; }
217 function(o) { return o[eventName]; }
215 ).length;
218 ).length;
216 });
219 });
217 }
220 }
218 }
221 }
219
222
220 /*--------------------------------------------------------------------------*/
223 /*--------------------------------------------------------------------------*/
221
224
222 - var Draggable = Class.create();
225 + var Draggable = Class.create({
223 - Draggable._dragging = {};
224 -
225 - Draggable.prototype = {
226 initialize: function(element) {
226 initialize: function(element) {
227 var defaults = {
227 var defaults = {
228 handle: false,
228 handle: false,
229 reverteffect: function(element, top_offset, left_offset) {
229 reverteffect: function(element, top_offset, left_offset) {
230 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
230 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
231 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
231 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
232 queue: {scope:'_draggable', position:'end'}
232 queue: {scope:'_draggable', position:'end'}
233 });
233 });
234 },
234 },
235 endeffect: function(element) {
235 endeffect: function(element) {
236 - var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
236 + var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238 queue: {scope:'_draggable', position:'end'},
238 queue: {scope:'_draggable', position:'end'},
239 afterFinish: function(){
239 afterFinish: function(){
240 Draggable._dragging[element] = false
240 Draggable._dragging[element] = false
241 }
241 }
242 });
242 });
243 },
243 },
244 zindex: 1000,
244 zindex: 1000,
245 revert: false,
245 revert: false,
246 + quiet: false,
246 scroll: false,
247 scroll: false,
247 scrollSensitivity: 20,
248 scrollSensitivity: 20,
248 scrollSpeed: 15,
249 scrollSpeed: 15,
249 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
250 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
250 delay: 0
251 delay: 0
251 };
252 };
252
253
253 - if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
254 + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
254 Object.extend(defaults, {
255 Object.extend(defaults, {
255 starteffect: function(element) {
256 starteffect: function(element) {
256 element._opacity = Element.getOpacity(element);
257 element._opacity = Element.getOpacity(element);
257 Draggable._dragging[element] = true;
258 Draggable._dragging[element] = true;
258 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
259 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
259 }
260 }
260 });
261 });
261
262
262 var options = Object.extend(defaults, arguments[1] || {});
263 var options = Object.extend(defaults, arguments[1] || { });
263
264
264 this.element = $(element);
265 this.element = $(element);
265
266
266 - if(options.handle && (typeof options.handle == 'string'))
267 + if(options.handle && Object.isString(options.handle))
267 this.handle = this.element.down('.'+options.handle, 0);
268 this.handle = this.element.down('.'+options.handle, 0);
268
269
269 if(!this.handle) this.handle = $(options.handle);
270 if(!this.handle) this.handle = $(options.handle);
270 if(!this.handle) this.handle = this.element;
271 if(!this.handle) this.handle = this.element;
271
272
272 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
273 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
273 options.scroll = $(options.scroll);
274 options.scroll = $(options.scroll);
274 this._isScrollChild = Element.childOf(this.element, options.scroll);
275 this._isScrollChild = Element.childOf(this.element, options.scroll);
275 }
276 }
276
277
277 Element.makePositioned(this.element); // fix IE
278 Element.makePositioned(this.element); // fix IE
278
279
279 - this.delta = this.currentDelta();
280 this.options = options;
280 this.options = options;
281 this.dragging = false;
281 this.dragging = false;
282
282
283 this.eventMouseDown = this.initDrag.bindAsEventListener(this);
283 this.eventMouseDown = this.initDrag.bindAsEventListener(this);
284 Event.observe(this.handle, "mousedown", this.eventMouseDown);
284 Event.observe(this.handle, "mousedown", this.eventMouseDown);
285
285
286 Draggables.register(this);
286 Draggables.register(this);
287 },
287 },
288
288
289 destroy: function() {
289 destroy: function() {
290 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
290 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
291 Draggables.unregister(this);
291 Draggables.unregister(this);
292 },
292 },
293
293
294 currentDelta: function() {
294 currentDelta: function() {
295 return([
295 return([
296 parseInt(Element.getStyle(this.element,'left') || '0'),
296 parseInt(Element.getStyle(this.element,'left') || '0'),
297 parseInt(Element.getStyle(this.element,'top') || '0')]);
297 parseInt(Element.getStyle(this.element,'top') || '0')]);
298 },
298 },
299
299
300 initDrag: function(event) {
300 initDrag: function(event) {
301 - if(typeof Draggable._dragging[this.element] != 'undefined' &&
301 + if(!Object.isUndefined(Draggable._dragging[this.element]) &&
302 Draggable._dragging[this.element]) return;
302 Draggable._dragging[this.element]) return;
303 if(Event.isLeftClick(event)) {
303 if(Event.isLeftClick(event)) {
304 // abort on form elements, fixes a Firefox issue
304 // abort on form elements, fixes a Firefox issue
305 var src = Event.element(event);
305 var src = Event.element(event);
306 - if(src.tagName && (
306 + if((tag_name = src.tagName.toUpperCase()) && (
307 - src.tagName=='INPUT' ||
307 + tag_name=='INPUT' ||
308 - src.tagName=='SELECT' ||
308 + tag_name=='SELECT' ||
309 - src.tagName=='OPTION' ||
309 + tag_name=='OPTION' ||
310 - src.tagName=='BUTTON' ||
310 + tag_name=='BUTTON' ||
311 - src.tagName=='TEXTAREA')) return;
311 + tag_name=='TEXTAREA')) return;
312
312
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
314 var pos = Position.cumulativeOffset(this.element);
314 var pos = Position.cumulativeOffset(this.element);
315 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
315 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
316
316
317 Draggables.activate(this);
317 Draggables.activate(this);
318 Event.stop(event);
318 Event.stop(event);
319 }
319 }
320 },
320 },
321
321
322 startDrag: function(event) {
322 startDrag: function(event) {
323 this.dragging = true;
323 this.dragging = true;
324 + if(!this.delta)
325 + this.delta = this.currentDelta();
324
326
325 if(this.options.zindex) {
327 if(this.options.zindex) {
326 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
328 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
327 this.element.style.zIndex = this.options.zindex;
329 this.element.style.zIndex = this.options.zindex;
328 }
330 }
329
331
330 if(this.options.ghosting) {
332 if(this.options.ghosting) {
331 this._clone = this.element.cloneNode(true);
333 this._clone = this.element.cloneNode(true);
334 + this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
335 + if (!this.element._originallyAbsolute)
332 Position.absolutize(this.element);
336 Position.absolutize(this.element);
333 this.element.parentNode.insertBefore(this._clone, this.element);
337 this.element.parentNode.insertBefore(this._clone, this.element);
334 }
338 }
335
339
336 if(this.options.scroll) {
340 if(this.options.scroll) {
337 if (this.options.scroll == window) {
341 if (this.options.scroll == window) {
338 var where = this._getWindowScroll(this.options.scroll);
342 var where = this._getWindowScroll(this.options.scroll);
339 this.originalScrollLeft = where.left;
343 this.originalScrollLeft = where.left;
340 this.originalScrollTop = where.top;
344 this.originalScrollTop = where.top;
341 } else {
345 } else {
342 this.originalScrollLeft = this.options.scroll.scrollLeft;
346 this.originalScrollLeft = this.options.scroll.scrollLeft;
343 this.originalScrollTop = this.options.scroll.scrollTop;
347 this.originalScrollTop = this.options.scroll.scrollTop;
344 }
348 }
345 }
349 }
346
350
347 Draggables.notify('onStart', this, event);
351 Draggables.notify('onStart', this, event);
348
352
349 if(this.options.starteffect) this.options.starteffect(this.element);
353 if(this.options.starteffect) this.options.starteffect(this.element);
350 },
354 },
351
355
352 updateDrag: function(event, pointer) {
356 updateDrag: function(event, pointer) {
353 if(!this.dragging) this.startDrag(event);
357 if(!this.dragging) this.startDrag(event);
358 +
359 + if(!this.options.quiet){
354 Position.prepare();
360 Position.prepare();
355 Droppables.show(pointer, this.element);
361 Droppables.show(pointer, this.element);
362 + }
363 +
356 Draggables.notify('onDrag', this, event);
364 Draggables.notify('onDrag', this, event);
357
365
358 this.draw(pointer);
366 this.draw(pointer);
359 if(this.options.change) this.options.change(this);
367 if(this.options.change) this.options.change(this);
360
368
361 if(this.options.scroll) {
369 if(this.options.scroll) {
362 this.stopScrolling();
370 this.stopScrolling();
363
371
364 var p;
372 var p;
365 if (this.options.scroll == window) {
373 if (this.options.scroll == window) {
366 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
374 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
367 } else {
375 } else {
368 p = Position.page(this.options.scroll);
376 p = Position.page(this.options.scroll);
369 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
377 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
370 p[1] += this.options.scroll.scrollTop + Position.deltaY;
378 p[1] += this.options.scroll.scrollTop + Position.deltaY;
371 p.push(p[0]+this.options.scroll.offsetWidth);
379 p.push(p[0]+this.options.scroll.offsetWidth);
372 p.push(p[1]+this.options.scroll.offsetHeight);
380 p.push(p[1]+this.options.scroll.offsetHeight);
373 }
381 }
374 var speed = [0,0];
382 var speed = [0,0];
375 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
383 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
376 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
384 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
377 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
385 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
378 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
386 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
379 this.startScrolling(speed);
387 this.startScrolling(speed);
380 }
388 }
381
389
382 // fix AppleWebKit rendering
390 // fix AppleWebKit rendering
383 - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
391 + if(Prototype.Browser.WebKit) window.scrollBy(0,0);
384
392
385 Event.stop(event);
393 Event.stop(event);
386 },
394 },
387
395
388 finishDrag: function(event, success) {
396 finishDrag: function(event, success) {
389 this.dragging = false;
397 this.dragging = false;
390
398
399 + if(this.options.quiet){
400 + Position.prepare();
401 + var pointer = [Event.pointerX(event), Event.pointerY(event)];
402 + Droppables.show(pointer, this.element);
403 + }
404 +
391 if(this.options.ghosting) {
405 if(this.options.ghosting) {
406 + if (!this.element._originallyAbsolute)
392 Position.relativize(this.element);
407 Position.relativize(this.element);
408 + delete this.element._originallyAbsolute;
393 Element.remove(this._clone);
409 Element.remove(this._clone);
394 this._clone = null;
410 this._clone = null;
395 }
411 }
396
412
397 - if(success) Droppables.fire(event, this.element);
413 + var dropped = false;
414 + if(success) {
415 + dropped = Droppables.fire(event, this.element);
416 + if (!dropped) dropped = false;
417 + }
418 + if(dropped && this.options.onDropped) this.options.onDropped(this.element);
398 Draggables.notify('onEnd', this, event);
419 Draggables.notify('onEnd', this, event);
399
420
400 var revert = this.options.revert;
421 var revert = this.options.revert;
401 - if(revert && typeof revert == 'function') revert = revert(this.element);
422 + if(revert && Object.isFunction(revert)) revert = revert(this.element);
402
423
403 var d = this.currentDelta();
424 var d = this.currentDelta();
404 if(revert && this.options.reverteffect) {
425 if(revert && this.options.reverteffect) {
426 + if (dropped == 0 || revert != 'failure')
405 this.options.reverteffect(this.element,
427 this.options.reverteffect(this.element,
406 d[1]-this.delta[1], d[0]-this.delta[0]);
428 d[1]-this.delta[1], d[0]-this.delta[0]);
407 } else {
429 } else {
408 this.delta = d;
430 this.delta = d;
409 }
431 }
410
432
411 if(this.options.zindex)
433 if(this.options.zindex)
412 this.element.style.zIndex = this.originalZ;
434 this.element.style.zIndex = this.originalZ;
413
435
414 if(this.options.endeffect)
436 if(this.options.endeffect)
415 this.options.endeffect(this.element);
437 this.options.endeffect(this.element);
416
438
417 Draggables.deactivate(this);
439 Draggables.deactivate(this);
418 Droppables.reset();
440 Droppables.reset();
419 },
441 },
420
442
421 keyPress: function(event) {
443 keyPress: function(event) {
422 if(event.keyCode!=Event.KEY_ESC) return;
444 if(event.keyCode!=Event.KEY_ESC) return;
423 this.finishDrag(event, false);
445 this.finishDrag(event, false);
424 Event.stop(event);
446 Event.stop(event);
425 },
447 },
426
448
427 endDrag: function(event) {
449 endDrag: function(event) {
428 if(!this.dragging) return;
450 if(!this.dragging) return;
429 this.stopScrolling();
451 this.stopScrolling();
430 this.finishDrag(event, true);
452 this.finishDrag(event, true);
431 Event.stop(event);
453 Event.stop(event);
432 },
454 },
433
455
434 draw: function(point) {
456 draw: function(point) {
435 var pos = Position.cumulativeOffset(this.element);
457 var pos = Position.cumulativeOffset(this.element);
436 if(this.options.ghosting) {
458 if(this.options.ghosting) {
437 var r = Position.realOffset(this.element);
459 var r = Position.realOffset(this.element);
438 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
460 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
439 }
461 }
440
462
441 var d = this.currentDelta();
463 var d = this.currentDelta();
442 pos[0] -= d[0]; pos[1] -= d[1];
464 pos[0] -= d[0]; pos[1] -= d[1];
443
465
444 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
466 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
445 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
467 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
446 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
468 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
447 }
469 }
448
470
449 var p = [0,1].map(function(i){
471 var p = [0,1].map(function(i){
450 return (point[i]-pos[i]-this.offset[i])
472 return (point[i]-pos[i]-this.offset[i])
451 }.bind(this));
473 }.bind(this));
452
474
453 if(this.options.snap) {
475 if(this.options.snap) {
454 - if(typeof this.options.snap == 'function') {
476 + if(Object.isFunction(this.options.snap)) {
455 p = this.options.snap(p[0],p[1],this);
477 p = this.options.snap(p[0],p[1],this);
456 } else {
478 } else {
457 - if(this.options.snap instanceof Array) {
479 + if(Object.isArray(this.options.snap)) {
458 p = p.map( function(v, i) {
480 p = p.map( function(v, i) {
459 - return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
481 + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
460 } else {
482 } else {
461 p = p.map( function(v) {
483 p = p.map( function(v) {
462 - return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
484 + return (v/this.options.snap).round()*this.options.snap }.bind(this))
463 }
485 }
464 }}
486 }}
465
487
466 var style = this.element.style;
488 var style = this.element.style;
467 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
489 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
468 style.left = p[0] + "px";
490 style.left = p[0] + "px";
469 if((!this.options.constraint) || (this.options.constraint=='vertical'))
491 if((!this.options.constraint) || (this.options.constraint=='vertical'))
470 style.top = p[1] + "px";
492 style.top = p[1] + "px";
471
493
472 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
494 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
473 },
495 },
474
496
475 stopScrolling: function() {
497 stopScrolling: function() {
476 if(this.scrollInterval) {
498 if(this.scrollInterval) {
477 clearInterval(this.scrollInterval);
499 clearInterval(this.scrollInterval);
478 this.scrollInterval = null;
500 this.scrollInterval = null;
479 Draggables._lastScrollPointer = null;
501 Draggables._lastScrollPointer = null;
480 }
502 }
481 },
503 },
482
504
483 startScrolling: function(speed) {
505 startScrolling: function(speed) {
484 if(!(speed[0] || speed[1])) return;
506 if(!(speed[0] || speed[1])) return;
485 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
507 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
486 this.lastScrolled = new Date();
508 this.lastScrolled = new Date();
487 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
509 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
488 },
510 },
489
511
490 scroll: function() {
512 scroll: function() {
491 var current = new Date();
513 var current = new Date();
492 var delta = current - this.lastScrolled;
514 var delta = current - this.lastScrolled;
493 this.lastScrolled = current;
515 this.lastScrolled = current;
494 if(this.options.scroll == window) {
516 if(this.options.scroll == window) {
495 with (this._getWindowScroll(this.options.scroll)) {
517 with (this._getWindowScroll(this.options.scroll)) {
496 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
518 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
497 var d = delta / 1000;
519 var d = delta / 1000;
498 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
520 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
499 }
521 }
500 }
522 }
501 } else {
523 } else {
502 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
524 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
503 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
525 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
504 }
526 }
505
527
506 Position.prepare();
528 Position.prepare();
507 Droppables.show(Draggables._lastPointer, this.element);
529 Droppables.show(Draggables._lastPointer, this.element);
508 Draggables.notify('onDrag', this);
530 Draggables.notify('onDrag', this);
509 if (this._isScrollChild) {
531 if (this._isScrollChild) {
510 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
532 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
511 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
533 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
512 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
534 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
513 if (Draggables._lastScrollPointer[0] < 0)
535 if (Draggables._lastScrollPointer[0] < 0)
514 Draggables._lastScrollPointer[0] = 0;
536 Draggables._lastScrollPointer[0] = 0;
515 if (Draggables._lastScrollPointer[1] < 0)
537 if (Draggables._lastScrollPointer[1] < 0)
516 Draggables._lastScrollPointer[1] = 0;
538 Draggables._lastScrollPointer[1] = 0;
517 this.draw(Draggables._lastScrollPointer);
539 this.draw(Draggables._lastScrollPointer);
518 }
540 }
519
541
520 if(this.options.change) this.options.change(this);
542 if(this.options.change) this.options.change(this);
521 },
543 },
522
544
523 _getWindowScroll: function(w) {
545 _getWindowScroll: function(w) {
524 var T, L, W, H;
546 var T, L, W, H;
525 with (w.document) {
547 with (w.document) {
526 if (w.document.documentElement && documentElement.scrollTop) {
548 if (w.document.documentElement && documentElement.scrollTop) {
527 T = documentElement.scrollTop;
549 T = documentElement.scrollTop;
528 L = documentElement.scrollLeft;
550 L = documentElement.scrollLeft;
529 } else if (w.document.body) {
551 } else if (w.document.body) {
530 T = body.scrollTop;
552 T = body.scrollTop;
531 L = body.scrollLeft;
553 L = body.scrollLeft;
532 }
554 }
533 if (w.innerWidth) {
555 if (w.innerWidth) {
534 W = w.innerWidth;
556 W = w.innerWidth;
535 H = w.innerHeight;
557 H = w.innerHeight;
536 } else if (w.document.documentElement && documentElement.clientWidth) {
558 } else if (w.document.documentElement && documentElement.clientWidth) {
537 W = documentElement.clientWidth;
559 W = documentElement.clientWidth;
538 H = documentElement.clientHeight;
560 H = documentElement.clientHeight;
539 } else {
561 } else {
540 W = body.offsetWidth;
562 W = body.offsetWidth;
541 H = body.offsetHeight
563 H = body.offsetHeight
542 }
564 }
543 }
565 }
544 return { top: T, left: L, width: W, height: H };
566 return { top: T, left: L, width: W, height: H };
545 }
567 }
546 - }
568 + });
569 +
570 + Draggable._dragging = { };
547
571
548 /*--------------------------------------------------------------------------*/
572 /*--------------------------------------------------------------------------*/
549
573
550 - var SortableObserver = Class.create();
574 + var SortableObserver = Class.create({
551 - SortableObserver.prototype = {
552 initialize: function(element, observer) {
575 initialize: function(element, observer) {
553 this.element = $(element);
576 this.element = $(element);
554 this.observer = observer;
577 this.observer = observer;
555 this.lastValue = Sortable.serialize(this.element);
578 this.lastValue = Sortable.serialize(this.element);
556 },
579 },
557
580
558 onStart: function() {
581 onStart: function() {
559 this.lastValue = Sortable.serialize(this.element);
582 this.lastValue = Sortable.serialize(this.element);
560 },
583 },
561
584
562 onEnd: function() {
585 onEnd: function() {
563 Sortable.unmark();
586 Sortable.unmark();
564 if(this.lastValue != Sortable.serialize(this.element))
587 if(this.lastValue != Sortable.serialize(this.element))
565 this.observer(this.element)
588 this.observer(this.element)
566 }
589 }
567 - }
590 + });
568
591
569 var Sortable = {
592 var Sortable = {
570 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
593 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
571
594
572 sortables: {},
595 sortables: { },
573
596
574 _findRootElement: function(element) {
597 _findRootElement: function(element) {
575 - while (element.tagName != "BODY") {
598 + while (element.tagName.toUpperCase() != "BODY") {
576 if(element.id && Sortable.sortables[element.id]) return element;
599 if(element.id && Sortable.sortables[element.id]) return element;
577 element = element.parentNode;
600 element = element.parentNode;
578 }
601 }
579 },
602 },
580
603
581 options: function(element) {
604 options: function(element) {
582 element = Sortable._findRootElement($(element));
605 element = Sortable._findRootElement($(element));
583 if(!element) return;
606 if(!element) return;
584 return Sortable.sortables[element.id];
607 return Sortable.sortables[element.id];
585 },
608 },
586
609
587 destroy: function(element){
610 destroy: function(element){
588 var s = Sortable.options(element);
611 var s = Sortable.options(element);
589
612
590 if(s) {
613 if(s) {
591 Draggables.removeObserver(s.element);
614 Draggables.removeObserver(s.element);
592 s.droppables.each(function(d){ Droppables.remove(d) });
615 s.droppables.each(function(d){ Droppables.remove(d) });
593 s.draggables.invoke('destroy');
616 s.draggables.invoke('destroy');
594
617
595 delete Sortable.sortables[s.element.id];
618 delete Sortable.sortables[s.element.id];
596 }
619 }
597 },
620 },
598
621
599 create: function(element) {
622 create: function(element) {
600 element = $(element);
623 element = $(element);
601 var options = Object.extend({
624 var options = Object.extend({
602 element: element,
625 element: element,
603 tag: 'li', // assumes li children, override with tag: 'tagname'
626 tag: 'li', // assumes li children, override with tag: 'tagname'
604 dropOnEmpty: false,
627 dropOnEmpty: false,
605 tree: false,
628 tree: false,
606 treeTag: 'ul',
629 treeTag: 'ul',
607 overlap: 'vertical', // one of 'vertical', 'horizontal'
630 overlap: 'vertical', // one of 'vertical', 'horizontal'
608 constraint: 'vertical', // one of 'vertical', 'horizontal', false
631 constraint: 'vertical', // one of 'vertical', 'horizontal', false
609 containment: element, // also takes array of elements (or id's); or false
632 containment: element, // also takes array of elements (or id's); or false
610 handle: false, // or a CSS class
633 handle: false, // or a CSS class
611 only: false,
634 only: false,
612 delay: 0,
635 delay: 0,
613 hoverclass: null,
636 hoverclass: null,
614 ghosting: false,
637 ghosting: false,
638 + quiet: false,
615 scroll: false,
639 scroll: false,
616 scrollSensitivity: 20,
640 scrollSensitivity: 20,
617 scrollSpeed: 15,
641 scrollSpeed: 15,
618 format: this.SERIALIZE_RULE,
642 format: this.SERIALIZE_RULE,
643 +
644 + // these take arrays of elements or ids and can be
645 + // used for better initialization performance
646 + elements: false,
647 + handles: false,
648 +
619 onChange: Prototype.emptyFunction,
649 onChange: Prototype.emptyFunction,
620 onUpdate: Prototype.emptyFunction
650 onUpdate: Prototype.emptyFunction
621 }, arguments[1] || {});
651 }, arguments[1] || { });
622
652
623 // clear any old sortable with same element
653 // clear any old sortable with same element
624 this.destroy(element);
654 this.destroy(element);
625
655
626 // build options for the draggables
656 // build options for the draggables
627 var options_for_draggable = {
657 var options_for_draggable = {
628 revert: true,
658 revert: true,
659 + quiet: options.quiet,
629 scroll: options.scroll,
660 scroll: options.scroll,
630 scrollSpeed: options.scrollSpeed,
661 scrollSpeed: options.scrollSpeed,
631 scrollSensitivity: options.scrollSensitivity,
662 scrollSensitivity: options.scrollSensitivity,
632 delay: options.delay,
663 delay: options.delay,
633 ghosting: options.ghosting,
664 ghosting: options.ghosting,
634 constraint: options.constraint,
665 constraint: options.constraint,
635 handle: options.handle };
666 handle: options.handle };
636
667
637 if(options.starteffect)
668 if(options.starteffect)
638 options_for_draggable.starteffect = options.starteffect;
669 options_for_draggable.starteffect = options.starteffect;
639
670
640 if(options.reverteffect)
671 if(options.reverteffect)
641 options_for_draggable.reverteffect = options.reverteffect;
672 options_for_draggable.reverteffect = options.reverteffect;
642 else
673 else
643 if(options.ghosting) options_for_draggable.reverteffect = function(element) {
674 if(options.ghosting) options_for_draggable.reverteffect = function(element) {
644 element.style.top = 0;
675 element.style.top = 0;
645 element.style.left = 0;
676 element.style.left = 0;
646 };
677 };
647
678
648 if(options.endeffect)
679 if(options.endeffect)
649 options_for_draggable.endeffect = options.endeffect;
680 options_for_draggable.endeffect = options.endeffect;
650
681
651 if(options.zindex)
682 if(options.zindex)
652 options_for_draggable.zindex = options.zindex;
683 options_for_draggable.zindex = options.zindex;
653
684
654 // build options for the droppables
685 // build options for the droppables
655 var options_for_droppable = {
686 var options_for_droppable = {
656 overlap: options.overlap,
687 overlap: options.overlap,
657 containment: options.containment,
688 containment: options.containment,
658 tree: options.tree,
689 tree: options.tree,
659 hoverclass: options.hoverclass,
690 hoverclass: options.hoverclass,
660 onHover: Sortable.onHover
691 onHover: Sortable.onHover
661 }
692 }
662
693
663 var options_for_tree = {
694 var options_for_tree = {
664 onHover: Sortable.onEmptyHover,
695 onHover: Sortable.onEmptyHover,
665 overlap: options.overlap,
696 overlap: options.overlap,
666 containment: options.containment,
697 containment: options.containment,
667 hoverclass: options.hoverclass
698 hoverclass: options.hoverclass
668 }
699 }
669
700
670 // fix for gecko engine
701 // fix for gecko engine
671 Element.cleanWhitespace(element);
702 Element.cleanWhitespace(element);
672
703
673 options.draggables = [];
704 options.draggables = [];
674 options.droppables = [];
705 options.droppables = [];
675
706
676 // drop on empty handling
707 // drop on empty handling
677 if(options.dropOnEmpty || options.tree) {
708 if(options.dropOnEmpty || options.tree) {
678 Droppables.add(element, options_for_tree);
709 Droppables.add(element, options_for_tree);
679 options.droppables.push(element);
710 options.droppables.push(element);
680 }
711 }
681
712
682 - (this.findElements(element, options) || []).each( function(e) {
713 + (options.elements || this.findElements(element, options) || []).each( function(e,i) {
683 - // handles are per-draggable
714 + var handle = options.handles ? $(options.handles[i]) :
684 - var handle = options.handle ?
715 + (options.handle ? $(e).select('.' + options.handle)[0] : e);
685 - $(e).down('.'+options.handle,0) : e;
686 options.draggables.push(
716 options.draggables.push(
687 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
717 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
688 Droppables.add(e, options_for_droppable);
718 Droppables.add(e, options_for_droppable);
689 if(options.tree) e.treeNode = element;
719 if(options.tree) e.treeNode = element;
690 options.droppables.push(e);
720 options.droppables.push(e);
691 });
721 });
692
722
693 if(options.tree) {
723 if(options.tree) {
694 (Sortable.findTreeElements(element, options) || []).each( function(e) {
724 (Sortable.findTreeElements(element, options) || []).each( function(e) {
695 Droppables.add(e, options_for_tree);
725 Droppables.add(e, options_for_tree);
696 e.treeNode = element;
726 e.treeNode = element;
697 options.droppables.push(e);
727 options.droppables.push(e);
698 });
728 });
699 }
729 }
700
730
701 // keep reference
731 // keep reference
702 this.sortables[element.id] = options;
732 this.sortables[element.id] = options;
703
733
704 // for onupdate
734 // for onupdate
705 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
735 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
706
736
707 },
737 },
708
738
709 // return all suitable-for-sortable elements in a guaranteed order
739 // return all suitable-for-sortable elements in a guaranteed order
710 findElements: function(element, options) {
740 findElements: function(element, options) {
711 return Element.findChildren(
741 return Element.findChildren(
712 element, options.only, options.tree ? true : false, options.tag);
742 element, options.only, options.tree ? true : false, options.tag);
713 },
743 },
714
744
715 findTreeElements: function(element, options) {
745 findTreeElements: function(element, options) {
716 return Element.findChildren(
746 return Element.findChildren(
717 element, options.only, options.tree ? true : false, options.treeTag);
747 element, options.only, options.tree ? true : false, options.treeTag);
718 },
748 },
719
749
720 onHover: function(element, dropon, overlap) {
750 onHover: function(element, dropon, overlap) {
721 if(Element.isParent(dropon, element)) return;
751 if(Element.isParent(dropon, element)) return;
722
752
723 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
753 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
724 return;
754 return;
725 } else if(overlap>0.5) {
755 } else if(overlap>0.5) {
726 Sortable.mark(dropon, 'before');
756 Sortable.mark(dropon, 'before');
727 if(dropon.previousSibling != element) {
757 if(dropon.previousSibling != element) {
728 var oldParentNode = element.parentNode;
758 var oldParentNode = element.parentNode;
729 element.style.visibility = "hidden"; // fix gecko rendering
759 element.style.visibility = "hidden"; // fix gecko rendering
730 dropon.parentNode.insertBefore(element, dropon);
760 dropon.parentNode.insertBefore(element, dropon);
731 if(dropon.parentNode!=oldParentNode)
761 if(dropon.parentNode!=oldParentNode)
732 Sortable.options(oldParentNode).onChange(element);
762 Sortable.options(oldParentNode).onChange(element);
733 Sortable.options(dropon.parentNode).onChange(element);
763 Sortable.options(dropon.parentNode).onChange(element);
734 }
764 }
735 } else {
765 } else {
736 Sortable.mark(dropon, 'after');
766 Sortable.mark(dropon, 'after');
737 var nextElement = dropon.nextSibling || null;
767 var nextElement = dropon.nextSibling || null;
738 if(nextElement != element) {
768 if(nextElement != element) {
739 var oldParentNode = element.parentNode;
769 var oldParentNode = element.parentNode;
740 element.style.visibility = "hidden"; // fix gecko rendering
770 element.style.visibility = "hidden"; // fix gecko rendering
741 dropon.parentNode.insertBefore(element, nextElement);
771 dropon.parentNode.insertBefore(element, nextElement);
742 if(dropon.parentNode!=oldParentNode)
772 if(dropon.parentNode!=oldParentNode)
743 Sortable.options(oldParentNode).onChange(element);
773 Sortable.options(oldParentNode).onChange(element);
744 Sortable.options(dropon.parentNode).onChange(element);
774 Sortable.options(dropon.parentNode).onChange(element);
745 }
775 }
746 }
776 }
747 },
777 },
748
778
749 onEmptyHover: function(element, dropon, overlap) {
779 onEmptyHover: function(element, dropon, overlap) {
750 var oldParentNode = element.parentNode;
780 var oldParentNode = element.parentNode;
751 var droponOptions = Sortable.options(dropon);
781 var droponOptions = Sortable.options(dropon);
752
782
753 if(!Element.isParent(dropon, element)) {
783 if(!Element.isParent(dropon, element)) {
754 var index;
784 var index;
755
785
756 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
786 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
757 var child = null;
787 var child = null;
758
788
759 if(children) {
789 if(children) {
760 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
790 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
761
791
762 for (index = 0; index < children.length; index += 1) {
792 for (index = 0; index < children.length; index += 1) {
763 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
793 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
764 offset -= Element.offsetSize (children[index], droponOptions.overlap);
794 offset -= Element.offsetSize (children[index], droponOptions.overlap);
765 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
795 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
766 child = index + 1 < children.length ? children[index + 1] : null;
796 child = index + 1 < children.length ? children[index + 1] : null;
767 break;
797 break;
768 } else {
798 } else {
769 child = children[index];
799 child = children[index];
770 break;
800 break;
771 }
801 }
772 }
802 }
773 }
803 }
774
804
775 dropon.insertBefore(element, child);
805 dropon.insertBefore(element, child);
776
806
777 Sortable.options(oldParentNode).onChange(element);
807 Sortable.options(oldParentNode).onChange(element);
778 droponOptions.onChange(element);
808 droponOptions.onChange(element);
779 }
809 }
780 },
810 },
781
811
This diff has been collapsed as it changes many lines, (596 lines changed) Show them Hide them
@@ -1,1088 +1,1120
1 - // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // Contributors:
2 // Contributors:
3 // Justin Palmer (http://encytemedia.com/)
3 // Justin Palmer (http://encytemedia.com/)
4 // Mark Pilgrim (http://diveintomark.org/)
4 // Mark Pilgrim (http://diveintomark.org/)
5 // Martin Bialasinki
5 // Martin Bialasinki
6 //
6 //
7 // script.aculo.us is freely distributable under the terms of an MIT-style license.
7 // script.aculo.us is freely distributable under the terms of an MIT-style license.
8 // For details, see the script.aculo.us web site: http://script.aculo.us/
8 // For details, see the script.aculo.us web site: http://script.aculo.us/
9
9
10 // converts rgb() and #xxx to #xxxxxx format,
10 // converts rgb() and #xxx to #xxxxxx format,
11 // returns self (or first argument) if not convertable
11 // returns self (or first argument) if not convertable
12 String.prototype.parseColor = function() {
12 String.prototype.parseColor = function() {
13 var color = '#';
13 var color = '#';
14 if(this.slice(0,4) == 'rgb(') {
14 if (this.slice(0,4) == 'rgb(') {
15 var cols = this.slice(4,this.length-1).split(',');
15 var cols = this.slice(4,this.length-1).split(',');
16 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
16 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
17 } else {
17 } else {
18 if(this.slice(0,1) == '#') {
18 if (this.slice(0,1) == '#') {
19 if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
19 if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
20 if(this.length==7) color = this.toLowerCase();
20 if (this.length==7) color = this.toLowerCase();
21 }
21 }
22 }
22 }
23 return(color.length==7 ? color : (arguments[0] || this));
23 return (color.length==7 ? color : (arguments[0] || this));
24 - }
24 + };
25
25
26 /*--------------------------------------------------------------------------*/
26 /*--------------------------------------------------------------------------*/
27
27
28 Element.collectTextNodes = function(element) {
28 Element.collectTextNodes = function(element) {
29 return $A($(element).childNodes).collect( function(node) {
29 return $A($(element).childNodes).collect( function(node) {
30 return (node.nodeType==3 ? node.nodeValue :
30 return (node.nodeType==3 ? node.nodeValue :
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
32 }).flatten().join('');
32 }).flatten().join('');
33 - }
33 + };
34
34
35 Element.collectTextNodesIgnoreClass = function(element, className) {
35 Element.collectTextNodesIgnoreClass = function(element, className) {
36 return $A($(element).childNodes).collect( function(node) {
36 return $A($(element).childNodes).collect( function(node) {
37 return (node.nodeType==3 ? node.nodeValue :
37 return (node.nodeType==3 ? node.nodeValue :
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
40 }).flatten().join('');
40 }).flatten().join('');
41 - }
41 + };
42
42
43 Element.setContentZoom = function(element, percent) {
43 Element.setContentZoom = function(element, percent) {
44 element = $(element);
44 element = $(element);
45 element.setStyle({fontSize: (percent/100) + 'em'});
45 element.setStyle({fontSize: (percent/100) + 'em'});
46 - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
46 + if (Prototype.Browser.WebKit) window.scrollBy(0,0);
47 return element;
47 return element;
48 - }
48 + };
49 -
50 - Element.getOpacity = function(element){
51 - element = $(element);
52 - var opacity;
53 - if (opacity = element.getStyle('opacity'))
54 - return parseFloat(opacity);
55 - if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
56 - if(opacity[1]) return parseFloat(opacity[1]) / 100;
57 - return 1.0;
58 - }
59 -
60 - Element.setOpacity = function(element, value){
61 - element= $(element);
62 - if (value == 1){
63 - element.setStyle({ opacity:
64 - (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
65 - 0.999999 : 1.0 });
66 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
67 - element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
68 - } else {
69 - if(value < 0.00001) value = 0;
70 - element.setStyle({opacity: value});
71 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
72 - element.setStyle(
73 - { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
74 - 'alpha(opacity='+value*100+')' });
75 - }
76 - return element;
77 - }
78
49
79 Element.getInlineOpacity = function(element){
50 Element.getInlineOpacity = function(element){
80 return $(element).style.opacity || '';
51 return $(element).style.opacity || '';
81 - }
52 + };
82
53
83 Element.forceRerendering = function(element) {
54 Element.forceRerendering = function(element) {
84 try {
55 try {
85 element = $(element);
56 element = $(element);
86 var n = document.createTextNode(' ');
57 var n = document.createTextNode(' ');
87 element.appendChild(n);
58 element.appendChild(n);
88 element.removeChild(n);
59 element.removeChild(n);
89 } catch(e) { }
60 } catch(e) { }
90 };
61 };
91
62
92 /*--------------------------------------------------------------------------*/
63 /*--------------------------------------------------------------------------*/
93
64
94 - Array.prototype.call = function() {
95 - var args = arguments;
96 - this.each(function(f){ f.apply(this, args) });
97 - }
98 -
99 - /*--------------------------------------------------------------------------*/
100 -
101 var Effect = {
65 var Effect = {
102 _elementDoesNotExistError: {
66 _elementDoesNotExistError: {
103 name: 'ElementDoesNotExistError',
67 name: 'ElementDoesNotExistError',
104 message: 'The specified DOM element does not exist, but is required for this effect to operate'
68 message: 'The specified DOM element does not exist, but is required for this effect to operate'
105 },
69 },
70 + Transitions: {
71 + linear: Prototype.K,
72 + sinoidal: function(pos) {
73 + return (-Math.cos(pos*Math.PI)/2) + 0.5;
74 + },
75 + reverse: function(pos) {
76 + return 1-pos;
77 + },
78 + flicker: function(pos) {
79 + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
80 + return pos > 1 ? 1 : pos;
81 + },
82 + wobble: function(pos) {
83 + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
84 + },
85 + pulse: function(pos, pulses) {
86 + pulses = pulses || 5;
87 + return (
88 + ((pos % (1/pulses)) * pulses).round() == 0 ?
89 + ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
90 + 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
91 + );
92 + },
93 + spring: function(pos) {
94 + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
95 + },
96 + none: function(pos) {
97 + return 0;
98 + },
99 + full: function(pos) {
100 + return 1;
101 + }
102 + },
103 + DefaultOptions: {
104 + duration: 1.0, // seconds
105 + fps: 100, // 100= assume 66fps max.
106 + sync: false, // true for combining
107 + from: 0.0,
108 + to: 1.0,
109 + delay: 0.0,
110 + queue: 'parallel'
111 + },
106 tagifyText: function(element) {
112 tagifyText: function(element) {
107 - if(typeof Builder == 'undefined')
108 - throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109 -
110 var tagifyStyle = 'position:relative';
113 var tagifyStyle = 'position:relative';
111 - if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
114 + if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
112
115
113 element = $(element);
116 element = $(element);
114 $A(element.childNodes).each( function(child) {
117 $A(element.childNodes).each( function(child) {
115 if(child.nodeType==3) {
118 if (child.nodeType==3) {
116 child.nodeValue.toArray().each( function(character) {
119 child.nodeValue.toArray().each( function(character) {
117 element.insertBefore(
120 element.insertBefore(
118 - Builder.node('span',{style: tagifyStyle},
121 + new Element('span', {style: tagifyStyle}).update(
119 character == ' ' ? String.fromCharCode(160) : character),
122 character == ' ' ? String.fromCharCode(160) : character),
120 child);
123 child);
121 });
124 });
122 Element.remove(child);
125 Element.remove(child);
123 }
126 }
124 });
127 });
125 },
128 },
126 multiple: function(element, effect) {
129 multiple: function(element, effect) {
127 var elements;
130 var elements;
128 if(((typeof element == 'object') ||
131 if (((typeof element == 'object') ||
129 - (typeof element == 'function')) &&
132 + Object.isFunction(element)) &&
130 (element.length))
133 (element.length))
131 elements = element;
134 elements = element;
132 else
135 else
133 elements = $(element).childNodes;
136 elements = $(element).childNodes;
134
137
135 var options = Object.extend({
138 var options = Object.extend({
136 speed: 0.1,
139 speed: 0.1,
137 delay: 0.0
140 delay: 0.0
138 }, arguments[2] || {});
141 }, arguments[2] || { });
139 var masterDelay = options.delay;
142 var masterDelay = options.delay;
140
143
141 $A(elements).each( function(element, index) {
144 $A(elements).each( function(element, index) {
142 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
145 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
143 });
146 });
144 },
147 },
145 PAIRS: {
148 PAIRS: {
146 'slide': ['SlideDown','SlideUp'],
149 'slide': ['SlideDown','SlideUp'],
147 'blind': ['BlindDown','BlindUp'],
150 'blind': ['BlindDown','BlindUp'],
148 'appear': ['Appear','Fade']
151 'appear': ['Appear','Fade']
149 },
152 },
150 toggle: function(element, effect) {
153 toggle: function(element, effect) {
151 element = $(element);
154 element = $(element);
152 effect = (effect || 'appear').toLowerCase();
155 effect = (effect || 'appear').toLowerCase();
153 var options = Object.extend({
156 var options = Object.extend({
154 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
157 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
155 }, arguments[2] || {});
158 }, arguments[2] || { });
156 Effect[element.visible() ?
159 Effect[element.visible() ?
157 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
160 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
158 }
161 }
159 };
162 };
160
163
161 - var Effect2 = Effect; // deprecated
164 + Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
162 -
163 - /* ------------- transitions ------------- */
164 -
165 - Effect.Transitions = {
166 - linear: Prototype.K,
167 - sinoidal: function(pos) {
168 - return (-Math.cos(pos*Math.PI)/2) + 0.5;
169 - },
170 - reverse: function(pos) {
171 - return 1-pos;
172 - },
173 - flicker: function(pos) {
174 - return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
175 - },
176 - wobble: function(pos) {
177 - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
178 - },
179 - pulse: function(pos, pulses) {
180 - pulses = pulses || 5;
181 - return (
182 - Math.round((pos % (1/pulses)) * pulses) == 0 ?
183 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
184 - 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
185 - );
186 - },
187 - none: function(pos) {
188 - return 0;
189 - },
190 - full: function(pos) {
191 - return 1;
192 - }
193 - };
194
165
195 /* ------------- core effects ------------- */
166 /* ------------- core effects ------------- */
196
167
197 - Effect.ScopedQueue = Class.create();
168 + Effect.ScopedQueue = Class.create(Enumerable, {
198 - Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
199 initialize: function() {
169 initialize: function() {
200 this.effects = [];
170 this.effects = [];
201 this.interval = null;
171 this.interval = null;
202 },
172 },
203 _each: function(iterator) {
173 _each: function(iterator) {
204 this.effects._each(iterator);
174 this.effects._each(iterator);
205 },
175 },
206 add: function(effect) {
176 add: function(effect) {
207 var timestamp = new Date().getTime();
177 var timestamp = new Date().getTime();
208
178
209 - var position = (typeof effect.options.queue == 'string') ?
179 + var position = Object.isString(effect.options.queue) ?
210 effect.options.queue : effect.options.queue.position;
180 effect.options.queue : effect.options.queue.position;
211
181
212 switch(position) {
182 switch(position) {
213 case 'front':
183 case 'front':
214 // move unstarted effects after this effect
184 // move unstarted effects after this effect
215 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
185 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
216 e.startOn += effect.finishOn;
186 e.startOn += effect.finishOn;
217 e.finishOn += effect.finishOn;
187 e.finishOn += effect.finishOn;
218 });
188 });
219 break;
189 break;
220 case 'with-last':
190 case 'with-last':
221 timestamp = this.effects.pluck('startOn').max() || timestamp;
191 timestamp = this.effects.pluck('startOn').max() || timestamp;
222 break;
192 break;
223 case 'end':
193 case 'end':
224 // start effect after last queued effect has finished
194 // start effect after last queued effect has finished
225 timestamp = this.effects.pluck('finishOn').max() || timestamp;
195 timestamp = this.effects.pluck('finishOn').max() || timestamp;
226 break;
196 break;
227 }
197 }
228
198
229 effect.startOn += timestamp;
199 effect.startOn += timestamp;
230 effect.finishOn += timestamp;
200 effect.finishOn += timestamp;
231
201
232 if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
202 if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
233 this.effects.push(effect);
203 this.effects.push(effect);
234
204
235 if(!this.interval)
205 if (!this.interval)
236 - this.interval = setInterval(this.loop.bind(this), 40);
206 + this.interval = setInterval(this.loop.bind(this), 15);
237 },
207 },
238 remove: function(effect) {
208 remove: function(effect) {
239 this.effects = this.effects.reject(function(e) { return e==effect });
209 this.effects = this.effects.reject(function(e) { return e==effect });
240 if(this.effects.length == 0) {
210 if (this.effects.length == 0) {
241 clearInterval(this.interval);
211 clearInterval(this.interval);
242 this.interval = null;
212 this.interval = null;
243 }
213 }
244 },
214 },
245 loop: function() {
215 loop: function() {
246 var timePos = new Date().getTime();
216 var timePos = new Date().getTime();
247 - this.effects.invoke('loop', timePos);
217 + for(var i=0, len=this.effects.length;i<len;i++)
218 + this.effects[i] && this.effects[i].loop(timePos);
248 }
219 }
249 });
220 });
250
221
251 Effect.Queues = {
222 Effect.Queues = {
252 instances: $H(),
223 instances: $H(),
253 get: function(queueName) {
224 get: function(queueName) {
254 - if(typeof queueName != 'string') return queueName;
225 + if (!Object.isString(queueName)) return queueName;
255
226
256 - if(!this.instances[queueName])
227 + return this.instances.get(queueName) ||
257 - this.instances[queueName] = new Effect.ScopedQueue();
228 + this.instances.set(queueName, new Effect.ScopedQueue());
258 -
259 - return this.instances[queueName];
260 }
229 }
261 - }
230 + };
262 Effect.Queue = Effect.Queues.get('global');
231 Effect.Queue = Effect.Queues.get('global');
263
232
264 - Effect.DefaultOptions = {
233 + Effect.Base = Class.create({
265 - transition: Effect.Transitions.sinoidal,
266 - duration: 1.0, // seconds
267 - fps: 25.0, // max. 25fps due to Effect.Queue implementation
268 - sync: false, // true for combining
269 - from: 0.0,
270 - to: 1.0,
271 - delay: 0.0,
272 - queue: 'parallel'
273 - }
274 -
275 - Effect.Base = function() {};
276 - Effect.Base.prototype = {
277 position: null,
234 position: null,
278 start: function(options) {
235 start: function(options) {
236 + function codeForEvent(options,eventName){
237 + return (
238 + (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
239 + (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
240 + );
241 + }
242 + if (options && options.transition === false) options.transition = Effect.Transitions.linear;
279 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
243 this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
280 this.currentFrame = 0;
244 this.currentFrame = 0;
281 this.state = 'idle';
245 this.state = 'idle';
282 this.startOn = this.options.delay*1000;
246 this.startOn = this.options.delay*1000;
283 this.finishOn = this.startOn + (this.options.duration*1000);
247 this.finishOn = this.startOn+(this.options.duration*1000);
248 + this.fromToDelta = this.options.to-this.options.from;
249 + this.totalTime = this.finishOn-this.startOn;
250 + this.totalFrames = this.options.fps*this.options.duration;
251 +
252 + eval('this.render = function(pos){ '+
253 + 'if (this.state=="idle"){this.state="running";'+
254 + codeForEvent(this.options,'beforeSetup')+
255 + (this.setup ? 'this.setup();':'')+
256 + codeForEvent(this.options,'afterSetup')+
257 + '};if (this.state=="running"){'+
258 + 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
259 + 'this.position=pos;'+
260 + codeForEvent(this.options,'beforeUpdate')+
261 + (this.update ? 'this.update(pos);':'')+
262 + codeForEvent(this.options,'afterUpdate')+
263 + '}}');
264 +
284 this.event('beforeStart');
265 this.event('beforeStart');
285 if(!this.options.sync)
266 if (!this.options.sync)
286 - Effect.Queues.get(typeof this.options.queue == 'string' ?
267 + Effect.Queues.get(Object.isString(this.options.queue) ?
287 'global' : this.options.queue.scope).add(this);
268 'global' : this.options.queue.scope).add(this);
288 },
269 },
289 loop: function(timePos) {
270 loop: function(timePos) {
290 if(timePos >= this.startOn) {
271 if (timePos >= this.startOn) {
291 if(timePos >= this.finishOn) {
272 if (timePos >= this.finishOn) {
292 this.render(1.0);
273 this.render(1.0);
293 this.cancel();
274 this.cancel();
294 this.event('beforeFinish');
275 this.event('beforeFinish');
295 if(this.finish) this.finish();
276 if (this.finish) this.finish();
296 this.event('afterFinish');
277 this.event('afterFinish');
297 return;
278 return;
298 }
279 }
299 - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
280 + var pos = (timePos - this.startOn) / this.totalTime,
300 - var frame = Math.round(pos * this.options.fps * this.options.duration);
281 + frame = (pos * this.totalFrames).round();
301 if(frame > this.currentFrame) {
282 if (frame > this.currentFrame) {
302 this.render(pos);
283 this.render(pos);
303 this.currentFrame = frame;
284 this.currentFrame = frame;
304 }
285 }
305 }
286 }
306 },
287 },
307 - render: function(pos) {
308 - if(this.state == 'idle') {
309 - this.state = 'running';
310 - this.event('beforeSetup');
311 - if(this.setup) this.setup();
312 - this.event('afterSetup');
313 - }
314 - if(this.state == 'running') {
315 - if(this.options.transition) pos = this.options.transition(pos);
316 - pos *= (this.options.to-this.options.from);
317 - pos += this.options.from;
318 - this.position = pos;
319 - this.event('beforeUpdate');
320 - if(this.update) this.update(pos);
321 - this.event('afterUpdate');
322 - }
323 - },
324 cancel: function() {
288 cancel: function() {
325 if(!this.options.sync)
289 if (!this.options.sync)
326 - Effect.Queues.get(typeof this.options.queue == 'string' ?
290 + Effect.Queues.get(Object.isString(this.options.queue) ?
327 'global' : this.options.queue.scope).remove(this);
291 'global' : this.options.queue.scope).remove(this);
328 this.state = 'finished';
292 this.state = 'finished';
329 },
293 },
330 event: function(eventName) {
294 event: function(eventName) {
331 if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
295 if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
332 if(this.options[eventName]) this.options[eventName](this);
296 if (this.options[eventName]) this.options[eventName](this);
333 },
297 },
334 inspect: function() {
298 inspect: function() {
335 - return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
299 + var data = $H();
336 - }
300 + for(property in this)
301 + if (!Object.isFunction(this[property])) data.set(property, this[property]);
302 + return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
337 }
303 }
304 + });
338
305
339 - Effect.Parallel = Class.create();
306 + Effect.Parallel = Class.create(Effect.Base, {
340 - Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
341 initialize: function(effects) {
307 initialize: function(effects) {
342 this.effects = effects || [];
308 this.effects = effects || [];
343 this.start(arguments[1]);
309 this.start(arguments[1]);
344 },
310 },
345 update: function(position) {
311 update: function(position) {
346 this.effects.invoke('render', position);
312 this.effects.invoke('render', position);
347 },
313 },
348 finish: function(position) {
314 finish: function(position) {
349 this.effects.each( function(effect) {
315 this.effects.each( function(effect) {
350 effect.render(1.0);
316 effect.render(1.0);
351 effect.cancel();
317 effect.cancel();
352 effect.event('beforeFinish');
318 effect.event('beforeFinish');
353 if(effect.finish) effect.finish(position);
319 if (effect.finish) effect.finish(position);
354 effect.event('afterFinish');
320 effect.event('afterFinish');
355 });
321 });
356 }
322 }
357 });
323 });
358
324
359 - Effect.Event = Class.create();
325 + Effect.Tween = Class.create(Effect.Base, {
360 - Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
326 + initialize: function(object, from, to) {
327 + object = Object.isString(object) ? $(object) : object;
328 + var args = $A(arguments), method = args.last(),
329 + options = args.length == 5 ? args[3] : null;
330 + this.method = Object.isFunction(method) ? method.bind(object) :
331 + Object.isFunction(object[method]) ? object[method].bind(object) :
332 + function(value) { object[method] = value };
333 + this.start(Object.extend({ from: from, to: to }, options || { }));
334 + },
335 + update: function(position) {
336 + this.method(position);
337 + }
338 + });
339 +
340 + Effect.Event = Class.create(Effect.Base, {
361 initialize: function() {
341 initialize: function() {
362 - var options = Object.extend({
342 + this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
363 - duration: 0
364 - }, arguments[0] || {});
365 - this.start(options);
366 },
343 },
367 update: Prototype.emptyFunction
344 update: Prototype.emptyFunction
368 });
345 });
369
346
370 - Effect.Opacity = Class.create();
347 + Effect.Opacity = Class.create(Effect.Base, {
371 - Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
372 initialize: function(element) {
348 initialize: function(element) {
373 this.element = $(element);
349 this.element = $(element);
374 if(!this.element) throw(Effect._elementDoesNotExistError);
350 if (!this.element) throw(Effect._elementDoesNotExistError);
375 // make this work on IE on elements without 'layout'
351 // make this work on IE on elements without 'layout'
376 - if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
352 + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
377 this.element.setStyle({zoom: 1});
353 this.element.setStyle({zoom: 1});
378 var options = Object.extend({
354 var options = Object.extend({
379 from: this.element.getOpacity() || 0.0,
355 from: this.element.getOpacity() || 0.0,
380 to: 1.0
356 to: 1.0
381 }, arguments[1] || {});
357 }, arguments[1] || { });
382 this.start(options);
358 this.start(options);
383 },
359 },
384 update: function(position) {
360 update: function(position) {
385 this.element.setOpacity(position);
361 this.element.setOpacity(position);
386 }
362 }
387 });
363 });
388
364
389 - Effect.Move = Class.create();
365 + Effect.Move = Class.create(Effect.Base, {
390 - Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
391 initialize: function(element) {
366 initialize: function(element) {
392 this.element = $(element);
367 this.element = $(element);
393 if(!this.element) throw(Effect._elementDoesNotExistError);
368 if (!this.element) throw(Effect._elementDoesNotExistError);
394 var options = Object.extend({
369 var options = Object.extend({
395 x: 0,
370 x: 0,
396 y: 0,
371 y: 0,
397 mode: 'relative'
372 mode: 'relative'
398 }, arguments[1] || {});
373 }, arguments[1] || { });
399 this.start(options);
374 this.start(options);
400 },
375 },
401 setup: function() {
376 setup: function() {
402 - // Bug in Opera: Opera returns the "real" position of a static element or
403 - // relative element that does not have top/left explicitly set.
404 - // ==> Always set top and left for position relative elements in your stylesheets
405 - // (to 0 if you do not need them)
406 this.element.makePositioned();
377 this.element.makePositioned();
407 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
378 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
408 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
379 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
409 if(this.options.mode == 'absolute') {
380 if (this.options.mode == 'absolute') {
410 - // absolute movement, so we need to calc deltaX and deltaY
411 this.options.x = this.options.x - this.originalLeft;
381 this.options.x = this.options.x - this.originalLeft;
412 this.options.y = this.options.y - this.originalTop;
382 this.options.y = this.options.y - this.originalTop;
413 }
383 }
414 },
384 },
415 update: function(position) {
385 update: function(position) {
416 this.element.setStyle({
386 this.element.setStyle({
417 - left: Math.round(this.options.x * position + this.originalLeft) + 'px',
387 + left: (this.options.x * position + this.originalLeft).round() + 'px',
418 - top: Math.round(this.options.y * position + this.originalTop) + 'px'
388 + top: (this.options.y * position + this.originalTop).round() + 'px'
419 });
389 });
420 }
390 }
421 });
391 });
422
392
423 // for backwards compatibility
393 // for backwards compatibility
424 Effect.MoveBy = function(element, toTop, toLeft) {
394 Effect.MoveBy = function(element, toTop, toLeft) {
425 return new Effect.Move(element,
395 return new Effect.Move(element,
426 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
396 Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
427 };
397 };
428
398
429 - Effect.Scale = Class.create();
399 + Effect.Scale = Class.create(Effect.Base, {
430 - Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
431 initialize: function(element, percent) {
400 initialize: function(element, percent) {
432 this.element = $(element);
401 this.element = $(element);
433 if(!this.element) throw(Effect._elementDoesNotExistError);
402 if (!this.element) throw(Effect._elementDoesNotExistError);
434 var options = Object.extend({
403 var options = Object.extend({
435 scaleX: true,
404 scaleX: true,
436 scaleY: true,
405 scaleY: true,
437 scaleContent: true,
406 scaleContent: true,
438 scaleFromCenter: false,
407 scaleFromCenter: false,
439 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
408 scaleMode: 'box', // 'box' or 'contents' or { } with provided values
440 scaleFrom: 100.0,
409 scaleFrom: 100.0,
441 scaleTo: percent
410 scaleTo: percent
442 }, arguments[2] || {});
411 }, arguments[2] || { });
443 this.start(options);
412 this.start(options);
444 },
413 },
445 setup: function() {
414 setup: function() {
446 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
415 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
447 this.elementPositioning = this.element.getStyle('position');
416 this.elementPositioning = this.element.getStyle('position');
448
417
449 this.originalStyle = {};
418 this.originalStyle = { };
450 ['top','left','width','height','fontSize'].each( function(k) {
419 ['top','left','width','height','fontSize'].each( function(k) {
451 this.originalStyle[k] = this.element.style[k];
420 this.originalStyle[k] = this.element.style[k];
452 }.bind(this));
421 }.bind(this));
453
422
454 this.originalTop = this.element.offsetTop;
423 this.originalTop = this.element.offsetTop;
455 this.originalLeft = this.element.offsetLeft;
424 this.originalLeft = this.element.offsetLeft;
456
425
457 var fontSize = this.element.getStyle('font-size') || '100%';
426 var fontSize = this.element.getStyle('font-size') || '100%';
458 ['em','px','%','pt'].each( function(fontSizeType) {
427 ['em','px','%','pt'].each( function(fontSizeType) {
459 if(fontSize.indexOf(fontSizeType)>0) {
428 if (fontSize.indexOf(fontSizeType)>0) {
460 this.fontSize = parseFloat(fontSize);
429 this.fontSize = parseFloat(fontSize);
461 this.fontSizeType = fontSizeType;
430 this.fontSizeType = fontSizeType;
462 }
431 }
463 }.bind(this));
432 }.bind(this));
464
433
465 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
434 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
466
435
467 this.dims = null;
436 this.dims = null;
468 if(this.options.scaleMode=='box')
437 if (this.options.scaleMode=='box')
469 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
438 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
470 if(/^content/.test(this.options.scaleMode))
439 if (/^content/.test(this.options.scaleMode))
471 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
440 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
472 if(!this.dims)
441 if (!this.dims)
473 this.dims = [this.options.scaleMode.originalHeight,
442 this.dims = [this.options.scaleMode.originalHeight,
474 this.options.scaleMode.originalWidth];
443 this.options.scaleMode.originalWidth];
475 },
444 },
476 update: function(position) {
445 update: function(position) {
477 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
446 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
478 if(this.options.scaleContent && this.fontSize)
447 if (this.options.scaleContent && this.fontSize)
479 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
448 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
480 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
449 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
481 },
450 },
482 finish: function(position) {
451 finish: function(position) {
483 if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
452 if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
484 },
453 },
485 setDimensions: function(height, width) {
454 setDimensions: function(height, width) {
486 var d = {};
455 var d = { };
487 - if(this.options.scaleX) d.width = Math.round(width) + 'px';
456 + if (this.options.scaleX) d.width = width.round() + 'px';
488 - if(this.options.scaleY) d.height = Math.round(height) + 'px';
457 + if (this.options.scaleY) d.height = height.round() + 'px';
489 if(this.options.scaleFromCenter) {
458 if (this.options.scaleFromCenter) {
490 var topd = (height - this.dims[0])/2;
459 var topd = (height - this.dims[0])/2;
491 var leftd = (width - this.dims[1])/2;
460 var leftd = (width - this.dims[1])/2;
492 if(this.elementPositioning == 'absolute') {
461 if (this.elementPositioning == 'absolute') {
493 if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
462 if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
494 if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
463 if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
495 } else {
464 } else {
496 if(this.options.scaleY) d.top = -topd + 'px';
465 if (this.options.scaleY) d.top = -topd + 'px';
497 if(this.options.scaleX) d.left = -leftd + 'px';
466 if (this.options.scaleX) d.left = -leftd + 'px';
498 }
467 }
499 }
468 }
500 this.element.setStyle(d);
469 this.element.setStyle(d);
501 }
470 }
502 });
471 });
503
472
504 - Effect.Highlight = Class.create();
473 + Effect.Highlight = Class.create(Effect.Base, {
505 - Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
506 initialize: function(element) {
474 initialize: function(element) {
507 this.element = $(element);
475 this.element = $(element);
508 if(!this.element) throw(Effect._elementDoesNotExistError);
476 if (!this.element) throw(Effect._elementDoesNotExistError);
509 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
477 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
510 this.start(options);
478 this.start(options);
511 },
479 },
512 setup: function() {
480 setup: function() {
513 // Prevent executing on elements not in the layout flow
481 // Prevent executing on elements not in the layout flow
514 if(this.element.getStyle('display')=='none') { this.cancel(); return; }
482 if (this.element.getStyle('display')=='none') { this.cancel(); return; }
515 // Disable background image during the effect
483 // Disable background image during the effect
516 - this.oldStyle = {
484 + this.oldStyle = { };
517 - backgroundImage: this.element.getStyle('background-image') };
485 + if (!this.options.keepBackgroundImage) {
486 + this.oldStyle.backgroundImage = this.element.getStyle('background-image');
518 this.element.setStyle({backgroundImage: 'none'});
487 this.element.setStyle({backgroundImage: 'none'});
488 + }
519 if(!this.options.endcolor)
489 if (!this.options.endcolor)
520 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
490 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
521 if(!this.options.restorecolor)
491 if (!this.options.restorecolor)
522 this.options.restorecolor = this.element.getStyle('background-color');
492 this.options.restorecolor = this.element.getStyle('background-color');
523 // init color calculations
493 // init color calculations
524 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
494 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
525 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
495 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
526 },
496 },
527 update: function(position) {
497 update: function(position) {
528 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
498 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
529 - return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
499 + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
530 },
500 },
531 finish: function() {
501 finish: function() {
532 this.element.setStyle(Object.extend(this.oldStyle, {
502 this.element.setStyle(Object.extend(this.oldStyle, {
533 backgroundColor: this.options.restorecolor
503 backgroundColor: this.options.restorecolor
534 }));
504 }));
535 }
505 }
536 });
506 });
537
507
538 - Effect.ScrollTo = Class.create();
508 + Effect.ScrollTo = function(element) {
539 - Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
509 + var options = arguments[1] || { },
540 - initialize: function(element) {
510 + scrollOffsets = document.viewport.getScrollOffsets(),
541 - this.element = $(element);
511 + elementOffsets = $(element).cumulativeOffset(),
542 - this.start(arguments[1] || {});
512 + max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
543 - },
513 +
544 - setup: function() {
514 + if (options.offset) elementOffsets[1] += options.offset;
545 - Position.prepare();
515 +
546 - var offsets = Position.cumulativeOffset(this.element);
516 + return new Effect.Tween(null,
547 - if(this.options.offset) offsets[1] += this.options.offset;
517 + scrollOffsets.top,
548 - var max = window.innerHeight ?
518 + elementOffsets[1] > max ? max : elementOffsets[1],
549 - window.height - window.innerHeight :
519 + options,
550 - document.body.scrollHeight -
520 + function(p){ scrollTo(scrollOffsets.left, p.round()) }
551 - (document.documentElement.clientHeight ?
521 + );
552 - document.documentElement.clientHeight : document.body.clientHeight);
522 + };
553 - this.scrollStart = Position.deltaY;
554 - this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
555 - },
556 - update: function(position) {
557 - Position.prepare();
558 - window.scrollTo(Position.deltaX,
559 - this.scrollStart + (position*this.delta));
560 - }
561 - });
562
523
563 /* ------------- combination effects ------------- */
524 /* ------------- combination effects ------------- */
564
525
565 Effect.Fade = function(element) {
526 Effect.Fade = function(element) {
566 element = $(element);
527 element = $(element);
567 var oldOpacity = element.getInlineOpacity();
528 var oldOpacity = element.getInlineOpacity();
568 var options = Object.extend({
529 var options = Object.extend({
569 from: element.getOpacity() || 1.0,
530 from: element.getOpacity() || 1.0,
570 to: 0.0,
531 to: 0.0,
571 afterFinishInternal: function(effect) {
532 afterFinishInternal: function(effect) {
572 if(effect.options.to!=0) return;
533 if (effect.options.to!=0) return;
573 effect.element.hide().setStyle({opacity: oldOpacity});
534 effect.element.hide().setStyle({opacity: oldOpacity});
574 - }}, arguments[1] || {});
535 + }
536 + }, arguments[1] || { });
575 return new Effect.Opacity(element,options);
537 return new Effect.Opacity(element,options);
576 - }
538 + };
577
539
578 Effect.Appear = function(element) {
540 Effect.Appear = function(element) {
579 element = $(element);
541 element = $(element);
580 var options = Object.extend({
542 var options = Object.extend({
581 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
543 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
582 to: 1.0,
544 to: 1.0,
583 // force Safari to render floated elements properly
545 // force Safari to render floated elements properly
584 afterFinishInternal: function(effect) {
546 afterFinishInternal: function(effect) {
585 effect.element.forceRerendering();
547 effect.element.forceRerendering();
586 },
548 },
587 beforeSetup: function(effect) {
549 beforeSetup: function(effect) {
588 effect.element.setOpacity(effect.options.from).show();
550 effect.element.setOpacity(effect.options.from).show();
589 }}, arguments[1] || {});
551 }}, arguments[1] || { });
590 return new Effect.Opacity(element,options);
552 return new Effect.Opacity(element,options);
591 - }
553 + };
592
554
593 Effect.Puff = function(element) {
555 Effect.Puff = function(element) {
594 element = $(element);
556 element = $(element);
595 var oldStyle = {
557 var oldStyle = {
596 opacity: element.getInlineOpacity(),
558 opacity: element.getInlineOpacity(),
597 position: element.getStyle('position'),
559 position: element.getStyle('position'),
598 top: element.style.top,
560 top: element.style.top,
599 left: element.style.left,
561 left: element.style.left,
600 width: element.style.width,
562 width: element.style.width,
601 height: element.style.height
563 height: element.style.height
602 };
564 };
603 return new Effect.Parallel(
565 return new Effect.Parallel(
604 [ new Effect.Scale(element, 200,
566 [ new Effect.Scale(element, 200,
605 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
567 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
606 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
568 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
607 Object.extend({ duration: 1.0,
569 Object.extend({ duration: 1.0,
608 beforeSetupInternal: function(effect) {
570 beforeSetupInternal: function(effect) {
609 Position.absolutize(effect.effects[0].element)
571 Position.absolutize(effect.effects[0].element)
610 },
572 },
611 afterFinishInternal: function(effect) {
573 afterFinishInternal: function(effect) {
612 effect.effects[0].element.hide().setStyle(oldStyle); }
574 effect.effects[0].element.hide().setStyle(oldStyle); }
613 }, arguments[1] || {})
575 }, arguments[1] || { })
614 );
576 );
615 - }
577 + };
616
578
617 Effect.BlindUp = function(element) {
579 Effect.BlindUp = function(element) {
618 element = $(element);
580 element = $(element);
619 element.makeClipping();
581 element.makeClipping();
620 return new Effect.Scale(element, 0,
582 return new Effect.Scale(element, 0,
621 Object.extend({ scaleContent: false,
583 Object.extend({ scaleContent: false,
622 scaleX: false,
584 scaleX: false,
623 restoreAfterFinish: true,
585 restoreAfterFinish: true,
624 afterFinishInternal: function(effect) {
586 afterFinishInternal: function(effect) {
625 effect.element.hide().undoClipping();
587 effect.element.hide().undoClipping();
626 }
588 }
627 }, arguments[1] || {})
589 }, arguments[1] || { })
628 );
590 );
629 - }
591 + };
630
592
631 Effect.BlindDown = function(element) {
593 Effect.BlindDown = function(element) {
632 element = $(element);
594 element = $(element);
633 var elementDimensions = element.getDimensions();
595 var elementDimensions = element.getDimensions();
634 return new Effect.Scale(element, 100, Object.extend({
596 return new Effect.Scale(element, 100, Object.extend({
635 scaleContent: false,
597 scaleContent: false,
636 scaleX: false,
598 scaleX: false,
637 scaleFrom: 0,
599 scaleFrom: 0,
638 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
600 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
639 restoreAfterFinish: true,
601 restoreAfterFinish: true,
640 afterSetup: function(effect) {
602 afterSetup: function(effect) {
641 effect.element.makeClipping().setStyle({height: '0px'}).show();
603 effect.element.makeClipping().setStyle({height: '0px'}).show();
642 },
604 },
643 afterFinishInternal: function(effect) {
605 afterFinishInternal: function(effect) {
644 effect.element.undoClipping();
606 effect.element.undoClipping();
645 }
607 }
646 }, arguments[1] || {}));
608 }, arguments[1] || { }));
647 - }
609 + };
648
610
649 Effect.SwitchOff = function(element) {
611 Effect.SwitchOff = function(element) {
650 element = $(element);
612 element = $(element);
651 var oldOpacity = element.getInlineOpacity();
613 var oldOpacity = element.getInlineOpacity();
652 return new Effect.Appear(element, Object.extend({
614 return new Effect.Appear(element, Object.extend({
653 duration: 0.4,
615 duration: 0.4,
654 from: 0,
616 from: 0,
655 transition: Effect.Transitions.flicker,
617 transition: Effect.Transitions.flicker,
656 afterFinishInternal: function(effect) {
618 afterFinishInternal: function(effect) {
657 new Effect.Scale(effect.element, 1, {
619 new Effect.Scale(effect.element, 1, {
658 duration: 0.3, scaleFromCenter: true,
620 duration: 0.3, scaleFromCenter: true,
659 scaleX: false, scaleContent: false, restoreAfterFinish: true,
621 scaleX: false, scaleContent: false, restoreAfterFinish: true,
660 beforeSetup: function(effect) {
622 beforeSetup: function(effect) {
661 effect.element.makePositioned().makeClipping();
623 effect.element.makePositioned().makeClipping();
662 },
624 },
663 afterFinishInternal: function(effect) {
625 afterFinishInternal: function(effect) {
664 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
626 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
665 }
627 }
666 })
628 })
667 }
629 }
668 }, arguments[1] || {}));
630 }, arguments[1] || { }));
669 - }
631 + };
670
632
671 Effect.DropOut = function(element) {
633 Effect.DropOut = function(element) {
672 element = $(element);
634 element = $(element);
673 var oldStyle = {
635 var oldStyle = {
674 top: element.getStyle('top'),
636 top: element.getStyle('top'),
675 left: element.getStyle('left'),
637 left: element.getStyle('left'),
676 opacity: element.getInlineOpacity() };
638 opacity: element.getInlineOpacity() };
677 return new Effect.Parallel(
639 return new Effect.Parallel(
678 [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
640 [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
679 new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
641 new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
680 Object.extend(
642 Object.extend(
681 { duration: 0.5,
643 { duration: 0.5,
682 beforeSetup: function(effect) {
644 beforeSetup: function(effect) {
683 effect.effects[0].element.makePositioned();
645 effect.effects[0].element.makePositioned();
684 },
646 },
685 afterFinishInternal: function(effect) {
647 afterFinishInternal: function(effect) {
686 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
648 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
687 }
649 }
688 }, arguments[1] || {}));
650 }, arguments[1] || { }));
689 - }
651 + };
690
652
691 Effect.Shake = function(element) {
653 Effect.Shake = function(element) {
692 element = $(element);
654 element = $(element);
655 + var options = Object.extend({
656 + distance: 20,
657 + duration: 0.5
658 + }, arguments[1] || {});
659 + var distance = parseFloat(options.distance);
660 + var split = parseFloat(options.duration) / 10.0;
693 var oldStyle = {
661 var oldStyle = {
694 top: element.getStyle('top'),
662 top: element.getStyle('top'),
695 left: element.getStyle('left') };
663 left: element.getStyle('left') };
696 return new Effect.Move(element,
664 return new Effect.Move(element,
697 - { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
665 + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
698 new Effect.Move(effect.element,
666 new Effect.Move(effect.element,
699 - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
667 + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
700 new Effect.Move(effect.element,
668 new Effect.Move(effect.element,
701 - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
669 + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
702 new Effect.Move(effect.element,
670 new Effect.Move(effect.element,
703 - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
671 + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
704 new Effect.Move(effect.element,
672 new Effect.Move(effect.element,
705 - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
673 + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
706 new Effect.Move(effect.element,
674 new Effect.Move(effect.element,
707 - { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
675 + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
708 effect.element.undoPositioned().setStyle(oldStyle);
676 effect.element.undoPositioned().setStyle(oldStyle);
709 }}) }}) }}) }}) }}) }});
677 }}) }}) }}) }}) }}) }});
710 - }
678 + };
711
679
712 Effect.SlideDown = function(element) {
680 Effect.SlideDown = function(element) {
713 element = $(element).cleanWhitespace();
681 element = $(element).cleanWhitespace();
714 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
682 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
715 var oldInnerBottom = element.down().getStyle('bottom');
683 var oldInnerBottom = element.down().getStyle('bottom');
716 var elementDimensions = element.getDimensions();
684 var elementDimensions = element.getDimensions();
717 return new Effect.Scale(element, 100, Object.extend({
685 return new Effect.Scale(element, 100, Object.extend({
718 scaleContent: false,
686 scaleContent: false,
719 scaleX: false,
687 scaleX: false,
720 scaleFrom: window.opera ? 0 : 1,
688 scaleFrom: window.opera ? 0 : 1,
721 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
689 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
722 restoreAfterFinish: true,
690 restoreAfterFinish: true,
723 afterSetup: function(effect) {
691 afterSetup: function(effect) {
724 effect.element.makePositioned();
692 effect.element.makePositioned();
725 effect.element.down().makePositioned();
693 effect.element.down().makePositioned();
726 if(window.opera) effect.element.setStyle({top: ''});
694 if (window.opera) effect.element.setStyle({top: ''});
727 effect.element.makeClipping().setStyle({height: '0px'}).show();
695 effect.element.makeClipping().setStyle({height: '0px'}).show();
728 },
696 },
729 afterUpdateInternal: function(effect) {
697 afterUpdateInternal: function(effect) {
730 effect.element.down().setStyle({bottom:
698 effect.element.down().setStyle({bottom:
731 (effect.dims[0] - effect.element.clientHeight) + 'px' });
699 (effect.dims[0] - effect.element.clientHeight) + 'px' });
732 },
700 },
733 afterFinishInternal: function(effect) {
701 afterFinishInternal: function(effect) {
734 effect.element.undoClipping().undoPositioned();
702 effect.element.undoClipping().undoPositioned();
735 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
703 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
736 }, arguments[1] || {})
704 }, arguments[1] || { })
737 );
705 );
738 - }
706 + };
739
707
740 Effect.SlideUp = function(element) {
708 Effect.SlideUp = function(element) {
741 element = $(element).cleanWhitespace();
709 element = $(element).cleanWhitespace();
742 var oldInnerBottom = element.down().getStyle('bottom');
710 var oldInnerBottom = element.down().getStyle('bottom');
711 + var elementDimensions = element.getDimensions();
743 return new Effect.Scale(element, window.opera ? 0 : 1,
712 return new Effect.Scale(element, window.opera ? 0 : 1,
744 Object.extend({ scaleContent: false,
713 Object.extend({ scaleContent: false,
745 scaleX: false,
714 scaleX: false,
746 scaleMode: 'box',
715 scaleMode: 'box',
747 scaleFrom: 100,
716 scaleFrom: 100,
717 + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
748 restoreAfterFinish: true,
718 restoreAfterFinish: true,
749 - beforeStartInternal: function(effect) {
719 + afterSetup: function(effect) {
750 effect.element.makePositioned();
720 effect.element.makePositioned();
751 effect.element.down().makePositioned();
721 effect.element.down().makePositioned();
752 if(window.opera) effect.element.setStyle({top: ''});
722 if (window.opera) effect.element.setStyle({top: ''});
753 effect.element.makeClipping().show();
723 effect.element.makeClipping().show();
754 },
724 },
755 afterUpdateInternal: function(effect) {
725 afterUpdateInternal: function(effect) {
756 effect.element.down().setStyle({bottom:
726 effect.element.down().setStyle({bottom:
757 (effect.dims[0] - effect.element.clientHeight) + 'px' });
727 (effect.dims[0] - effect.element.clientHeight) + 'px' });
758 },
728 },
759 afterFinishInternal: function(effect) {
729 afterFinishInternal: function(effect) {
760 - effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
730 + effect.element.hide().undoClipping().undoPositioned();
761 - effect.element.down().undoPositioned();
731 + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
762 }
732 }
763 }, arguments[1] || {})
733 }, arguments[1] || { })
764 );
734 );
765 - }
735 + };
766
736
767 // Bug in opera makes the TD containing this element expand for a instance after finish
737 // Bug in opera makes the TD containing this element expand for a instance after finish
768 Effect.Squish = function(element) {
738 Effect.Squish = function(element) {
769 return new Effect.Scale(element, window.opera ? 1 : 0, {
739 return new Effect.Scale(element, window.opera ? 1 : 0, {
770 restoreAfterFinish: true,
740 restoreAfterFinish: true,
771 beforeSetup: function(effect) {
741 beforeSetup: function(effect) {
772 effect.element.makeClipping();
742 effect.element.makeClipping();
773 },
743 },
774 afterFinishInternal: function(effect) {
744 afterFinishInternal: function(effect) {
775 effect.element.hide().undoClipping();
745 effect.element.hide().undoClipping();
776 }
746 }
777 });
747 });
778 - }
748 + };
779
749
780 Effect.Grow = function(element) {
750 Effect.Grow = function(element) {
781 element = $(element);
751 element = $(element);
782 var options = Object.extend({
752 var options = Object.extend({
783 direction: 'center',
753 direction: 'center',
784 moveTransition: Effect.Transitions.sinoidal,
754 moveTransition: Effect.Transitions.sinoidal,
785 scaleTransition: Effect.Transitions.sinoidal,
755 scaleTransition: Effect.Transitions.sinoidal,
786 opacityTransition: Effect.Transitions.full
756 opacityTransition: Effect.Transitions.full
787 }, arguments[1] || {});
757 }, arguments[1] || { });
788 var oldStyle = {
758 var oldStyle = {
789 top: element.style.top,
759 top: element.style.top,
790 left: element.style.left,
760 left: element.style.left,
791 height: element.style.height,
761 height: element.style.height,
792 width: element.style.width,
762 width: element.style.width,
793 opacity: element.getInlineOpacity() };
763 opacity: element.getInlineOpacity() };
794
764
795 var dims = element.getDimensions();
765 var dims = element.getDimensions();
796 var initialMoveX, initialMoveY;
766 var initialMoveX, initialMoveY;
797 var moveX, moveY;
767 var moveX, moveY;
798
768
799 switch (options.direction) {
769 switch (options.direction) {
800 case 'top-left':
770 case 'top-left':
801 initialMoveX = initialMoveY = moveX = moveY = 0;
771 initialMoveX = initialMoveY = moveX = moveY = 0;
802 break;
772 break;
803 case 'top-right':
773 case 'top-right':
804 initialMoveX = dims.width;
774 initialMoveX = dims.width;
805 initialMoveY = moveY = 0;
775 initialMoveY = moveY = 0;
806 moveX = -dims.width;
776 moveX = -dims.width;
807 break;
777 break;
808 case 'bottom-left':
778 case 'bottom-left':
809 initialMoveX = moveX = 0;
779 initialMoveX = moveX = 0;
810 initialMoveY = dims.height;
780 initialMoveY = dims.height;
811 moveY = -dims.height;
781 moveY = -dims.height;
812 break;
782 break;
813 case 'bottom-right':
783 case 'bottom-right':
814 initialMoveX = dims.width;
784 initialMoveX = dims.width;
815 initialMoveY = dims.height;
785 initialMoveY = dims.height;
816 moveX = -dims.width;
786 moveX = -dims.width;
817 moveY = -dims.height;
787 moveY = -dims.height;
818 break;
788 break;
819 case 'center':
789 case 'center':
820 initialMoveX = dims.width / 2;
790 initialMoveX = dims.width / 2;
821 initialMoveY = dims.height / 2;
791 initialMoveY = dims.height / 2;
822 moveX = -dims.width / 2;
792 moveX = -dims.width / 2;
823 moveY = -dims.height / 2;
793 moveY = -dims.height / 2;
824 break;
794 break;
825 }
795 }
826
796
827 return new Effect.Move(element, {
797 return new Effect.Move(element, {
828 x: initialMoveX,
798 x: initialMoveX,
829 y: initialMoveY,
799 y: initialMoveY,
830 duration: 0.01,
800 duration: 0.01,
831 beforeSetup: function(effect) {
801 beforeSetup: function(effect) {
832 effect.element.hide().makeClipping().makePositioned();
802 effect.element.hide().makeClipping().makePositioned();
833 },
803 },
834 afterFinishInternal: function(effect) {
804 afterFinishInternal: function(effect) {
835 new Effect.Parallel(
805 new Effect.Parallel(
836 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
806 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
837 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
807 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
838 new Effect.Scale(effect.element, 100, {
808 new Effect.Scale(effect.element, 100, {
839 scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
809 scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
840 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
810 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
841 ], Object.extend({
811 ], Object.extend({
842 beforeSetup: function(effect) {
812 beforeSetup: function(effect) {
843 effect.effects[0].element.setStyle({height: '0px'}).show();
813 effect.effects[0].element.setStyle({height: '0px'}).show();
844 },
814 },
845 afterFinishInternal: function(effect) {
815 afterFinishInternal: function(effect) {
846 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
816 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
847 }
817 }
848 }, options)
818 }, options)
849 )
819 )
850 }
820 }
851 });
821 });
852 - }
822 + };
853
823
854 Effect.Shrink = function(element) {
824 Effect.Shrink = function(element) {
855 element = $(element);
825 element = $(element);
856 var options = Object.extend({
826 var options = Object.extend({
857 direction: 'center',
827 direction: 'center',
858 moveTransition: Effect.Transitions.sinoidal,
828 moveTransition: Effect.Transitions.sinoidal,
859 scaleTransition: Effect.Transitions.sinoidal,
829 scaleTransition: Effect.Transitions.sinoidal,
860 opacityTransition: Effect.Transitions.none
830 opacityTransition: Effect.Transitions.none
861 }, arguments[1] || {});
831 }, arguments[1] || { });
862 var oldStyle = {
832 var oldStyle = {
863 top: element.style.top,
833 top: element.style.top,
864 left: element.style.left,
834 left: element.style.left,
865 height: element.style.height,
835 height: element.style.height,
866 width: element.style.width,
836 width: element.style.width,
867 opacity: element.getInlineOpacity() };
837 opacity: element.getInlineOpacity() };
868
838
869 var dims = element.getDimensions();
839 var dims = element.getDimensions();
870 var moveX, moveY;
840 var moveX, moveY;
871
841
872 switch (options.direction) {
842 switch (options.direction) {
873 case 'top-left':
843 case 'top-left':
874 moveX = moveY = 0;
844 moveX = moveY = 0;
875 break;
845 break;
876 case 'top-right':
846 case 'top-right':
877 moveX = dims.width;
847 moveX = dims.width;
878 moveY = 0;
848 moveY = 0;
879 break;
849 break;
880 case 'bottom-left':
850 case 'bottom-left':
881 moveX = 0;
851 moveX = 0;
882 moveY = dims.height;
852 moveY = dims.height;
883 break;
853 break;
884 case 'bottom-right':
854 case 'bottom-right':
885 moveX = dims.width;
855 moveX = dims.width;
886 moveY = dims.height;
856 moveY = dims.height;
887 break;
857 break;
888 case 'center':
858 case 'center':
889 moveX = dims.width / 2;
859 moveX = dims.width / 2;
890 moveY = dims.height / 2;
860 moveY = dims.height / 2;
891 break;
861 break;
892 }
862 }
893
863
894 return new Effect.Parallel(
864 return new Effect.Parallel(
895 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
865 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
896 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
866 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
897 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
867 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
898 ], Object.extend({
868 ], Object.extend({
899 beforeStartInternal: function(effect) {
869 beforeStartInternal: function(effect) {
900 effect.effects[0].element.makePositioned().makeClipping();
870 effect.effects[0].element.makePositioned().makeClipping();
901 },
871 },
902 afterFinishInternal: function(effect) {
872 afterFinishInternal: function(effect) {
903 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
873 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
904 }, options)
874 }, options)
905 );
875 );
906 - }
876 + };
907
877
908 Effect.Pulsate = function(element) {
878 Effect.Pulsate = function(element) {
909 element = $(element);
879 element = $(element);
910 var options = arguments[1] || {};
880 var options = arguments[1] || { };
911 var oldOpacity = element.getInlineOpacity();
881 var oldOpacity = element.getInlineOpacity();
912 var transition = options.transition || Effect.Transitions.sinoidal;
882 var transition = options.transition || Effect.Transitions.sinoidal;
913 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
883 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
914 reverser.bind(transition);
884 reverser.bind(transition);
915 return new Effect.Opacity(element,
885 return new Effect.Opacity(element,
916 Object.extend(Object.extend({ duration: 2.0, from: 0,
886 Object.extend(Object.extend({ duration: 2.0, from: 0,
917 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
887 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
918 }, options), {transition: reverser}));
888 }, options), {transition: reverser}));
919 - }
889 + };
920
890
921 Effect.Fold = function(element) {
891 Effect.Fold = function(element) {
922 element = $(element);
892 element = $(element);
923 var oldStyle = {
893 var oldStyle = {
924 top: element.style.top,
894 top: element.style.top,
925 left: element.style.left,
895 left: element.style.left,
926 width: element.style.width,
896 width: element.style.width,
927 height: element.style.height };
897 height: element.style.height };
928 element.makeClipping();
898 element.makeClipping();
929 return new Effect.Scale(element, 5, Object.extend({
899 return new Effect.Scale(element, 5, Object.extend({
930 scaleContent: false,
900 scaleContent: false,
931 scaleX: false,
901 scaleX: false,
932 afterFinishInternal: function(effect) {
902 afterFinishInternal: function(effect) {
933 new Effect.Scale(element, 1, {
903 new Effect.Scale(element, 1, {
934 scaleContent: false,
904 scaleContent: false,
935 scaleY: false,
905 scaleY: false,
936 afterFinishInternal: function(effect) {
906 afterFinishInternal: function(effect) {
937 effect.element.hide().undoClipping().setStyle(oldStyle);
907 effect.element.hide().undoClipping().setStyle(oldStyle);
938 } });
908 } });
939 }}, arguments[1] || {}));
909 }}, arguments[1] || { }));
940 };
910 };
941
911
942 - Effect.Morph = Class.create();
912 + Effect.Morph = Class.create(Effect.Base, {
943 - Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
944 initialize: function(element) {
913 initialize: function(element) {
945 this.element = $(element);
914 this.element = $(element);
946 if(!this.element) throw(Effect._elementDoesNotExistError);
915 if (!this.element) throw(Effect._elementDoesNotExistError);
947 var options = Object.extend({
916 var options = Object.extend({
948 - style: ''
917 + style: { }
949 }, arguments[1] || {});
918 }, arguments[1] || { });
919 +
920 + if (!Object.isString(options.style)) this.style = $H(options.style);
921 + else {
922 + if (options.style.include(':'))
923 + this.style = options.style.parseStyle();
924 + else {
925 + this.element.addClassName(options.style);
926 + this.style = $H(this.element.getStyles());
927 + this.element.removeClassName(options.style);
928 + var css = this.element.getStyles();
929 + this.style = this.style.reject(function(style) {
930 + return style.value == css[style.key];
931 + });
932 + options.afterFinishInternal = function(effect) {
933 + effect.element.addClassName(effect.options.style);
934 + effect.transforms.each(function(transform) {
935 + effect.element.style[transform.style] = '';
936 + });
937 + }
938 + }
939 + }
950 this.start(options);
940 this.start(options);
951 },
941 },
942 +
952 setup: function(){
943 setup: function(){
953 function parseColor(color){
944 function parseColor(color){
954 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
945 if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
955 color = color.parseColor();
946 color = color.parseColor();
956 return $R(0,2).map(function(i){
947 return $R(0,2).map(function(i){
957 return parseInt( color.slice(i*2+1,i*2+3), 16 )
948 return parseInt( color.slice(i*2+1,i*2+3), 16 )
958 });
949 });
959 }
950 }
960 - this.transforms = this.options.style.parseStyle().map(function(property){
951 + this.transforms = this.style.map(function(pair){
961 - var originalValue = this.element.getStyle(property[0]);
952 + var property = pair[0], value = pair[1], unit = null;
962 - return $H({
953 +
963 - style: property[0],
954 + if (value.parseColor('#zzzzzz') != '#zzzzzz') {
964 - originalValue: property[1].unit=='color' ?
955 + value = value.parseColor();
965 - parseColor(originalValue) : parseFloat(originalValue || 0),
956 + unit = 'color';
966 - targetValue: property[1].unit=='color' ?
957 + } else if (property == 'opacity') {
967 - parseColor(property[1].value) : property[1].value,
958 + value = parseFloat(value);
968 - unit: property[1].unit
959 + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
969 - });
960 + this.element.setStyle({zoom: 1});
961 + } else if (Element.CSS_LENGTH.test(value)) {
962 + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
963 + value = parseFloat(components[1]);
964 + unit = (components.length == 3) ? components[2] : null;
965 + }
966 +
967 + var originalValue = this.element.getStyle(property);
968 + return {
969 + style: property.camelize(),
970 + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
971 + targetValue: unit=='color' ? parseColor(value) : value,
972 + unit: unit
973 + };
970 }.bind(this)).reject(function(transform){
974 }.bind(this)).reject(function(transform){
971 return (
975 return (
972 (transform.originalValue == transform.targetValue) ||
976 (transform.originalValue == transform.targetValue) ||
973 (
977 (
974 transform.unit != 'color' &&
978 transform.unit != 'color' &&
975 (isNaN(transform.originalValue) || isNaN(transform.targetValue))
979 (isNaN(transform.originalValue) || isNaN(transform.targetValue))
976 )
980 )
977 )
981 )
978 });
982 });
979 },
983 },
980 update: function(position) {
984 update: function(position) {
981 - var style = $H(), value = null;
985 + var style = { }, transform, i = this.transforms.length;
982 - this.transforms.each(function(transform){
986 + while(i--)
983 - value = transform.unit=='color' ?
987 + style[(transform = this.transforms[i]).style] =
984 - $R(0,2).inject('#',function(m,v,i){
988 + transform.unit=='color' ? '#'+
985 - return m+(Math.round(transform.originalValue[i]+
989 + (Math.round(transform.originalValue[0]+
986 - (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
990 + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
987 - transform.originalValue + Math.round(
991 + (Math.round(transform.originalValue[1]+
988 - ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
992 + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
989 - style[transform.style] = value;
993 + (Math.round(transform.originalValue[2]+
990 - });
994 + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
991 - this.element.setStyle(style);
995 + (transform.originalValue +
996 + (transform.targetValue - transform.originalValue) * position).toFixed(3) +
997 + (transform.unit === null ? '' : transform.unit);
998 + this.element.setStyle(style, true);
992 }
999 }
993 });
1000 });
994
1001
995 - Effect.Transform = Class.create();
1002 + Effect.Transform = Class.create({
996 - Object.extend(Effect.Transform.prototype, {
997 initialize: function(tracks){
1003 initialize: function(tracks){
998 this.tracks = [];
1004 this.tracks = [];
999 this.options = arguments[1] || {};
1005 this.options = arguments[1] || { };
1000 this.addTracks(tracks);
1006 this.addTracks(tracks);
1001 },
1007 },
1002 addTracks: function(tracks){
1008 addTracks: function(tracks){
1003 tracks.each(function(track){
1009 tracks.each(function(track){
1004 - var data = $H(track).values().first();
1010 + track = $H(track);
1011 + var data = track.values().first();
1005 this.tracks.push($H({
1012 this.tracks.push($H({
1006 - ids: $H(track).keys().first(),
1013 + ids: track.keys().first(),
1007 effect: Effect.Morph,
1014 effect: Effect.Morph,
1008 options: { style: data }
1015 options: { style: data }
1009 }));
1016 }));
1010 }.bind(this));
1017 }.bind(this));
1011 return this;
1018 return this;
1012 },
1019 },
1013 play: function(){
1020 play: function(){
1014 return new Effect.Parallel(
1021 return new Effect.Parallel(
1015 this.tracks.map(function(track){
1022 this.tracks.map(function(track){
1016 - var elements = [$(track.ids) || $$(track.ids)].flatten();
1023 + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1017 - return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1024 + var elements = [$(ids) || $$(ids)].flatten();
1025 + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1018 }).flatten(),
1026 }).flatten(),
1019 this.options
1027 this.options
1020 );
1028 );
1021 }
1029 }
1022 });
1030 });
1023
1031
1024 - Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1032 + Element.CSS_PROPERTIES = $w(
1025 - 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1033 + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1026 - 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1034 + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1027 - 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1035 + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1028 - 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1036 + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1029 - 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1037 + 'fontSize fontWeight height left letterSpacing lineHeight ' +
1030 - 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1038 + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1031 - 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1039 + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1032 - 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1040 + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1033 - 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
1041 + 'right textIndent top width wordSpacing zIndex');
1034 - 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
1035 - 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
1036 - 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
1037 - 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
1038 - 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
1039 - 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
1040 - 'width', 'wordSpacing', 'zIndex'];
1041
1042
1042 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043
1044
1045 + String.__parseStyleElement = document.createElement('div');
1044 String.prototype.parseStyle = function(){
1046 String.prototype.parseStyle = function(){
1045 - var element = Element.extend(document.createElement('div'));
1047 + var style, styleRules = $H();
1046 - element.innerHTML = '<div style="' + this + '"></div>';
1048 + if (Prototype.Browser.WebKit)
1047 - var style = element.down().style, styleRules = $H();
1049 + style = new Element('div',{style:this}).style;
1050 + else {
1051 + String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
1052 + style = String.__parseStyleElement.childNodes[0].style;
1053 + }
1048
1054
1049 Element.CSS_PROPERTIES.each(function(property){
1055 Element.CSS_PROPERTIES.each(function(property){
1050 - if(style[property]) styleRules[property] = style[property];
1056 + if (style[property]) styleRules.set(property, style[property]);
1051 });
1057 });
1052
1058
1053 - var result = $H();
1059 + if (Prototype.Browser.IE && this.include('opacity'))
1060 + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
1054
1061
1055 - styleRules.each(function(pair){
1062 + return styleRules;
1056 - var property = pair[0], value = pair[1], unit = null;
1063 + };
1057
1064
1058 - if(value.parseColor('#zzzzzz') != '#zzzzzz') {
1065 + if (document.defaultView && document.defaultView.getComputedStyle) {
1059 - value = value.parseColor();
1066 + Element.getStyles = function(element) {
1060 - unit = 'color';
1067 + var css = document.defaultView.getComputedStyle($(element), null);
1061 - } else if(Element.CSS_LENGTH.test(value))
1068 + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1062 - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
1069 + styles[property] = css[property];
1063 - value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
1070 + return styles;
1064 -
1071 + });
1065 - result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1072 + };
1066 - }.bind(this));
1073 + } else {
1067 -
1074 + Element.getStyles = function(element) {
1068 - return result;
1075 + element = $(element);
1076 + var css = element.currentStyle, styles;
1077 + styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
1078 + hash.set(property, css[property]);
1079 + return hash;
1080 + });
1081 + if (!styles.opacity) styles.set('opacity', element.getOpacity());
1082 + return styles;
1083 + };
1069 };
1084 };
1070
1085
1071 - Element.morph = function(element, style) {
1086 + Effect.Methods = {
1087 + morph: function(element, style) {
1088 + element = $(element);
1072 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1089 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
1073 return element;
1090 return element;
1091 + },
1092 + visualEffect: function(element, effect, options) {
1093 + element = $(element)
1094 + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
1095 + new Effect[klass](element, options);
1096 + return element;
1097 + },
1098 + highlight: function(element, options) {
1099 + element = $(element);
1100 + new Effect.Highlight(element, options);
1101 + return element;
1102 + }
1074 };
1103 };
1075
1104
1076 - ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
1105 + $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1077 - 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1106 + 'pulsate shake puff squish switchOff dropOut').each(
1078 - function(f) { Element.Methods[f] = Element[f]; }
1107 + function(effect) {
1108 + Effect.Methods[effect] = function(element, options){
1109 + element = $(element);
1110 + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
1111 + return element;
1112 + }
1113 + }
1079 );
1114 );
1080
1115
1081 - Element.Methods.visualEffect = function(element, effect, options) {
1116 + $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1082 - s = effect.gsub(/_/, '-').camelize();
1117 + function(f) { Effect.Methods[f] = Element[f]; }
1083 - effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1118 + );
1084 - new Effect[effect_class](element, options);
1085 - return $(element);
1086 - };
1087
1119
1088 - Element.addMethods(); No newline at end of file
1120 + Element.addMethods(Effect.Methods);
This diff has been collapsed as it changes many lines, (4500 lines changed) Show them Hide them
@@ -1,2515 +1,4225
1 - /* Prototype JavaScript framework, version 1.5.0
1 + /* Prototype JavaScript framework, version 1.6.0.1
2 * (c) 2005-2007 Sam Stephenson
2 * (c) 2005-2007 Sam Stephenson
3 *
3 *
4 * Prototype is freely distributable under the terms of an MIT-style license.
4 * Prototype is freely distributable under the terms of an MIT-style license.
5 - * For details, see the Prototype web site: http://prototype.conio.net/
5 + * For details, see the Prototype web site: http://www.prototypejs.org/
6 *
6 *
7 - /*--------------------------------------------------------------------------*/
7 + *--------------------------------------------------------------------------*/
8
8
9 var Prototype = {
9 var Prototype = {
10 - Version: '1.5.0',
10 + Version: '1.6.0.1',
11 +
12 + Browser: {
13 + IE: !!(window.attachEvent && !window.opera),
14 + Opera: !!window.opera,
15 + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16 + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
17 + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
18 + },
19 +
11 BrowserFeatures: {
20 BrowserFeatures: {
12 - XPath: !!document.evaluate
21 + XPath: !!document.evaluate,
13 - },
22 + ElementExtensions: !!window.HTMLElement,
14 -
23 + SpecificElementExtensions:
15 - ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
24 + document.createElement('div').__proto__ &&
25 + document.createElement('div').__proto__ !==
26 + document.createElement('form').__proto__
27 + },
28 +
29 + ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
30 + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
31 +
16 emptyFunction: function() {},
32 emptyFunction: function() { },
17 K: function(x) { return x }
33 K: function(x) { return x }
18 - }
34 + };
19 -
35 +
36 + if (Prototype.Browser.MobileSafari)
37 + Prototype.BrowserFeatures.SpecificElementExtensions = false;
38 +
39 +
40 + /* Based on Alex Arnell's inheritance implementation. */
20 var Class = {
41 var Class = {
21 create: function() {
42 create: function() {
22 - return function() {
43 + var parent = null, properties = $A(arguments);
44 + if (Object.isFunction(properties[0]))
45 + parent = properties.shift();
46 +
47 + function klass() {
23 this.initialize.apply(this, arguments);
48 this.initialize.apply(this, arguments);
24 }
49 }
25 - }
50 +
26 - }
51 + Object.extend(klass, Class.Methods);
27 -
52 + klass.superclass = parent;
28 - var Abstract = new Object();
53 + klass.subclasses = [];
54 +
55 + if (parent) {
56 + var subclass = function() { };
57 + subclass.prototype = parent.prototype;
58 + klass.prototype = new subclass;
59 + parent.subclasses.push(klass);
60 + }
61 +
62 + for (var i = 0; i < properties.length; i++)
63 + klass.addMethods(properties[i]);
64 +
65 + if (!klass.prototype.initialize)
66 + klass.prototype.initialize = Prototype.emptyFunction;
67 +
68 + klass.prototype.constructor = klass;
69 +
70 + return klass;
71 + }
72 + };
73 +
74 + Class.Methods = {
75 + addMethods: function(source) {
76 + var ancestor = this.superclass && this.superclass.prototype;
77 + var properties = Object.keys(source);
78 +
79 + if (!Object.keys({ toString: true }).length)
80 + properties.push("toString", "valueOf");
81 +
82 + for (var i = 0, length = properties.length; i < length; i++) {
83 + var property = properties[i], value = source[property];
84 + if (ancestor && Object.isFunction(value) &&
85 + value.argumentNames().first() == "$super") {
86 + var method = value, value = Object.extend((function(m) {
87 + return function() { return ancestor[m].apply(this, arguments) };
88 + })(property).wrap(method), {
89 + valueOf: function() { return method },
90 + toString: function() { return method.toString() }
91 + });
92 + }
93 + this.prototype[property] = value;
94 + }
95 +
96 + return this;
97 + }
98 + };
99 +
100 + var Abstract = { };
29
101
30 Object.extend = function(destination, source) {
102 Object.extend = function(destination, source) {
31 - for (var property in source) {
103 + for (var property in source)
32 destination[property] = source[property];
104 destination[property] = source[property];
33 - }
34 return destination;
105 return destination;
35 - }
106 + };
36
107
37 Object.extend(Object, {
108 Object.extend(Object, {
38 inspect: function(object) {
109 inspect: function(object) {
39 try {
110 try {
40 - if (object === undefined) return 'undefined';
111 + if (Object.isUndefined(object)) return 'undefined';
41 if (object === null) return 'null';
112 if (object === null) return 'null';
42 return object.inspect ? object.inspect() : object.toString();
113 return object.inspect ? object.inspect() : object.toString();
43 } catch (e) {
114 } catch (e) {
44 if (e instanceof RangeError) return '...';
115 if (e instanceof RangeError) return '...';
45 throw e;
116 throw e;
46 }
117 }
47 },
118 },
48
119
120 + toJSON: function(object) {
121 + var type = typeof object;
122 + switch (type) {
123 + case 'undefined':
124 + case 'function':
125 + case 'unknown': return;
126 + case 'boolean': return object.toString();
127 + }
128 +
129 + if (object === null) return 'null';
130 + if (object.toJSON) return object.toJSON();
131 + if (Object.isElement(object)) return;
132 +
133 + var results = [];
134 + for (var property in object) {
135 + var value = Object.toJSON(object[property]);
136 + if (!Object.isUndefined(value))
137 + results.push(property.toJSON() + ': ' + value);
138 + }
139 +
140 + return '{' + results.join(', ') + '}';
141 + },
142 +
143 + toQueryString: function(object) {
144 + return $H(object).toQueryString();
145 + },
146 +
147 + toHTML: function(object) {
148 + return object && object.toHTML ? object.toHTML() : String.interpret(object);
149 + },
150 +
49 keys: function(object) {
151 keys: function(object) {
50 var keys = [];
152 var keys = [];
51 for (var property in object)
153 for (var property in object)
52 keys.push(property);
154 keys.push(property);
53 return keys;
155 return keys;
54 },
156 },
55
157
56 values: function(object) {
158 values: function(object) {
57 var values = [];
159 var values = [];
58 for (var property in object)
160 for (var property in object)
59 values.push(object[property]);
161 values.push(object[property]);
60 return values;
162 return values;
61 },
163 },
62
164
63 clone: function(object) {
165 clone: function(object) {
64 return Object.extend({}, object);
166 return Object.extend({ }, object);
167 + },
168 +
169 + isElement: function(object) {
170 + return object && object.nodeType == 1;
171 + },
172 +
173 + isArray: function(object) {
174 + return object && object.constructor === Array;
175 + },
176 +
177 + isHash: function(object) {
178 + return object instanceof Hash;
179 + },
180 +
181 + isFunction: function(object) {
182 + return typeof object == "function";
183 + },
184 +
185 + isString: function(object) {
186 + return typeof object == "string";
187 + },
188 +
189 + isNumber: function(object) {
190 + return typeof object == "number";
191 + },
192 +
193 + isUndefined: function(object) {
194 + return typeof object == "undefined";
65 }
195 }
66 });
196 });
67
197
68 - Function.prototype.bind = function() {
198 + Object.extend(Function.prototype, {
199 + argumentNames: function() {
200 + var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
201 + return names.length == 1 && !names[0] ? [] : names;
202 + },
203 +
204 + bind: function() {
205 + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
69 var __method = this, args = $A(arguments), object = args.shift();
206 var __method = this, args = $A(arguments), object = args.shift();
70 return function() {
207 return function() {
71 return __method.apply(object, args.concat($A(arguments)));
208 return __method.apply(object, args.concat($A(arguments)));
72 }
209 }
73 - }
210 + },
74 -
211 +
75 - Function.prototype.bindAsEventListener = function(object) {
212 + bindAsEventListener: function() {
76 var __method = this, args = $A(arguments), object = args.shift();
213 var __method = this, args = $A(arguments), object = args.shift();
77 return function(event) {
214 return function(event) {
78 - return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
215 + return __method.apply(object, [event || window.event].concat(args));
79 - }
216 + }
80 - }
217 + },
81 -
218 +
82 - Object.extend(Number.prototype, {
219 + curry: function() {
83 - toColorPart: function() {
220 + if (!arguments.length) return this;
84 - var digits = this.toString(16);
221 + var __method = this, args = $A(arguments);
85 - if (this < 16) return '0' + digits;
222 + return function() {
86 - return digits;
223 + return __method.apply(this, args.concat($A(arguments)));
87 - },
224 + }
88 -
225 + },
89 - succ: function() {
226 +
90 - return this + 1;
227 + delay: function() {
91 - },
228 + var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
92 -
229 + return window.setTimeout(function() {
93 - times: function(iterator) {
230 + return __method.apply(__method, args);
94 - $R(0, this, true).each(iterator);
231 + }, timeout);
95 - return this;
232 + },
233 +
234 + wrap: function(wrapper) {
235 + var __method = this;
236 + return function() {
237 + return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
238 + }
239 + },
240 +
241 + methodize: function() {
242 + if (this._methodized) return this._methodized;
243 + var __method = this;
244 + return this._methodized = function() {
245 + return __method.apply(null, [this].concat($A(arguments)));
246 + };
96 }
247 }
97 });
248 });
98
249
250 + Function.prototype.defer = Function.prototype.delay.curry(0.01);
251 +
252 + Date.prototype.toJSON = function() {
253 + return '"' + this.getUTCFullYear() + '-' +
254 + (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
255 + this.getUTCDate().toPaddedString(2) + 'T' +
256 + this.getUTCHours().toPaddedString(2) + ':' +
257 + this.getUTCMinutes().toPaddedString(2) + ':' +
258 + this.getUTCSeconds().toPaddedString(2) + 'Z"';
259 + };
260 +
99 var Try = {
261 var Try = {
100 these: function() {
262 these: function() {
101 var returnValue;
263 var returnValue;
102
264
103 for (var i = 0, length = arguments.length; i < length; i++) {
265 for (var i = 0, length = arguments.length; i < length; i++) {
104 var lambda = arguments[i];
266 var lambda = arguments[i];
105 try {
267 try {
106 returnValue = lambda();
268 returnValue = lambda();
107 break;
269 break;
108 } catch (e) {}
270 } catch (e) { }
109 }
271 }
110
272
111 return returnValue;
273 return returnValue;
112 }
274 }
113 - }
275 + };
276 +
277 + RegExp.prototype.match = RegExp.prototype.test;
278 +
279 + RegExp.escape = function(str) {
280 + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
281 + };
114
282
115 /*--------------------------------------------------------------------------*/
283 /*--------------------------------------------------------------------------*/
116
284
117 - var PeriodicalExecuter = Class.create();
285 + var PeriodicalExecuter = Class.create({
118 - PeriodicalExecuter.prototype = {
119 initialize: function(callback, frequency) {
286 initialize: function(callback, frequency) {
120 this.callback = callback;
287 this.callback = callback;
121 this.frequency = frequency;
288 this.frequency = frequency;
122 this.currentlyExecuting = false;
289 this.currentlyExecuting = false;
123
290
124 this.registerCallback();
291 this.registerCallback();
125 },
292 },
126
293
127 registerCallback: function() {
294 registerCallback: function() {
128 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
295 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129 },
296 },
130
297
298 + execute: function() {
299 + this.callback(this);
300 + },
301 +
131 stop: function() {
302 stop: function() {
132 if (!this.timer) return;
303 if (!this.timer) return;
133 clearInterval(this.timer);
304 clearInterval(this.timer);
134 this.timer = null;
305 this.timer = null;
135 },
306 },
136
307
137 onTimerEvent: function() {
308 onTimerEvent: function() {
138 if (!this.currentlyExecuting) {
309 if (!this.currentlyExecuting) {
139 try {
310 try {
140 this.currentlyExecuting = true;
311 this.currentlyExecuting = true;
141 - this.callback(this);
312 + this.execute();
142 } finally {
313 } finally {
143 this.currentlyExecuting = false;
314 this.currentlyExecuting = false;
144 }
315 }
145 }
316 }
146 }
317 }
147 - }
318 + });
148 - String.interpret = function(value){
319 + Object.extend(String, {
320 + interpret: function(value) {
149 return value == null ? '' : String(value);
321 return value == null ? '' : String(value);
150 - }
322 + },
323 + specialChar: {
324 + '\b': '\\b',
325 + '\t': '\\t',
326 + '\n': '\\n',
327 + '\f': '\\f',
328 + '\r': '\\r',
329 + '\\': '\\\\'
330 + }
331 + });
151
332
152 Object.extend(String.prototype, {
333 Object.extend(String.prototype, {
153 gsub: function(pattern, replacement) {
334 gsub: function(pattern, replacement) {
154 var result = '', source = this, match;
335 var result = '', source = this, match;
155 replacement = arguments.callee.prepareReplacement(replacement);
336 replacement = arguments.callee.prepareReplacement(replacement);
156
337
157 while (source.length > 0) {
338 while (source.length > 0) {
158 if (match = source.match(pattern)) {
339 if (match = source.match(pattern)) {
159 result += source.slice(0, match.index);
340 result += source.slice(0, match.index);
160 result += String.interpret(replacement(match));
341 result += String.interpret(replacement(match));
161 source = source.slice(match.index + match[0].length);
342 source = source.slice(match.index + match[0].length);
162 } else {
343 } else {
163 result += source, source = '';
344 result += source, source = '';
164 }
345 }
165 }
346 }
166 return result;
347 return result;
167 },
348 },
168
349
169 sub: function(pattern, replacement, count) {
350 sub: function(pattern, replacement, count) {
170 replacement = this.gsub.prepareReplacement(replacement);
351 replacement = this.gsub.prepareReplacement(replacement);
171 - count = count === undefined ? 1 : count;
352 + count = Object.isUndefined(count) ? 1 : count;
172
353
173 return this.gsub(pattern, function(match) {
354 return this.gsub(pattern, function(match) {
174 if (--count < 0) return match[0];
355 if (--count < 0) return match[0];
175 return replacement(match);
356 return replacement(match);
176 });
357 });
177 },
358 },
178
359
179 scan: function(pattern, iterator) {
360 scan: function(pattern, iterator) {
180 this.gsub(pattern, iterator);
361 this.gsub(pattern, iterator);
181 - return this;
362 + return String(this);
182 },
363 },
183
364
184 truncate: function(length, truncation) {
365 truncate: function(length, truncation) {
185 length = length || 30;
366 length = length || 30;
186 - truncation = truncation === undefined ? '...' : truncation;
367 + truncation = Object.isUndefined(truncation) ? '...' : truncation;
187 return this.length > length ?
368 return this.length > length ?
188 - this.slice(0, length - truncation.length) + truncation : this;
369 + this.slice(0, length - truncation.length) + truncation : String(this);
189 },
370 },
190
371
191 strip: function() {
372 strip: function() {
192 return this.replace(/^\s+/, '').replace(/\s+$/, '');
373 return this.replace(/^\s+/, '').replace(/\s+$/, '');
193 },
374 },
194
375
195 stripTags: function() {
376 stripTags: function() {
196 return this.replace(/<\/?[^>]+>/gi, '');
377 return this.replace(/<\/?[^>]+>/gi, '');
197 },
378 },
198
379
199 stripScripts: function() {
380 stripScripts: function() {
200 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
381 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
201 },
382 },
202
383
203 extractScripts: function() {
384 extractScripts: function() {
204 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
385 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
205 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
386 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
206 return (this.match(matchAll) || []).map(function(scriptTag) {
387 return (this.match(matchAll) || []).map(function(scriptTag) {
207 return (scriptTag.match(matchOne) || ['', ''])[1];
388 return (scriptTag.match(matchOne) || ['', ''])[1];
208 });
389 });
209 },
390 },
210
391
211 evalScripts: function() {
392 evalScripts: function() {
212 return this.extractScripts().map(function(script) { return eval(script) });
393 return this.extractScripts().map(function(script) { return eval(script) });
213 },
394 },
214
395
215 escapeHTML: function() {
396 escapeHTML: function() {
216 - var div = document.createElement('div');
397 + var self = arguments.callee;
217 - var text = document.createTextNode(this);
398 + self.text.data = this;
218 - div.appendChild(text);
399 + return self.div.innerHTML;
219 - return div.innerHTML;
220 },
400 },
221
401
222 unescapeHTML: function() {
402 unescapeHTML: function() {
223 - var div = document.createElement('div');
403 + var div = new Element('div');
224 div.innerHTML = this.stripTags();
404 div.innerHTML = this.stripTags();
225 return div.childNodes[0] ? (div.childNodes.length > 1 ?
405 return div.childNodes[0] ? (div.childNodes.length > 1 ?
226 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
406 $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
227 div.childNodes[0].nodeValue) : '';
407 div.childNodes[0].nodeValue) : '';
228 },
408 },
229
409
230 toQueryParams: function(separator) {
410 toQueryParams: function(separator) {
231 var match = this.strip().match(/([^?#]*)(#.*)?$/);
411 var match = this.strip().match(/([^?#]*)(#.*)?$/);
232 if (!match) return {};
412 if (!match) return { };
233
413
234 return match[1].split(separator || '&').inject({}, function(hash, pair) {
414 return match[1].split(separator || '&').inject({ }, function(hash, pair) {
235 if ((pair = pair.split('='))[0]) {
415 if ((pair = pair.split('='))[0]) {
236 - var name = decodeURIComponent(pair[0]);
416 + var key = decodeURIComponent(pair.shift());
237 - var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
417 + var value = pair.length > 1 ? pair.join('=') : pair[0];
238 -
418 + if (value != undefined) value = decodeURIComponent(value);
239 - if (hash[name] !== undefined) {
419 +
240 - if (hash[name].constructor != Array)
420 + if (key in hash) {
241 - hash[name] = [hash[name]];
421 + if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
242 - if (value) hash[name].push(value);
422 + hash[key].push(value);
243 - }
423 + }
244 - else hash[name] = value;
424 + else hash[key] = value;
245 }
425 }
246 return hash;
426 return hash;
247 });
427 });
248 },
428 },
249
429
250 toArray: function() {
430 toArray: function() {
251 return this.split('');
431 return this.split('');
252 },
432 },
253
433
254 succ: function() {
434 succ: function() {
255 return this.slice(0, this.length - 1) +
435 return this.slice(0, this.length - 1) +
256 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
436 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257 },
437 },
258
438
439 + times: function(count) {
440 + return count < 1 ? '' : new Array(count + 1).join(this);
441 + },
442 +
259 camelize: function() {
443 camelize: function() {
260 var parts = this.split('-'), len = parts.length;
444 var parts = this.split('-'), len = parts.length;
261 if (len == 1) return parts[0];
445 if (len == 1) return parts[0];
262
446
263 var camelized = this.charAt(0) == '-'
447 var camelized = this.charAt(0) == '-'
264 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
448 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265 : parts[0];
449 : parts[0];
266
450
267 for (var i = 1; i < len; i++)
451 for (var i = 1; i < len; i++)
268 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
452 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
269
453
270 return camelized;
454 return camelized;
271 },
455 },
272
456
273 capitalize: function(){
457 capitalize: function() {
274 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
458 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275 },
459 },
276
460
277 underscore: function() {
461 underscore: function() {
278 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
462 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
279 },
463 },
280
464
281 dasherize: function() {
465 dasherize: function() {
282 return this.gsub(/_/,'-');
466 return this.gsub(/_/,'-');
283 },
467 },
284
468
285 inspect: function(useDoubleQuotes) {
469 inspect: function(useDoubleQuotes) {
286 - var escapedString = this.replace(/\\/g, '\\\\');
470 + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
287 - if (useDoubleQuotes)
471 + var character = String.specialChar[match[0]];
288 - return '"' + escapedString.replace(/"/g, '\\"') + '"';
472 + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
289 - else
473 + });
474 + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
290 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
475 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
476 + },
477 +
478 + toJSON: function() {
479 + return this.inspect(true);
480 + },
481 +
482 + unfilterJSON: function(filter) {
483 + return this.sub(filter || Prototype.JSONFilter, '#{1}');
484 + },
485 +
486 + isJSON: function() {
487 + var str = this;
488 + if (str.blank()) return false;
489 + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
490 + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
491 + },
492 +
493 + evalJSON: function(sanitize) {
494 + var json = this.unfilterJSON();
495 + try {
496 + if (!sanitize || json.isJSON()) return eval('(' + json + ')');
497 + } catch (e) { }
498 + throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
499 + },
500 +
501 + include: function(pattern) {
502 + return this.indexOf(pattern) > -1;
503 + },
504 +
505 + startsWith: function(pattern) {
506 + return this.indexOf(pattern) === 0;
507 + },
508 +
509 + endsWith: function(pattern) {
510 + var d = this.length - pattern.length;
511 + return d >= 0 && this.lastIndexOf(pattern) === d;
512 + },
513 +
514 + empty: function() {
515 + return this == '';
516 + },
517 +
518 + blank: function() {
519 + return /^\s*$/.test(this);
520 + },
521 +
522 + interpolate: function(object, pattern) {
523 + return new Template(this, pattern).evaluate(object);
524 + }
525 + });
526 +
527 + if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
528 + escapeHTML: function() {
529 + return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
530 + },
531 + unescapeHTML: function() {
532 + return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
291 }
533 }
292 });
534 });
293
535
294 String.prototype.gsub.prepareReplacement = function(replacement) {
536 String.prototype.gsub.prepareReplacement = function(replacement) {
295 - if (typeof replacement == 'function') return replacement;
537 + if (Object.isFunction(replacement)) return replacement;
296 var template = new Template(replacement);
538 var template = new Template(replacement);
297 return function(match) { return template.evaluate(match) };
539 return function(match) { return template.evaluate(match) };
298 - }
540 + };
299
541
300 String.prototype.parseQuery = String.prototype.toQueryParams;
542 String.prototype.parseQuery = String.prototype.toQueryParams;
301
543
302 - var Template = Class.create();
544 + Object.extend(String.prototype.escapeHTML, {
303 - Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
545 + div: document.createElement('div'),
304 - Template.prototype = {
546 + text: document.createTextNode('')
547 + });
548 +
549 + with (String.prototype.escapeHTML) div.appendChild(text);
550 +
551 + var Template = Class.create({
305 initialize: function(template, pattern) {
552 initialize: function(template, pattern) {
306 this.template = template.toString();
553 this.template = template.toString();
307 this.pattern = pattern || Template.Pattern;
554 this.pattern = pattern || Template.Pattern;
308 },
555 },
309
556
310 evaluate: function(object) {
557 evaluate: function(object) {
558 + if (Object.isFunction(object.toTemplateReplacements))
559 + object = object.toTemplateReplacements();
560 +
311 return this.template.gsub(this.pattern, function(match) {
561 return this.template.gsub(this.pattern, function(match) {
312 - var before = match[1];
562 + if (object == null) return '';
563 +
564 + var before = match[1] || '';
313 if (before == '\\') return match[2];
565 if (before == '\\') return match[2];
314 - return before + String.interpret(object[match[3]]);
566 +
567 + var ctx = object, expr = match[3];
568 + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
569 + match = pattern.exec(expr);
570 + if (match == null) return before;
571 +
572 + while (match != null) {
573 + var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
574 + ctx = ctx[comp];
575 + if (null == ctx || '' == match[3]) break;
576 + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
577 + match = pattern.exec(expr);
578 + }
579 +
580 + return before + String.interpret(ctx);
581 + }.bind(this));
582 + }
315 });
583 });
316 - }
584 + Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
317 - }
585 +
318 -
586 + var $break = { };
319 - var $break = new Object();
320 - var $continue = new Object();
321
587
322 var Enumerable = {
588 var Enumerable = {
323 - each: function(iterator) {
589 + each: function(iterator, context) {
324 var index = 0;
590 var index = 0;
591 + iterator = iterator.bind(context);
325 try {
592 try {
326 this._each(function(value) {
593 this._each(function(value) {
327 - try {
328 iterator(value, index++);
594 iterator(value, index++);
329 - } catch (e) {
330 - if (e != $continue) throw e;
331 - }
332 });
595 });
333 } catch (e) {
596 } catch (e) {
334 if (e != $break) throw e;
597 if (e != $break) throw e;
335 }
598 }
336 return this;
599 return this;
337 },
600 },
338
601
339 - eachSlice: function(number, iterator) {
602 + eachSlice: function(number, iterator, context) {
603 + iterator = iterator ? iterator.bind(context) : Prototype.K;
340 var index = -number, slices = [], array = this.toArray();
604 var index = -number, slices = [], array = this.toArray();
341 while ((index += number) < array.length)
605 while ((index += number) < array.length)
342 slices.push(array.slice(index, index+number));
606 slices.push(array.slice(index, index+number));
343 - return slices.map(iterator);
607 + return slices.collect(iterator, context);
344 - },
608 + },
345 -
609 +
346 - all: function(iterator) {
610 + all: function(iterator, context) {
611 + iterator = iterator ? iterator.bind(context) : Prototype.K;
347 var result = true;
612 var result = true;
348 this.each(function(value, index) {
613 this.each(function(value, index) {
349 - result = result && !!(iterator || Prototype.K)(value, index);
614 + result = result && !!iterator(value, index);
350 if (!result) throw $break;
615 if (!result) throw $break;
351 });
616 });
352 return result;
617 return result;
353 },
618 },
354
619
355 - any: function(iterator) {
620 + any: function(iterator, context) {
621 + iterator = iterator ? iterator.bind(context) : Prototype.K;
356 var result = false;
622 var result = false;
357 this.each(function(value, index) {
623 this.each(function(value, index) {
358 - if (result = !!(iterator || Prototype.K)(value, index))
624 + if (result = !!iterator(value, index))
359 throw $break;
625 throw $break;
360 });
626 });
361 return result;
627 return result;
362 },
628 },
363
629
364 - collect: function(iterator) {
630 + collect: function(iterator, context) {
631 + iterator = iterator ? iterator.bind(context) : Prototype.K;
365 var results = [];
632 var results = [];
366 this.each(function(value, index) {
633 this.each(function(value, index) {
367 - results.push((iterator || Prototype.K)(value, index));
634 + results.push(iterator(value, index));
368 });
635 });
369 return results;
636 return results;
370 },
637 },
371
638
372 - detect: function(iterator) {
639 + detect: function(iterator, context) {
640 + iterator = iterator.bind(context);
373 var result;
641 var result;
374 this.each(function(value, index) {
642 this.each(function(value, index) {
375 if (iterator(value, index)) {
643 if (iterator(value, index)) {
376 result = value;
644 result = value;
377 throw $break;
645 throw $break;
378 }
646 }
379 });
647 });
380 return result;
648 return result;
381 },
649 },
382
650
383 - findAll: function(iterator) {
651 + findAll: function(iterator, context) {
652 + iterator = iterator.bind(context);
384 var results = [];
653 var results = [];
385 this.each(function(value, index) {
654 this.each(function(value, index) {
386 if (iterator(value, index))
655 if (iterator(value, index))
387 results.push(value);
656 results.push(value);
388 });
657 });
389 return results;
658 return results;
390 },
659 },
391
660
392 - grep: function(pattern, iterator) {
661 + grep: function(filter, iterator, context) {
662 + iterator = iterator ? iterator.bind(context) : Prototype.K;
393 var results = [];
663 var results = [];
664 +
665 + if (Object.isString(filter))
666 + filter = new RegExp(filter);
667 +
394 this.each(function(value, index) {
668 this.each(function(value, index) {
395 - var stringValue = value.toString();
669 + if (filter.match(value))
396 - if (stringValue.match(pattern))
670 + results.push(iterator(value, index));
397 - results.push((iterator || Prototype.K)(value, index));
671 + });
398 - })
399 return results;
672 return results;
400 },
673 },
401
674
402 include: function(object) {
675 include: function(object) {
676 + if (Object.isFunction(this.indexOf))
677 + if (this.indexOf(object) != -1) return true;
678 +
403 var found = false;
679 var found = false;
404 this.each(function(value) {
680 this.each(function(value) {
405 if (value == object) {
681 if (value == object) {
406 found = true;
682 found = true;
407 throw $break;
683 throw $break;
408 }
684 }
409 });
685 });
410 return found;
686 return found;
411 },
687 },
412
688
413 inGroupsOf: function(number, fillWith) {
689 inGroupsOf: function(number, fillWith) {
414 - fillWith = fillWith === undefined ? null : fillWith;
690 + fillWith = Object.isUndefined(fillWith) ? null : fillWith;
415 return this.eachSlice(number, function(slice) {
691 return this.eachSlice(number, function(slice) {
416 while(slice.length < number) slice.push(fillWith);
692 while(slice.length < number) slice.push(fillWith);
417 return slice;
693 return slice;
418 });
694 });
419 },
695 },
420
696
421 - inject: function(memo, iterator) {
697 + inject: function(memo, iterator, context) {
698 + iterator = iterator.bind(context);
422 this.each(function(value, index) {
699 this.each(function(value, index) {
423 memo = iterator(memo, value, index);
700 memo = iterator(memo, value, index);
424 });
701 });
425 return memo;
702 return memo;
426 },
703 },
427
704
428 invoke: function(method) {
705 invoke: function(method) {
429 var args = $A(arguments).slice(1);
706 var args = $A(arguments).slice(1);
430 return this.map(function(value) {
707 return this.map(function(value) {
431 return value[method].apply(value, args);
708 return value[method].apply(value, args);
432 });
709 });
433 },
710 },
434
711
435 - max: function(iterator) {
712 + max: function(iterator, context) {
713 + iterator = iterator ? iterator.bind(context) : Prototype.K;
436 var result;
714 var result;
437 this.each(function(value, index) {
715 this.each(function(value, index) {
438 - value = (iterator || Prototype.K)(value, index);
716 + value = iterator(value, index);
439 - if (result == undefined || value >= result)
717 + if (result == null || value >= result)
440 result = value;
718 result = value;
441 });
719 });
442 return result;
720 return result;
443 },
721 },
444
722
445 - min: function(iterator) {
723 + min: function(iterator, context) {
724 + iterator = iterator ? iterator.bind(context) : Prototype.K;
446 var result;
725 var result;
447 this.each(function(value, index) {
726 this.each(function(value, index) {
448 - value = (iterator || Prototype.K)(value, index);
727 + value = iterator(value, index);
449 - if (result == undefined || value < result)
728 + if (result == null || value < result)
450 result = value;
729 result = value;
451 });
730 });
452 return result;
731 return result;
453 },
732 },
454
733
455 - partition: function(iterator) {
734 + partition: function(iterator, context) {
735 + iterator = iterator ? iterator.bind(context) : Prototype.K;
456 var trues = [], falses = [];
736 var trues = [], falses = [];
457 this.each(function(value, index) {
737 this.each(function(value, index) {
458 - ((iterator || Prototype.K)(value, index) ?
738 + (iterator(value, index) ?
459 trues : falses).push(value);
739 trues : falses).push(value);
460 });
740 });
461 return [trues, falses];
741 return [trues, falses];
462 },
742 },
463
743
464 pluck: function(property) {
744 pluck: function(property) {
465 var results = [];
745 var results = [];
466 - this.each(function(value, index) {
746 + this.each(function(value) {
467 results.push(value[property]);
747 results.push(value[property]);
468 });
748 });
469 return results;
749 return results;
470 },
750 },
471
751
472 - reject: function(iterator) {
752 + reject: function(iterator, context) {
753 + iterator = iterator.bind(context);
473 var results = [];
754 var results = [];
474 this.each(function(value, index) {
755 this.each(function(value, index) {
475 if (!iterator(value, index))
756 if (!iterator(value, index))
476 results.push(value);
757 results.push(value);
477 });
758 });
478 return results;
759 return results;
479 },
760 },
480
761
481 - sortBy: function(iterator) {
762 + sortBy: function(iterator, context) {
763 + iterator = iterator.bind(context);
482 return this.map(function(value, index) {
764 return this.map(function(value, index) {
483 return {value: value, criteria: iterator(value, index)};
765 return {value: value, criteria: iterator(value, index)};
484 }).sort(function(left, right) {
766 }).sort(function(left, right) {
485 var a = left.criteria, b = right.criteria;
767 var a = left.criteria, b = right.criteria;
486 return a < b ? -1 : a > b ? 1 : 0;
768 return a < b ? -1 : a > b ? 1 : 0;
487 }).pluck('value');
769 }).pluck('value');
488 },
770 },
489
771
490 toArray: function() {
772 toArray: function() {
491 return this.map();
773 return this.map();
492 },
774 },
493
775
494 zip: function() {
776 zip: function() {
495 var iterator = Prototype.K, args = $A(arguments);
777 var iterator = Prototype.K, args = $A(arguments);
496 - if (typeof args.last() == 'function')
778 + if (Object.isFunction(args.last()))
497 iterator = args.pop();
779 iterator = args.pop();
498
780
499 var collections = [this].concat(args).map($A);
781 var collections = [this].concat(args).map($A);
500 return this.map(function(value, index) {
782 return this.map(function(value, index) {
501 return iterator(collections.pluck(index));
783 return iterator(collections.pluck(index));
502 });
784 });
503 },
785 },
504
786
505 size: function() {
787 size: function() {
506 return this.toArray().length;
788 return this.toArray().length;
507 },
789 },
508
790
509 inspect: function() {
791 inspect: function() {
510 return '#<Enumerable:' + this.toArray().inspect() + '>';
792 return '#<Enumerable:' + this.toArray().inspect() + '>';
511 }
793 }
512 - }
794 + };
513
795
514 Object.extend(Enumerable, {
796 Object.extend(Enumerable, {
515 map: Enumerable.collect,
797 map: Enumerable.collect,
516 find: Enumerable.detect,
798 find: Enumerable.detect,
517 select: Enumerable.findAll,
799 select: Enumerable.findAll,
800 + filter: Enumerable.findAll,
518 member: Enumerable.include,
801 member: Enumerable.include,
519 - entries: Enumerable.toArray
802 + entries: Enumerable.toArray,
803 + every: Enumerable.all,
804 + some: Enumerable.any
520 });
805 });
521 - var $A = Array.from = function(iterable) {
806 + function $A(iterable) {
522 if (!iterable) return [];
807 if (!iterable) return [];
523 - if (iterable.toArray) {
808 + if (iterable.toArray) return iterable.toArray();
524 - return iterable.toArray();
809 + var length = iterable.length, results = new Array(length);
525 - } else {
810 + while (length--) results[length] = iterable[length];
526 - var results = [];
527 - for (var i = 0, length = iterable.length; i < length; i++)
528 - results.push(iterable[i]);
529 return results;
811 return results;
530 }
812 }
531 - }
813 +
814 + if (Prototype.Browser.WebKit) {
815 + function $A(iterable) {
816 + if (!iterable) return [];
817 + if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
818 + iterable.toArray) return iterable.toArray();
819 + var length = iterable.length, results = new Array(length);
820 + while (length--) results[length] = iterable[length];
821 + return results;
822 + }
823 + }
824 +
825 + Array.from = $A;
532
826
533 Object.extend(Array.prototype, Enumerable);
827 Object.extend(Array.prototype, Enumerable);
534
828
535 - if (!Array.prototype._reverse)
829 + if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
536 - Array.prototype._reverse = Array.prototype.reverse;
537
830
538 Object.extend(Array.prototype, {
831 Object.extend(Array.prototype, {
539 _each: function(iterator) {
832 _each: function(iterator) {
540 for (var i = 0, length = this.length; i < length; i++)
833 for (var i = 0, length = this.length; i < length; i++)
541 iterator(this[i]);
834 iterator(this[i]);
542 },
835 },
543
836
544 clear: function() {
837 clear: function() {
545 this.length = 0;
838 this.length = 0;
546 return this;
839 return this;
547 },
840 },
548
841
549 first: function() {
842 first: function() {
550 return this[0];
843 return this[0];
551 },
844 },
552
845
553 last: function() {
846 last: function() {
554 return this[this.length - 1];
847 return this[this.length - 1];
555 },
848 },
556
849
557 compact: function() {
850 compact: function() {
558 return this.select(function(value) {
851 return this.select(function(value) {
559 return value != null;
852 return value != null;
560 });
853 });
561 },
854 },
562
855
563 flatten: function() {
856 flatten: function() {
564 return this.inject([], function(array, value) {
857 return this.inject([], function(array, value) {
565 - return array.concat(value && value.constructor == Array ?
858 + return array.concat(Object.isArray(value) ?
566 value.flatten() : [value]);
859 value.flatten() : [value]);
567 });
860 });
568 },
861 },
569
862
570 without: function() {
863 without: function() {
571 var values = $A(arguments);
864 var values = $A(arguments);
572 return this.select(function(value) {
865 return this.select(function(value) {
573 return !values.include(value);
866 return !values.include(value);
574 });
867 });
575 },
868 },
576
869
577 - indexOf: function(object) {
578 - for (var i = 0, length = this.length; i < length; i++)
579 - if (this[i] == object) return i;
580 - return -1;
581 - },
582 -
583 reverse: function(inline) {
870 reverse: function(inline) {
584 return (inline !== false ? this : this.toArray())._reverse();
871 return (inline !== false ? this : this.toArray())._reverse();
585 },
872 },
586
873
587 reduce: function() {
874 reduce: function() {
588 return this.length > 1 ? this : this[0];
875 return this.length > 1 ? this : this[0];
589 },
876 },
590
877
591 - uniq: function() {
878 + uniq: function(sorted) {
592 - return this.inject([], function(array, value) {
879 + return this.inject([], function(array, value, index) {
593 - return array.include(value) ? array : array.concat([value]);
880 + if (0 == index || (sorted ? array.last() != value : !array.include(value)))
881 + array.push(value);
882 + return array;
883 + });
884 + },
885 +
886 + intersect: function(array) {
887 + return this.uniq().findAll(function(item) {
888 + return array.detect(function(value) { return item === value });
594 });
889 });
595 },
890 },
596
891
597 clone: function() {
892 clone: function() {
598 return [].concat(this);
893 return [].concat(this);
599 },
894 },
600
895
601 size: function() {
896 size: function() {
602 return this.length;
897 return this.length;
603 },
898 },
604
899
605 inspect: function() {
900 inspect: function() {
606 return '[' + this.map(Object.inspect).join(', ') + ']';
901 return '[' + this.map(Object.inspect).join(', ') + ']';
902 + },
903 +
904 + toJSON: function() {
905 + var results = [];
906 + this.each(function(object) {
907 + var value = Object.toJSON(object);
908 + if (!Object.isUndefined(value)) results.push(value);
909 + });
910 + return '[' + results.join(', ') + ']';
607 }
911 }
608 });
912 });
609
913
914 + // use native browser JS 1.6 implementation if available
915 + if (Object.isFunction(Array.prototype.forEach))
916 + Array.prototype._each = Array.prototype.forEach;
917 +
918 + if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
919 + i || (i = 0);
920 + var length = this.length;
921 + if (i < 0) i = length + i;
922 + for (; i < length; i++)
923 + if (this[i] === item) return i;
924 + return -1;
925 + };
926 +
927 + if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
928 + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
929 + var n = this.slice(0, i).reverse().indexOf(item);
930 + return (n < 0) ? n : i - n - 1;
931 + };
932 +
610 Array.prototype.toArray = Array.prototype.clone;
933 Array.prototype.toArray = Array.prototype.clone;
611
934
612 function $w(string){
935 function $w(string) {
936 + if (!Object.isString(string)) return [];
613 string = string.strip();
937 string = string.strip();
614 return string ? string.split(/\s+/) : [];
938 return string ? string.split(/\s+/) : [];
615 }
939 }
616
940
617 - if(window.opera){
941 + if (Prototype.Browser.Opera){
618 Array.prototype.concat = function(){
942 Array.prototype.concat = function() {
619 var array = [];
943 var array = [];
620 for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
944 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621 for(var i = 0, length = arguments.length; i < length; i++) {
945 for (var i = 0, length = arguments.length; i < length; i++) {
622 - if(arguments[i].constructor == Array) {
946 + if (Object.isArray(arguments[i])) {
623 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
947 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624 array.push(arguments[i][j]);
948 array.push(arguments[i][j]);
625 } else {
949 } else {
626 array.push(arguments[i]);
950 array.push(arguments[i]);
627 }
951 }
628 }
952 }
629 return array;
953 return array;
630 - }
631 - }
632 - var Hash = function(obj) {
633 - Object.extend(this, obj || {});
634 };
954 };
635 -
955 + }
636 - Object.extend(Hash, {
956 + Object.extend(Number.prototype, {
637 - toQueryString: function(obj) {
957 + toColorPart: function() {
638 - var parts = [];
958 + return this.toPaddedString(2, 16);
639 -
959 + },
640 - this.prototype._each.call(obj, function(pair) {
960 +
641 - if (!pair.key) return;
961 + succ: function() {
642 -
962 + return this + 1;
643 - if (pair.value && pair.value.constructor == Array) {
963 + },
644 - var values = pair.value.compact();
964 +
645 - if (values.length < 2) pair.value = values.reduce();
965 + times: function(iterator) {
646 - else {
966 + $R(0, this, true).each(iterator);
647 - key = encodeURIComponent(pair.key);
967 + return this;
648 - values.each(function(value) {
968 + },
649 - value = value != undefined ? encodeURIComponent(value) : '';
969 +
650 - parts.push(key + '=' + encodeURIComponent(value));
970 + toPaddedString: function(length, radix) {
651 - });
971 + var string = this.toString(radix || 10);
652 - return;
972 + return '0'.times(length - string.length) + string;
653 - }
973 + },
654 - }
974 +
655 - if (pair.value == undefined) pair[1] = '';
975 + toJSON: function() {
656 - parts.push(pair.map(encodeURIComponent).join('='));
976 + return isFinite(this) ? this.toString() : 'null';
657 - });
658 -
659 - return parts.join('&');
660 }
977 }
661 });
978 });
662
979
663 - Object.extend(Hash.prototype, Enumerable);
980 + $w('abs round ceil floor').each(function(method){
664 - Object.extend(Hash.prototype, {
981 + Number.prototype[method] = Math[method].methodize();
982 + });
983 + function $H(object) {
984 + return new Hash(object);
985 + };
986 +
987 + var Hash = Class.create(Enumerable, (function() {
988 +
989 + function toQueryPair(key, value) {
990 + if (Object.isUndefined(value)) return key;
991 + return key + '=' + encodeURIComponent(String.interpret(value));
992 + }
993 +
994 + return {
995 + initialize: function(object) {
996 + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
997 + },
998 +
665 _each: function(iterator) {
999 _each: function(iterator) {
666 - for (var key in this) {
1000 + for (var key in this._object) {
667 - var value = this[key];
1001 + var value = this._object[key], pair = [key, value];
668 - if (value && value == Hash.prototype[key]) continue;
669 -
670 - var pair = [key, value];
671 pair.key = key;
1002 pair.key = key;
672 pair.value = value;
1003 pair.value = value;
673 iterator(pair);
1004 iterator(pair);
674 }
1005 }
675 },
1006 },
676
1007
1008 + set: function(key, value) {
1009 + return this._object[key] = value;
1010 + },
1011 +
1012 + get: function(key) {
1013 + return this._object[key];
1014 + },
1015 +
1016 + unset: function(key) {
1017 + var value = this._object[key];
1018 + delete this._object[key];
1019 + return value;
1020 + },
1021 +
1022 + toObject: function() {
1023 + return Object.clone(this._object);
1024 + },
1025 +
677 keys: function() {
1026 keys: function() {
678 return this.pluck('key');
1027 return this.pluck('key');
679 },
1028 },
680
1029
681 values: function() {
1030 values: function() {
682 return this.pluck('value');
1031 return this.pluck('value');
683 },
1032 },
684
1033
685 - merge: function(hash) {
1034 + index: function(value) {
686 - return $H(hash).inject(this, function(mergedHash, pair) {
1035 + var match = this.detect(function(pair) {
687 - mergedHash[pair.key] = pair.value;
1036 + return pair.value === value;
688 - return mergedHash;
689 });
1037 });
690 - },
1038 + return match && match.key;
691 -
1039 + },
692 - remove: function() {
1040 +
693 - var result;
1041 + merge: function(object) {
694 - for(var i = 0, length = arguments.length; i < length; i++) {
1042 + return this.clone().update(object);
695 - var value = this[arguments[i]];
1043 + },
696 - if (value !== undefined){
1044 +
697 - if (result === undefined) result = value;
1045 + update: function(object) {
698 - else {
1046 + return new Hash(object).inject(this, function(result, pair) {
699 - if (result.constructor != Array) result = [result];
1047 + result.set(pair.key, pair.value);
700 - result.push(value)
701 - }
702 - }
703 - delete this[arguments[i]];
704 - }
705 return result;
1048 return result;
1049 + });
706 },
1050 },
707
1051
708 toQueryString: function() {
1052 toQueryString: function() {
709 - return Hash.toQueryString(this);
1053 + return this.map(function(pair) {
1054 + var key = encodeURIComponent(pair.key), values = pair.value;
1055 +
1056 + if (values && typeof values == 'object') {
1057 + if (Object.isArray(values))
1058 + return values.map(toQueryPair.curry(key)).join('&');
1059 + }
1060 + return toQueryPair(key, values);
1061 + }).join('&');
710 },
1062 },
711
1063
712 inspect: function() {
1064 inspect: function() {
713 return '#<Hash:{' + this.map(function(pair) {
1065 return '#<Hash:{' + this.map(function(pair) {
714 return pair.map(Object.inspect).join(': ');
1066 return pair.map(Object.inspect).join(': ');
715 }).join(', ') + '}>';
1067 }).join(', ') + '}>';
716 - }
1068 + },
717 - });
1069 +
718 -
1070 + toJSON: function() {
719 - function $H(object) {
1071 + return Object.toJSON(this.toObject());
720 - if (object && object.constructor == Hash) return object;
1072 + },
721 - return new Hash(object);
1073 +
722 - };
1074 + clone: function() {
723 - ObjectRange = Class.create();
1075 + return new Hash(this);
724 - Object.extend(ObjectRange.prototype, Enumerable);
1076 + }
725 - Object.extend(ObjectRange.prototype, {
1077 + }
1078 + })());
1079 +
1080 + Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1081 + Hash.from = $H;
1082 + var ObjectRange = Class.create(Enumerable, {
726 initialize: function(start, end, exclusive) {
1083 initialize: function(start, end, exclusive) {
727 this.start = start;
1084 this.start = start;
728 this.end = end;
1085 this.end = end;
729 this.exclusive = exclusive;
1086 this.exclusive = exclusive;
730 },
1087 },
731
1088
732 _each: function(iterator) {
1089 _each: function(iterator) {
733 var value = this.start;
1090 var value = this.start;
734 while (this.include(value)) {
1091 while (this.include(value)) {
735 iterator(value);
1092 iterator(value);
736 value = value.succ();
1093 value = value.succ();
737 }
1094 }
738 },
1095 },
739
1096
740 include: function(value) {
1097 include: function(value) {
741 if (value < this.start)
1098 if (value < this.start)
742 return false;
1099 return false;
743 if (this.exclusive)
1100 if (this.exclusive)
744 return value < this.end;
1101 return value < this.end;
745 return value <= this.end;
1102 return value <= this.end;
746 }
1103 }
747 });
1104 });
748
1105
749 var $R = function(start, end, exclusive) {
1106 var $R = function(start, end, exclusive) {
750 return new ObjectRange(start, end, exclusive);
1107 return new ObjectRange(start, end, exclusive);
751 - }
1108 + };
752
1109
753 var Ajax = {
1110 var Ajax = {
754 getTransport: function() {
1111 getTransport: function() {
755 return Try.these(
1112 return Try.these(
756 function() {return new XMLHttpRequest()},
1113 function() {return new XMLHttpRequest()},
757 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1114 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
758 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1115 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
759 ) || false;
1116 ) || false;
760 },
1117 },
761
1118
762 activeRequestCount: 0
1119 activeRequestCount: 0
763 - }
1120 + };
764
1121
765 Ajax.Responders = {
1122 Ajax.Responders = {
766 responders: [],
1123 responders: [],
767
1124
768 _each: function(iterator) {
1125 _each: function(iterator) {
769 this.responders._each(iterator);
1126 this.responders._each(iterator);
770 },
1127 },
771
1128
772 register: function(responder) {
1129 register: function(responder) {
773 if (!this.include(responder))
1130 if (!this.include(responder))
774 this.responders.push(responder);
1131 this.responders.push(responder);
775 },
1132 },
776
1133
777 unregister: function(responder) {
1134 unregister: function(responder) {
778 this.responders = this.responders.without(responder);
1135 this.responders = this.responders.without(responder);
779 },
1136 },
780
1137
781 dispatch: function(callback, request, transport, json) {
1138 dispatch: function(callback, request, transport, json) {
782 this.each(function(responder) {
1139 this.each(function(responder) {
783 - if (typeof responder[callback] == 'function') {
1140 + if (Object.isFunction(responder[callback])) {
784 try {
1141 try {
785 responder[callback].apply(responder, [request, transport, json]);
1142 responder[callback].apply(responder, [request, transport, json]);
786 } catch (e) {}
1143 } catch (e) { }
787 }
1144 }
788 });
1145 });
789 }
1146 }
790 };
1147 };
791
1148
792 Object.extend(Ajax.Responders, Enumerable);
1149 Object.extend(Ajax.Responders, Enumerable);
793
1150
794 Ajax.Responders.register({
1151 Ajax.Responders.register({
795 - onCreate: function() {
1152 + onCreate: function() { Ajax.activeRequestCount++ },
796 - Ajax.activeRequestCount++;
1153 + onComplete: function() { Ajax.activeRequestCount-- }
797 - },
798 - onComplete: function() {
799 - Ajax.activeRequestCount--;
800 - }
801 });
1154 });
802
1155
803 - Ajax.Base = function() {};
1156 + Ajax.Base = Class.create({
804 - Ajax.Base.prototype = {
1157 + initialize: function(options) {
805 - setOptions: function(options) {
806 this.options = {
1158 this.options = {
807 method: 'post',
1159 method: 'post',
808 asynchronous: true,
1160 asynchronous: true,
809 contentType: 'application/x-www-form-urlencoded',
1161 contentType: 'application/x-www-form-urlencoded',
810 encoding: 'UTF-8',
1162 encoding: 'UTF-8',
811 - parameters: ''
1163 + parameters: '',
812 - }
1164 + evalJSON: true,
1165 + evalJS: true
1166 + };
813 Object.extend(this.options, options || {});
1167 Object.extend(this.options, options || { });
814
1168
815 this.options.method = this.options.method.toLowerCase();
1169 this.options.method = this.options.method.toLowerCase();
816 - if (typeof this.options.parameters == 'string')
1170 +
1171 + if (Object.isString(this.options.parameters))
817 this.options.parameters = this.options.parameters.toQueryParams();
1172 this.options.parameters = this.options.parameters.toQueryParams();
818 - }
1173 + else if (Object.isHash(this.options.parameters))
819 - }
1174 + this.options.parameters = this.options.parameters.toObject();
820 -
1175 + }
821 - Ajax.Request = Class.create();
1176 + });
822 - Ajax.Request.Events =
1177 +
823 - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1178 + Ajax.Request = Class.create(Ajax.Base, {
824 -
825 - Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
826 _complete: false,
1179 _complete: false,
827
1180
828 - initialize: function(url, options) {
1181 + initialize: function($super, url, options) {
1182 + $super(options);
829 this.transport = Ajax.getTransport();
1183 this.transport = Ajax.getTransport();
830 - this.setOptions(options);
831 this.request(url);
1184 this.request(url);
832 },
1185 },
833
1186
834 request: function(url) {
1187 request: function(url) {
835 this.url = url;
1188 this.url = url;
836 this.method = this.options.method;
1189 this.method = this.options.method;
837 - var params = this.options.parameters;
1190 + var params = Object.clone(this.options.parameters);
838
1191
839 if (!['get', 'post'].include(this.method)) {
1192 if (!['get', 'post'].include(this.method)) {
840 // simulate other verbs over post
1193 // simulate other verbs over post
841 params['_method'] = this.method;
1194 params['_method'] = this.method;
842 this.method = 'post';
1195 this.method = 'post';
843 }
1196 }
844
1197
845 - params = Hash.toQueryString(params);
1198 + this.parameters = params;
846 - if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
1199 +
847 -
1200 + if (params = Object.toQueryString(params)) {
848 // when GET, append parameters to URL
1201 // when GET, append parameters to URL
849 - if (this.method == 'get' && params)
1202 + if (this.method == 'get')
850 - this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
1203 + this.url += (this.url.include('?') ? '&' : '?') + params;
1204 + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1205 + params += '&_=';
1206 + }
851
1207
852 try {
1208 try {
853 - Ajax.Responders.dispatch('onCreate', this, this.transport);
1209 + var response = new Ajax.Response(this);
1210 + if (this.options.onCreate) this.options.onCreate(response);
1211 + Ajax.Responders.dispatch('onCreate', this, response);
854
1212
855 this.transport.open(this.method.toUpperCase(), this.url,
1213 this.transport.open(this.method.toUpperCase(), this.url,
856 this.options.asynchronous);
1214 this.options.asynchronous);
857
1215
858 - if (this.options.asynchronous)
1216 + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
859 - setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
860
1217
861 this.transport.onreadystatechange = this.onStateChange.bind(this);
1218 this.transport.onreadystatechange = this.onStateChange.bind(this);
862 this.setRequestHeaders();
1219 this.setRequestHeaders();
863
1220
864 - var body = this.method == 'post' ? (this.options.postBody || params) : null;
1221 + this.body = this.method == 'post' ? (this.options.postBody || params) : null;
865 -
1222 + this.transport.send(this.body);
866 - this.transport.send(body);
867
1223
868 /* Force Firefox to handle ready state 4 for synchronous requests */
1224 /* Force Firefox to handle ready state 4 for synchronous requests */
869 if (!this.options.asynchronous && this.transport.overrideMimeType)
1225 if (!this.options.asynchronous && this.transport.overrideMimeType)
870 this.onStateChange();
1226 this.onStateChange();
871
1227
872 }
1228 }
873 catch (e) {
1229 catch (e) {
874 this.dispatchException(e);
1230 this.dispatchException(e);
875 }
1231 }
876 },
1232 },
877
1233
878 onStateChange: function() {
1234 onStateChange: function() {
879 var readyState = this.transport.readyState;
1235 var readyState = this.transport.readyState;
880 if (readyState > 1 && !((readyState == 4) && this._complete))
1236 if (readyState > 1 && !((readyState == 4) && this._complete))
881 this.respondToReadyState(this.transport.readyState);
1237 this.respondToReadyState(this.transport.readyState);
882 },
1238 },
883
1239
884 setRequestHeaders: function() {
1240 setRequestHeaders: function() {
885 var headers = {
1241 var headers = {
886 'X-Requested-With': 'XMLHttpRequest',
1242 'X-Requested-With': 'XMLHttpRequest',
887 'X-Prototype-Version': Prototype.Version,
1243 'X-Prototype-Version': Prototype.Version,
888 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1244 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
889 };
1245 };
890
1246
891 if (this.method == 'post') {
1247 if (this.method == 'post') {
892 headers['Content-type'] = this.options.contentType +
1248 headers['Content-type'] = this.options.contentType +
893 (this.options.encoding ? '; charset=' + this.options.encoding : '');
1249 (this.options.encoding ? '; charset=' + this.options.encoding : '');
894
1250
895 /* Force "Connection: close" for older Mozilla browsers to work
1251 /* Force "Connection: close" for older Mozilla browsers to work
896 * around a bug where XMLHttpRequest sends an incorrect
1252 * around a bug where XMLHttpRequest sends an incorrect
897 * Content-length header. See Mozilla Bugzilla #246651.
1253 * Content-length header. See Mozilla Bugzilla #246651.
898 */
1254 */
899 if (this.transport.overrideMimeType &&
1255 if (this.transport.overrideMimeType &&
900 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1256 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
901 headers['Connection'] = 'close';
1257 headers['Connection'] = 'close';
902 }
1258 }
903
1259
904 // user-defined headers
1260 // user-defined headers
905 if (typeof this.options.requestHeaders == 'object') {
1261 if (typeof this.options.requestHeaders == 'object') {
906 var extras = this.options.requestHeaders;
1262 var extras = this.options.requestHeaders;
907
1263
908 - if (typeof extras.push == 'function')
1264 + if (Object.isFunction(extras.push))
909 for (var i = 0, length = extras.length; i < length; i += 2)
1265 for (var i = 0, length = extras.length; i < length; i += 2)
910 headers[extras[i]] = extras[i+1];
1266 headers[extras[i]] = extras[i+1];
911 else
1267 else
912 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1268 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
913 }
1269 }
914
1270
915 for (var name in headers)
1271 for (var name in headers)
916 this.transport.setRequestHeader(name, headers[name]);
1272 this.transport.setRequestHeader(name, headers[name]);
917 },
1273 },
918
1274
919 success: function() {
1275 success: function() {
920 - return !this.transport.status
1276 + var status = this.getStatus();
921 - || (this.transport.status >= 200 && this.transport.status < 300);
1277 + return !status || (status >= 200 && status < 300);
1278 + },
1279 +
1280 + getStatus: function() {
1281 + try {
1282 + return this.transport.status || 0;
1283 + } catch (e) { return 0 }
922 },
1284 },
923
1285
924 respondToReadyState: function(readyState) {
1286 respondToReadyState: function(readyState) {
925 - var state = Ajax.Request.Events[readyState];
1287 + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
926 - var transport = this.transport, json = this.evalJSON();
927
1288
928 if (state == 'Complete') {
1289 if (state == 'Complete') {
929 try {
1290 try {
930 this._complete = true;
1291 this._complete = true;
931 - (this.options['on' + this.transport.status]
1292 + (this.options['on' + response.status]
932 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1293 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
933 - || Prototype.emptyFunction)(transport, json);
1294 + || Prototype.emptyFunction)(response, response.headerJSON);
934 } catch (e) {
1295 } catch (e) {
935 this.dispatchException(e);
1296 this.dispatchException(e);
936 }
1297 }
937
1298
938 - if ((this.getHeader('Content-type') || 'text/javascript').strip().
1299 + var contentType = response.getHeader('Content-type');
939 - match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1300 + if (this.options.evalJS == 'force'
1301 + || (this.options.evalJS && contentType
1302 + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
940 this.evalResponse();
1303 this.evalResponse();
941 }
1304 }
942
1305
943 try {
1306 try {
944 - (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1307 + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
945 - Ajax.Responders.dispatch('on' + state, this, transport, json);
1308 + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
946 } catch (e) {
1309 } catch (e) {
947 this.dispatchException(e);
1310 this.dispatchException(e);
948 }
1311 }
949
1312
950 if (state == 'Complete') {
1313 if (state == 'Complete') {
951 // avoid memory leak in MSIE: clean up
1314 // avoid memory leak in MSIE: clean up
952 this.transport.onreadystatechange = Prototype.emptyFunction;
1315 this.transport.onreadystatechange = Prototype.emptyFunction;
953 }
1316 }
954 },
1317 },
955
1318
956 getHeader: function(name) {
1319 getHeader: function(name) {
957 try {
1320 try {
958 return this.transport.getResponseHeader(name);
1321 return this.transport.getResponseHeader(name);
959 } catch (e) { return null }
1322 } catch (e) { return null }
960 },
1323 },
961
1324
962 - evalJSON: function() {
963 - try {
964 - var json = this.getHeader('X-JSON');
965 - return json ? eval('(' + json + ')') : null;
966 - } catch (e) { return null }
967 - },
968 -
969 evalResponse: function() {
1325 evalResponse: function() {
970 try {
1326 try {
971 - return eval(this.transport.responseText);
1327 + return eval((this.transport.responseText || '').unfilterJSON());
972 } catch (e) {
1328 } catch (e) {
973 this.dispatchException(e);
1329 this.dispatchException(e);
974 }
1330 }
975 },
1331 },
976
1332
977 dispatchException: function(exception) {
1333 dispatchException: function(exception) {
978 (this.options.onException || Prototype.emptyFunction)(this, exception);
1334 (this.options.onException || Prototype.emptyFunction)(this, exception);
979 Ajax.Responders.dispatch('onException', this, exception);
1335 Ajax.Responders.dispatch('onException', this, exception);
980 }
1336 }
981 });
1337 });
982
1338
983 - Ajax.Updater = Class.create();
1339 + Ajax.Request.Events =
984 -
1340 + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
985 - Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1341 +
986 - initialize: function(container, url, options) {
1342 + Ajax.Response = Class.create({
1343 + initialize: function(request){
1344 + this.request = request;
1345 + var transport = this.transport = request.transport,
1346 + readyState = this.readyState = transport.readyState;
1347 +
1348 + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1349 + this.status = this.getStatus();
1350 + this.statusText = this.getStatusText();
1351 + this.responseText = String.interpret(transport.responseText);
1352 + this.headerJSON = this._getHeaderJSON();
1353 + }
1354 +
1355 + if(readyState == 4) {
1356 + var xml = transport.responseXML;
1357 + this.responseXML = Object.isUndefined(xml) ? null : xml;
1358 + this.responseJSON = this._getResponseJSON();
1359 + }
1360 + },
1361 +
1362 + status: 0,
1363 + statusText: '',
1364 +
1365 + getStatus: Ajax.Request.prototype.getStatus,
1366 +
1367 + getStatusText: function() {
1368 + try {
1369 + return this.transport.statusText || '';
1370 + } catch (e) { return '' }
1371 + },
1372 +
1373 + getHeader: Ajax.Request.prototype.getHeader,
1374 +
1375 + getAllHeaders: function() {
1376 + try {
1377 + return this.getAllResponseHeaders();
1378 + } catch (e) { return null }
1379 + },
1380 +
1381 + getResponseHeader: function(name) {
1382 + return this.transport.getResponseHeader(name);
1383 + },
1384 +
1385 + getAllResponseHeaders: function() {
1386 + return this.transport.getAllResponseHeaders();
1387 + },
1388 +
1389 + _getHeaderJSON: function() {
1390 + var json = this.getHeader('X-JSON');
1391 + if (!json) return null;
1392 + json = decodeURIComponent(escape(json));
1393 + try {
1394 + return json.evalJSON(this.request.options.sanitizeJSON);
1395 + } catch (e) {
1396 + this.request.dispatchException(e);
1397 + }
1398 + },
1399 +
1400 + _getResponseJSON: function() {
1401 + var options = this.request.options;
1402 + if (!options.evalJSON || (options.evalJSON != 'force' &&
1403 + !(this.getHeader('Content-type') || '').include('application/json')) ||
1404 + this.responseText.blank())
1405 + return null;
1406 + try {
1407 + return this.responseText.evalJSON(options.sanitizeJSON);
1408 + } catch (e) {
1409 + this.request.dispatchException(e);
1410 + }
1411 + }
1412 + });
1413 +
1414 + Ajax.Updater = Class.create(Ajax.Request, {
1415 + initialize: function($super, container, url, options) {
987 this.container = {
1416 this.container = {
988 success: (container.success || container),
1417 success: (container.success || container),
989 failure: (container.failure || (container.success ? null : container))
1418 failure: (container.failure || (container.success ? null : container))
990 - }
1419 + };
991 -
1420 +
992 - this.transport = Ajax.getTransport();
1421 + options = Object.clone(options);
993 - this.setOptions(options);
1422 + var onComplete = options.onComplete;
994 -
1423 + options.onComplete = (function(response, json) {
995 - var onComplete = this.options.onComplete || Prototype.emptyFunction;
1424 + this.updateContent(response.responseText);
996 - this.options.onComplete = (function(transport, param) {
1425 + if (Object.isFunction(onComplete)) onComplete(response, json);
997 - this.updateContent();
998 - onComplete(transport, param);
999 }).bind(this);
1426 }).bind(this);
1000
1427
1001 - this.request(url);
1428 + $super(url, options);
1002 - },
1429 + },
1003 -
1430 +
1004 - updateContent: function() {
1431 + updateContent: function(responseText) {
1005 - var receiver = this.container[this.success() ? 'success' : 'failure'];
1432 + var receiver = this.container[this.success() ? 'success' : 'failure'],
1006 - var response = this.transport.responseText;
1433 + options = this.options;
1007 -
1434 +
1008 - if (!this.options.evalScripts) response = response.stripScripts();
1435 + if (!options.evalScripts) responseText = responseText.stripScripts();
1009
1436
1010 if (receiver = $(receiver)) {
1437 if (receiver = $(receiver)) {
1011 - if (this.options.insertion)
1438 + if (options.insertion) {
1012 - new this.options.insertion(receiver, response);
1439 + if (Object.isString(options.insertion)) {
1013 - else
1440 + var insertion = { }; insertion[options.insertion] = responseText;
1014 - receiver.update(response);
1441 + receiver.insert(insertion);
1015 - }
1442 + }
1016 -
1443 + else options.insertion(receiver, responseText);
1017 - if (this.success()) {
1444 + }
1018 - if (this.onComplete)
1445 + else receiver.update(responseText);
1019 - setTimeout(this.onComplete.bind(this), 10);
1020 }
1446 }
1021 }
1447 }
1022 });
1448 });
1023
1449
1024 - Ajax.PeriodicalUpdater = Class.create();
1450 + Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1025 - Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1451 + initialize: function($super, container, url, options) {
1026 - initialize: function(container, url, options) {
1452 + $super(options);
1027 - this.setOptions(options);
1028 this.onComplete = this.options.onComplete;
1453 this.onComplete = this.options.onComplete;
1029
1454
1030 this.frequency = (this.options.frequency || 2);
1455 this.frequency = (this.options.frequency || 2);
1031 this.decay = (this.options.decay || 1);
1456 this.decay = (this.options.decay || 1);
1032
1457
1033 this.updater = {};
1458 this.updater = { };
1034 this.container = container;
1459 this.container = container;
1035 this.url = url;
1460 this.url = url;
1036
1461
1037 this.start();
1462 this.start();
1038 },
1463 },
1039
1464
1040 start: function() {
1465 start: function() {
1041 this.options.onComplete = this.updateComplete.bind(this);
1466 this.options.onComplete = this.updateComplete.bind(this);
1042 this.onTimerEvent();
1467 this.onTimerEvent();
1043 },
1468 },
1044
1469
1045 stop: function() {
1470 stop: function() {
1046 this.updater.options.onComplete = undefined;
1471 this.updater.options.onComplete = undefined;
1047 clearTimeout(this.timer);
1472 clearTimeout(this.timer);
1048 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1473 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049 },
1474 },
1050
1475
1051 - updateComplete: function(request) {
1476 + updateComplete: function(response) {
1052 if (this.options.decay) {
1477 if (this.options.decay) {
1053 - this.decay = (request.responseText == this.lastText ?
1478 + this.decay = (response.responseText == this.lastText ?
1054 this.decay * this.options.decay : 1);
1479 this.decay * this.options.decay : 1);
1055
1480
1056 - this.lastText = request.responseText;
1481 + this.lastText = response.responseText;
1057 - }
1482 + }
1058 - this.timer = setTimeout(this.onTimerEvent.bind(this),
1483 + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1059 - this.decay * this.frequency * 1000);
1060 },
1484 },
1061
1485
1062 onTimerEvent: function() {
1486 onTimerEvent: function() {
1063 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1487 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1064 }
1488 }
1065 });
1489 });
1066 function $(element) {
1490 function $(element) {
1067 if (arguments.length > 1) {
1491 if (arguments.length > 1) {
1068 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1492 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1069 elements.push($(arguments[i]));
1493 elements.push($(arguments[i]));
1070 return elements;
1494 return elements;
1071 }
1495 }
1072 - if (typeof element == 'string')
1496 + if (Object.isString(element))
1073 element = document.getElementById(element);
1497 element = document.getElementById(element);
1074 return Element.extend(element);
1498 return Element.extend(element);
1075 }
1499 }
1076
1500
1077 if (Prototype.BrowserFeatures.XPath) {
1501 if (Prototype.BrowserFeatures.XPath) {
1078 document._getElementsByXPath = function(expression, parentElement) {
1502 document._getElementsByXPath = function(expression, parentElement) {
1079 var results = [];
1503 var results = [];
1080 var query = document.evaluate(expression, $(parentElement) || document,
1504 var query = document.evaluate(expression, $(parentElement) || document,
1081 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1505 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082 for (var i = 0, length = query.snapshotLength; i < length; i++)
1506 for (var i = 0, length = query.snapshotLength; i < length; i++)
1083 - results.push(query.snapshotItem(i));
1507 + results.push(Element.extend(query.snapshotItem(i)));
1084 return results;
1508 return results;
1085 };
1509 };
1086 }
1510 }
1087
1511
1088 - document.getElementsByClassName = function(className, parentElement) {
1089 - if (Prototype.BrowserFeatures.XPath) {
1090 - var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1091 - return document._getElementsByXPath(q, parentElement);
1092 - } else {
1093 - var children = ($(parentElement) || document.body).getElementsByTagName('*');
1094 - var elements = [], child;
1095 - for (var i = 0, length = children.length; i < length; i++) {
1096 - child = children[i];
1097 - if (Element.hasClassName(child, className))
1098 - elements.push(Element.extend(child));
1099 - }
1100 - return elements;
1101 - }
1102 - };
1103 -
1104 /*--------------------------------------------------------------------------*/
1512 /*--------------------------------------------------------------------------*/
1105
1513
1106 - if (!window.Element)
1514 + if (!window.Node) var Node = { };
1107 - var Element = new Object();
1515 +
1108 -
1516 + if (!Node.ELEMENT_NODE) {
1109 - Element.extend = function(element) {
1517 + // DOM level 2 ECMAScript Language Binding
1110 - if (!element || _nativeExtensions || element.nodeType == 3) return element;
1518 + Object.extend(Node, {
1111 -
1519 + ELEMENT_NODE: 1,
1112 - if (!element._extended && element.tagName && element != window) {
1520 + ATTRIBUTE_NODE: 2,
1113 - var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1521 + TEXT_NODE: 3,
1114 -
1522 + CDATA_SECTION_NODE: 4,
1115 - if (element.tagName == 'FORM')
1523 + ENTITY_REFERENCE_NODE: 5,
1116 - Object.extend(methods, Form.Methods);
1524 + ENTITY_NODE: 6,
1117 - if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1525 + PROCESSING_INSTRUCTION_NODE: 7,
1118 - Object.extend(methods, Form.Element.Methods);
1526 + COMMENT_NODE: 8,
1119 -
1527 + DOCUMENT_NODE: 9,
1120 - Object.extend(methods, Element.Methods.Simulated);
1528 + DOCUMENT_TYPE_NODE: 10,
1121 -
1529 + DOCUMENT_FRAGMENT_NODE: 11,
1122 - for (var property in methods) {
1530 + NOTATION_NODE: 12
1123 - var value = methods[property];
1531 + });
1124 - if (typeof value == 'function' && !(property in element))
1532 + }
1125 - element[property] = cache.findOrStore(value);
1533 +
1126 - }
1534 + (function() {
1127 - }
1535 + var element = this.Element;
1128 -
1536 + this.Element = function(tagName, attributes) {
1129 - element._extended = true;
1537 + attributes = attributes || { };
1130 - return element;
1538 + tagName = tagName.toLowerCase();
1539 + var cache = Element.cache;
1540 + if (Prototype.Browser.IE && attributes.name) {
1541 + tagName = '<' + tagName + ' name="' + attributes.name + '">';
1542 + delete attributes.name;
1543 + return Element.writeAttribute(document.createElement(tagName), attributes);
1544 + }
1545 + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1546 + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1131 };
1547 };
1132 -
1548 + Object.extend(this.Element, element || { });
1133 - Element.extend.cache = {
1549 + }).call(window);
1134 - findOrStore: function(value) {
1550 +
1135 - return this[value] = this[value] || function() {
1551 + Element.cache = { };
1136 - return value.apply(null, [this].concat($A(arguments)));
1137 - }
1138 - }
1139 - };
1140
1552
1141 Element.Methods = {
1553 Element.Methods = {
1142 visible: function(element) {
1554 visible: function(element) {
1143 return $(element).style.display != 'none';
1555 return $(element).style.display != 'none';
1144 },
1556 },
1145
1557
1146 toggle: function(element) {
1558 toggle: function(element) {
1147 element = $(element);
1559 element = $(element);
1148 Element[Element.visible(element) ? 'hide' : 'show'](element);
1560 Element[Element.visible(element) ? 'hide' : 'show'](element);
1149 return element;
1561 return element;
1150 },
1562 },
1151
1563
1152 hide: function(element) {
1564 hide: function(element) {
1153 $(element).style.display = 'none';
1565 $(element).style.display = 'none';
1154 return element;
1566 return element;
1155 },
1567 },
1156
1568
1157 show: function(element) {
1569 show: function(element) {
1158 $(element).style.display = '';
1570 $(element).style.display = '';
1159 return element;
1571 return element;
1160 },
1572 },
1161
1573
1162 remove: function(element) {
1574 remove: function(element) {
1163 element = $(element);
1575 element = $(element);
1164 element.parentNode.removeChild(element);
1576 element.parentNode.removeChild(element);
1165 return element;
1577 return element;
1166 },
1578 },
1167
1579
1168 - update: function(element, html) {
1580 + update: function(element, content) {
1169 - html = typeof html == 'undefined' ? '' : html.toString();
1581 + element = $(element);
1170 - $(element).innerHTML = html.stripScripts();
1582 + if (content && content.toElement) content = content.toElement();
1171 - setTimeout(function() {html.evalScripts()}, 10);
1583 + if (Object.isElement(content)) return element.update().insert(content);
1584 + content = Object.toHTML(content);
1585 + element.innerHTML = content.stripScripts();
1586 + content.evalScripts.bind(content).defer();
1172 return element;
1587 return element;
1173 },
1588 },
1174
1589
1175 - replace: function(element, html) {
1590 + replace: function(element, content) {
1591 + element = $(element);
1592 + if (content && content.toElement) content = content.toElement();
1593 + else if (!Object.isElement(content)) {
1594 + content = Object.toHTML(content);
1595 + var range = element.ownerDocument.createRange();
1596 + range.selectNode(element);
1597 + content.evalScripts.bind(content).defer();
1598 + content = range.createContextualFragment(content.stripScripts());
1599 + }
1600 + element.parentNode.replaceChild(content, element);
1601 + return element;
1602 + },
1603 +
1604 + insert: function(element, insertions) {
1176 element = $(element);
1605 element = $(element);
1177 - html = typeof html == 'undefined' ? '' : html.toString();
1606 +
1178 - if (element.outerHTML) {
1607 + if (Object.isString(insertions) || Object.isNumber(insertions) ||
1179 - element.outerHTML = html.stripScripts();
1608 + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1180 - } else {
1609 + insertions = {bottom:insertions};
1181 - var range = element.ownerDocument.createRange();
1610 +
1182 - range.selectNodeContents(element);
1611 + var content, t, range;
1183 - element.parentNode.replaceChild(
1612 +
1184 - range.createContextualFragment(html.stripScripts()), element);
1613 + for (position in insertions) {
1185 - }
1614 + content = insertions[position];
1186 - setTimeout(function() {html.evalScripts()}, 10);
1615 + position = position.toLowerCase();
1616 + t = Element._insertionTranslations[position];
1617 +
1618 + if (content && content.toElement) content = content.toElement();
1619 + if (Object.isElement(content)) {
1620 + t.insert(element, content);
1621 + continue;
1622 + }
1623 +
1624 + content = Object.toHTML(content);
1625 +
1626 + range = element.ownerDocument.createRange();
1627 + t.initializeRange(element, range);
1628 + t.insert(element, range.createContextualFragment(content.stripScripts()));
1629 +
1630 + content.evalScripts.bind(content).defer();
1631 + }
1632 +
1187 return element;
1633 return element;
1188 },
1634 },
1189
1635
1636 + wrap: function(element, wrapper, attributes) {
1637 + element = $(element);
1638 + if (Object.isElement(wrapper))
1639 + $(wrapper).writeAttribute(attributes || { });
1640 + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1641 + else wrapper = new Element('div', wrapper);
1642 + if (element.parentNode)
1643 + element.parentNode.replaceChild(wrapper, element);
1644 + wrapper.appendChild(element);
1645 + return wrapper;
1646 + },
1647 +
1190 inspect: function(element) {
1648 inspect: function(element) {
1191 element = $(element);
1649 element = $(element);
1192 var result = '<' + element.tagName.toLowerCase();
1650 var result = '<' + element.tagName.toLowerCase();
1193 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1651 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1194 var property = pair.first(), attribute = pair.last();
1652 var property = pair.first(), attribute = pair.last();
1195 var value = (element[property] || '').toString();
1653 var value = (element[property] || '').toString();
1196 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1654 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1197 });
1655 });
1198 return result + '>';
1656 return result + '>';
1199 },
1657 },
1200
1658
1201 recursivelyCollect: function(element, property) {
1659 recursivelyCollect: function(element, property) {
1202 element = $(element);
1660 element = $(element);
1203 var elements = [];
1661 var elements = [];
1204 while (element = element[property])
1662 while (element = element[property])
1205 if (element.nodeType == 1)
1663 if (element.nodeType == 1)
1206 elements.push(Element.extend(element));
1664 elements.push(Element.extend(element));
1207 return elements;
1665 return elements;
1208 },
1666 },
1209
1667
1210 ancestors: function(element) {
1668 ancestors: function(element) {
1211 return $(element).recursivelyCollect('parentNode');
1669 return $(element).recursivelyCollect('parentNode');
1212 },
1670 },
1213
1671
1214 descendants: function(element) {
1672 descendants: function(element) {
1215 - return $A($(element).getElementsByTagName('*'));
1673 + return $(element).getElementsBySelector("*");
1674 + },
1675 +
1676 + firstDescendant: function(element) {
1677 + element = $(element).firstChild;
1678 + while (element && element.nodeType != 1) element = element.nextSibling;
1679 + return $(element);
1216 },
1680 },
1217
1681
1218 immediateDescendants: function(element) {
1682 immediateDescendants: function(element) {
1219 if (!(element = $(element).firstChild)) return [];
1683 if (!(element = $(element).firstChild)) return [];
1220 while (element && element.nodeType != 1) element = element.nextSibling;
1684 while (element && element.nodeType != 1) element = element.nextSibling;
1221 if (element) return [element].concat($(element).nextSiblings());
1685 if (element) return [element].concat($(element).nextSiblings());
1222 return [];
1686 return [];
1223 },
1687 },
1224
1688
1225 previousSiblings: function(element) {
1689 previousSiblings: function(element) {
1226 return $(element).recursivelyCollect('previousSibling');
1690 return $(element).recursivelyCollect('previousSibling');
1227 },
1691 },
1228
1692
1229 nextSiblings: function(element) {
1693 nextSiblings: function(element) {
1230 return $(element).recursivelyCollect('nextSibling');
1694 return $(element).recursivelyCollect('nextSibling');
1231 },
1695 },
1232
1696
1233 siblings: function(element) {
1697 siblings: function(element) {
1234 element = $(element);
1698 element = $(element);
1235 return element.previousSiblings().reverse().concat(element.nextSiblings());
1699 return element.previousSiblings().reverse().concat(element.nextSiblings());
1236 },
1700 },
1237
1701
1238 match: function(element, selector) {
1702 match: function(element, selector) {
1239 - if (typeof selector == 'string')
1703 + if (Object.isString(selector))
1240 selector = new Selector(selector);
1704 selector = new Selector(selector);
1241 return selector.match($(element));
1705 return selector.match($(element));
1242 },
1706 },
1243
1707
1244 up: function(element, expression, index) {
1708 up: function(element, expression, index) {
1245 - return Selector.findElement($(element).ancestors(), expression, index);
1709 + element = $(element);
1710 + if (arguments.length == 1) return $(element.parentNode);
1711 + var ancestors = element.ancestors();
1712 + return expression ? Selector.findElement(ancestors, expression, index) :
1713 + ancestors[index || 0];
1246 },
1714 },
1247
1715
1248 down: function(element, expression, index) {
1716 down: function(element, expression, index) {
1249 - return Selector.findElement($(element).descendants(), expression, index);
1717 + element = $(element);
1718 + if (arguments.length == 1) return element.firstDescendant();
1719 + var descendants = element.descendants();
1720 + return expression ? Selector.findElement(descendants, expression, index) :
1721 + descendants[index || 0];
1250 },
1722 },
1251
1723
1252 previous: function(element, expression, index) {
1724 previous: function(element, expression, index) {
1253 - return Selector.findElement($(element).previousSiblings(), expression, index);
1725 + element = $(element);
1726 + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1727 + var previousSiblings = element.previousSiblings();
1728 + return expression ? Selector.findElement(previousSiblings, expression, index) :
1729 + previousSiblings[index || 0];
1254 },
1730 },
1255
1731
1256 next: function(element, expression, index) {
1732 next: function(element, expression, index) {
1257 - return Selector.findElement($(element).nextSiblings(), expression, index);
1733 + element = $(element);
1258 - },
1734 + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1259 -
1735 + var nextSiblings = element.nextSiblings();
1260 - getElementsBySelector: function() {
1736 + return expression ? Selector.findElement(nextSiblings, expression, index) :
1737 + nextSiblings[index || 0];
1738 + },
1739 +
1740 + select: function() {
1261 var args = $A(arguments), element = $(args.shift());
1741 var args = $A(arguments), element = $(args.shift());
1262 return Selector.findChildElements(element, args);
1742 return Selector.findChildElements(element, args);
1263 },
1743 },
1264
1744
1265 - getElementsByClassName: function(element, className) {
1745 + adjacent: function() {
1266 - return document.getElementsByClassName(className, element);
1746 + var args = $A(arguments), element = $(args.shift());
1747 + return Selector.findChildElements(element.parentNode, args).without(element);
1748 + },
1749 +
1750 + identify: function(element) {
1751 + element = $(element);
1752 + var id = element.readAttribute('id'), self = arguments.callee;
1753 + if (id) return id;
1754 + do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1755 + element.writeAttribute('id', id);
1756 + return id;
1267 },
1757 },
1268
1758
1269 readAttribute: function(element, name) {
1759 readAttribute: function(element, name) {
1270 element = $(element);
1760 element = $(element);
1271 - if (document.all && !window.opera) {
1761 + if (Prototype.Browser.IE) {
1272 - var t = Element._attributeTranslations;
1762 + var t = Element._attributeTranslations.read;
1273 if (t.values[name]) return t.values[name](element, name);
1763 if (t.values[name]) return t.values[name](element, name);
1274 if (t.names[name]) name = t.names[name];
1764 if (t.names[name]) name = t.names[name];
1275 - var attribute = element.attributes[name];
1765 + if (name.include(':')) {
1276 - if(attribute) return attribute.nodeValue;
1766 + return (!element.attributes || !element.attributes[name]) ? null :
1767 + element.attributes[name].value;
1768 + }
1277 }
1769 }
1278 return element.getAttribute(name);
1770 return element.getAttribute(name);
1279 },
1771 },
1280
1772
1773 + writeAttribute: function(element, name, value) {
1774 + element = $(element);
1775 + var attributes = { }, t = Element._attributeTranslations.write;
1776 +
1777 + if (typeof name == 'object') attributes = name;
1778 + else attributes[name] = Object.isUndefined(value) ? true : value;
1779 +
1780 + for (var attr in attributes) {
1781 + name = t.names[attr] || attr;
1782 + value = attributes[attr];
1783 + if (t.values[attr]) name = t.values[attr](element, value);
1784 + if (value === false || value === null)
1785 + element.removeAttribute(name);
1786 + else if (value === true)
1787 + element.setAttribute(name, name);
1788 + else element.setAttribute(name, value);
1789 + }
1790 + return element;
1791 + },
1792 +
1281 getHeight: function(element) {
1793 getHeight: function(element) {
1282 return $(element).getDimensions().height;
1794 return $(element).getDimensions().height;
1283 },
1795 },
1284
1796
1285 getWidth: function(element) {
1797 getWidth: function(element) {
1286 return $(element).getDimensions().width;
1798 return $(element).getDimensions().width;
1287 },
1799 },
1288
1800
1289 classNames: function(element) {
1801 classNames: function(element) {
1290 return new Element.ClassNames(element);
1802 return new Element.ClassNames(element);
1291 },
1803 },
1292
1804
1293 hasClassName: function(element, className) {
1805 hasClassName: function(element, className) {
1294 if (!(element = $(element))) return;
1806 if (!(element = $(element))) return;
1295 var elementClassName = element.className;
1807 var elementClassName = element.className;
1296 - if (elementClassName.length == 0) return false;
1808 + return (elementClassName.length > 0 && (elementClassName == className ||
1297 - if (elementClassName == className ||
1809 + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1298 - elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299 - return true;
1300 - return false;
1301 },
1810 },
1302
1811
1303 addClassName: function(element, className) {
1812 addClassName: function(element, className) {
1304 if (!(element = $(element))) return;
1813 if (!(element = $(element))) return;
1305 - Element.classNames(element).add(className);
1814 + if (!element.hasClassName(className))
1815 + element.className += (element.className ? ' ' : '') + className;
1306 return element;
1816 return element;
1307 },
1817 },
1308
1818
1309 removeClassName: function(element, className) {
1819 removeClassName: function(element, className) {
1310 if (!(element = $(element))) return;
1820 if (!(element = $(element))) return;
1311 - Element.classNames(element).remove(className);
1821 + element.className = element.className.replace(
1822 + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1312 return element;
1823 return element;
1313 },
1824 },
1314
1825
1315 toggleClassName: function(element, className) {
1826 toggleClassName: function(element, className) {
1316 if (!(element = $(element))) return;
1827 if (!(element = $(element))) return;
1317 - Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1828 + return element[element.hasClassName(className) ?
1318 - return element;
1829 + 'removeClassName' : 'addClassName'](className);
1319 - },
1320 -
1321 - observe: function() {
1322 - Event.observe.apply(Event, arguments);
1323 - return $A(arguments).first();
1324 - },
1325 -
1326 - stopObserving: function() {
1327 - Event.stopObserving.apply(Event, arguments);
1328 - return $A(arguments).first();
1329 },
1830 },
1330
1831
1331 // removes whitespace-only text node children
1832 // removes whitespace-only text node children
1332 cleanWhitespace: function(element) {
1833 cleanWhitespace: function(element) {
1333 element = $(element);
1834 element = $(element);
1334 var node = element.firstChild;
1835 var node = element.firstChild;
1335 while (node) {
1836 while (node) {
1336 var nextNode = node.nextSibling;
1837 var nextNode = node.nextSibling;
1337 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1838 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1338 element.removeChild(node);
1839 element.removeChild(node);
1339 node = nextNode;
1840 node = nextNode;
1340 }
1841 }
1341 return element;
1842 return element;
1342 },
1843 },
1343
1844
1344 empty: function(element) {
1845 empty: function(element) {
1345 - return $(element).innerHTML.match(/^\s*$/);
1846 + return $(element).innerHTML.blank();
1346 },
1847 },
1347
1848
1348 descendantOf: function(element, ancestor) {
1849 descendantOf: function(element, ancestor) {
1349 element = $(element), ancestor = $(ancestor);
1850 element = $(element), ancestor = $(ancestor);
1851 + var originalAncestor = ancestor;
1852 +
1853 + if (element.compareDocumentPosition)
1854 + return (element.compareDocumentPosition(ancestor) & 8) === 8;
1855 +
1856 + if (element.sourceIndex && !Prototype.Browser.Opera) {
1857 + var e = element.sourceIndex, a = ancestor.sourceIndex,
1858 + nextAncestor = ancestor.nextSibling;
1859 + if (!nextAncestor) {
1860 + do { ancestor = ancestor.parentNode; }
1861 + while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1862 + }
1863 + if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
1864 + }
1865 +
1350 while (element = element.parentNode)
1866 while (element = element.parentNode)
1351 - if (element == ancestor) return true;
1867 + if (element == originalAncestor) return true;
1352 return false;
1868 return false;
1353 },
1869 },
1354
1870
1355 scrollTo: function(element) {
1871 scrollTo: function(element) {
1356 element = $(element);
1872 element = $(element);
1357 - var pos = Position.cumulativeOffset(element);
1873 + var pos = element.cumulativeOffset();
1358 window.scrollTo(pos[0], pos[1]);
1874 window.scrollTo(pos[0], pos[1]);
1359 return element;
1875 return element;
1360 },
1876 },
1361
1877
1362 getStyle: function(element, style) {
1878 getStyle: function(element, style) {
1363 element = $(element);
1879 element = $(element);
1364 - if (['float','cssFloat'].include(style))
1880 + style = style == 'float' ? 'cssFloat' : style.camelize();
1365 - style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366 - style = style.camelize();
1367 var value = element.style[style];
1881 var value = element.style[style];
1368 if (!value) {
1882 if (!value) {
1369 - if (document.defaultView && document.defaultView.getComputedStyle) {
1370 var css = document.defaultView.getComputedStyle(element, null);
1883 var css = document.defaultView.getComputedStyle(element, null);
1371 value = css ? css[style] : null;
1884 value = css ? css[style] : null;
1372 - } else if (element.currentStyle) {
1885 + }
1373 - value = element.currentStyle[style];
1886 + if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1374 - }
1375 - }
1376 -
1377 - if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1378 - value = element['offset'+style.capitalize()] + 'px';
1379 -
1380 - if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1381 - if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1382 - if(style == 'opacity') {
1383 - if(value) return parseFloat(value);
1384 - if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385 - if(value[1]) return parseFloat(value[1]) / 100;
1386 - return 1.0;
1387 - }
1388 return value == 'auto' ? null : value;
1887 return value == 'auto' ? null : value;
1389 },
1888 },
1390
1889
1391 - setStyle: function(element, style) {
1890 + getOpacity: function(element) {
1891 + return $(element).getStyle('opacity');
1892 + },
1893 +
1894 + setStyle: function(element, styles) {
1392 element = $(element);
1895 element = $(element);
1393 - for (var name in style) {
1896 + var elementStyle = element.style, match;
1394 - var value = style[name];
1897 + if (Object.isString(styles)) {
1395 - if(name == 'opacity') {
1898 + element.style.cssText += ';' + styles;
1396 - if (value == 1) {
1899 + return styles.include('opacity') ?
1397 - value = (/Gecko/.test(navigator.userAgent) &&
1900 + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1398 - !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1901 + }
1399 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1902 + for (var property in styles)
1400 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1903 + if (property == 'opacity') element.setOpacity(styles[property]);
1401 - } else if(value == '') {
1904 + else
1402 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1905 + elementStyle[(property == 'float' || property == 'cssFloat') ?
1403 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1906 + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
1404 - } else {
1907 + property] = styles[property];
1405 - if(value < 0.00001) value = 0;
1908 +
1406 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1909 + return element;
1407 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1910 + },
1408 - 'alpha(opacity='+value*100+')';
1911 +
1409 - }
1912 + setOpacity: function(element, value) {
1410 - } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1913 + element = $(element);
1411 - element.style[name.camelize()] = value;
1914 + element.style.opacity = (value == 1 || value === '') ? '' :
1412 - }
1915 + (value < 0.00001) ? 0 : value;
1413 return element;
1916 return element;
1414 },
1917 },
1415
1918
1416 getDimensions: function(element) {
1919 getDimensions: function(element) {
1417 element = $(element);
1920 element = $(element);
1418 var display = $(element).getStyle('display');
1921 var display = $(element).getStyle('display');
1419 if (display != 'none' && display != null) // Safari bug
1922 if (display != 'none' && display != null) // Safari bug
1420 return {width: element.offsetWidth, height: element.offsetHeight};
1923 return {width: element.offsetWidth, height: element.offsetHeight};
1421
1924
1422 // All *Width and *Height properties give 0 on elements with display none,
1925 // All *Width and *Height properties give 0 on elements with display none,
1423 // so enable the element temporarily
1926 // so enable the element temporarily
1424 var els = element.style;
1927 var els = element.style;
1425 var originalVisibility = els.visibility;
1928 var originalVisibility = els.visibility;
1426 var originalPosition = els.position;
1929 var originalPosition = els.position;
1427 var originalDisplay = els.display;
1930 var originalDisplay = els.display;
1428 els.visibility = 'hidden';
1931 els.visibility = 'hidden';
1429 els.position = 'absolute';
1932 els.position = 'absolute';
1430 els.display = 'block';
1933 els.display = 'block';
1431 var originalWidth = element.clientWidth;
1934 var originalWidth = element.clientWidth;
1432 var originalHeight = element.clientHeight;
1935 var originalHeight = element.clientHeight;
1433 els.display = originalDisplay;
1936 els.display = originalDisplay;
1434 els.position = originalPosition;
1937 els.position = originalPosition;
1435 els.visibility = originalVisibility;
1938 els.visibility = originalVisibility;
1436 return {width: originalWidth, height: originalHeight};
1939 return {width: originalWidth, height: originalHeight};
1437 },
1940 },
1438
1941
1439 makePositioned: function(element) {
1942 makePositioned: function(element) {
1440 element = $(element);
1943 element = $(element);
1441 var pos = Element.getStyle(element, 'position');
1944 var pos = Element.getStyle(element, 'position');
1442 if (pos == 'static' || !pos) {
1945 if (pos == 'static' || !pos) {
1443 element._madePositioned = true;
1946 element._madePositioned = true;
1444 element.style.position = 'relative';
1947 element.style.position = 'relative';
1445 // Opera returns the offset relative to the positioning context, when an
1948 // Opera returns the offset relative to the positioning context, when an
1446 // element is position relative but top and left have not been defined
1949 // element is position relative but top and left have not been defined
1447 if (window.opera) {
1950 if (window.opera) {
1448 element.style.top = 0;
1951 element.style.top = 0;
1449 element.style.left = 0;
1952 element.style.left = 0;
1450 }
1953 }
1451 }
1954 }
1452 return element;
1955 return element;
1453 },
1956 },
1454
1957
1455 undoPositioned: function(element) {
1958 undoPositioned: function(element) {
1456 element = $(element);
1959 element = $(element);
1457 if (element._madePositioned) {
1960 if (element._madePositioned) {
1458 element._madePositioned = undefined;
1961 element._madePositioned = undefined;
1459 element.style.position =
1962 element.style.position =
1460 element.style.top =
1963 element.style.top =
1461 element.style.left =
1964 element.style.left =
1462 element.style.bottom =
1965 element.style.bottom =
1463 element.style.right = '';
1966 element.style.right = '';
1464 }
1967 }
1465 return element;
1968 return element;
1466 },
1969 },
1467
1970
1468 makeClipping: function(element) {
1971 makeClipping: function(element) {
1469 element = $(element);
1972 element = $(element);
1470 if (element._overflow) return element;
1973 if (element._overflow) return element;
1471 - element._overflow = element.style.overflow || 'auto';
1974 + element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1472 - if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1975 + if (element._overflow !== 'hidden')
1473 element.style.overflow = 'hidden';
1976 element.style.overflow = 'hidden';
1474 return element;
1977 return element;
1475 },
1978 },
1476
1979
1477 undoClipping: function(element) {
1980 undoClipping: function(element) {
1478 element = $(element);
1981 element = $(element);
1479 if (!element._overflow) return element;
1982 if (!element._overflow) return element;
1480 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1983 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481 element._overflow = null;
1984 element._overflow = null;
1482 return element;
1985 return element;
1986 + },
1987 +
1988 + cumulativeOffset: function(element) {
1989 + var valueT = 0, valueL = 0;
1990 + do {
1991 + valueT += element.offsetTop || 0;
1992 + valueL += element.offsetLeft || 0;
1993 + element = element.offsetParent;
1994 + } while (element);
1995 + return Element._returnOffset(valueL, valueT);
1996 + },
1997 +
1998 + positionedOffset: function(element) {
1999 + var valueT = 0, valueL = 0;
2000 + do {
2001 + valueT += element.offsetTop || 0;
2002 + valueL += element.offsetLeft || 0;
2003 + element = element.offsetParent;
2004 + if (element) {
2005 + if (element.tagName == 'BODY') break;
2006 + var p = Element.getStyle(element, 'position');
2007 + if (p == 'relative' || p == 'absolute') break;
2008 + }
2009 + } while (element);
2010 + return Element._returnOffset(valueL, valueT);
2011 + },
2012 +
2013 + absolutize: function(element) {
2014 + element = $(element);
2015 + if (element.getStyle('position') == 'absolute') return;
2016 + // Position.prepare(); // To be done manually by Scripty when it needs it.
2017 +
2018 + var offsets = element.positionedOffset();
2019 + var top = offsets[1];
2020 + var left = offsets[0];
2021 + var width = element.clientWidth;
2022 + var height = element.clientHeight;
2023 +
2024 + element._originalLeft = left - parseFloat(element.style.left || 0);
2025 + element._originalTop = top - parseFloat(element.style.top || 0);
2026 + element._originalWidth = element.style.width;
2027 + element._originalHeight = element.style.height;
2028 +
2029 + element.style.position = 'absolute';
2030 + element.style.top = top + 'px';
2031 + element.style.left = left + 'px';
2032 + element.style.width = width + 'px';
2033 + element.style.height = height + 'px';
2034 + return element;
2035 + },
2036 +
2037 + relativize: function(element) {
2038 + element = $(element);
2039 + if (element.getStyle('position') == 'relative') return;
2040 + // Position.prepare(); // To be done manually by Scripty when it needs it.
2041 +
2042 + element.style.position = 'relative';
2043 + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2044 + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2045 +
2046 + element.style.top = top + 'px';
2047 + element.style.left = left + 'px';
2048 + element.style.height = element._originalHeight;
2049 + element.style.width = element._originalWidth;
2050 + return element;
2051 + },
2052 +
2053 + cumulativeScrollOffset: function(element) {
2054 + var valueT = 0, valueL = 0;
2055 + do {
2056 + valueT += element.scrollTop || 0;
2057 + valueL += element.scrollLeft || 0;
2058 + element = element.parentNode;
2059 + } while (element);
2060 + return Element._returnOffset(valueL, valueT);
2061 + },
2062 +
2063 + getOffsetParent: function(element) {
2064 + if (element.offsetParent) return $(element.offsetParent);
2065 + if (element == document.body) return $(element);
2066 +
2067 + while ((element = element.parentNode) && element != document.body)
2068 + if (Element.getStyle(element, 'position') != 'static')
2069 + return $(element);
2070 +
2071 + return $(document.body);
2072 + },
2073 +
2074 + viewportOffset: function(forElement) {
2075 + var valueT = 0, valueL = 0;
2076 +
2077 + var element = forElement;
2078 + do {
2079 + valueT += element.offsetTop || 0;
2080 + valueL += element.offsetLeft || 0;
2081 +
2082 + // Safari fix
2083 + if (element.offsetParent == document.body &&
2084 + Element.getStyle(element, 'position') == 'absolute') break;
2085 +
2086 + } while (element = element.offsetParent);
2087 +
2088 + element = forElement;
2089 + do {
2090 + if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2091 + valueT -= element.scrollTop || 0;
2092 + valueL -= element.scrollLeft || 0;
2093 + }
2094 + } while (element = element.parentNode);
2095 +
2096 + return Element._returnOffset(valueL, valueT);
2097 + },
2098 +
2099 + clonePosition: function(element, source) {
2100 + var options = Object.extend({
2101 + setLeft: true,
2102 + setTop: true,
2103 + setWidth: true,
2104 + setHeight: true,
2105 + offsetTop: 0,
2106 + offsetLeft: 0
2107 + }, arguments[2] || { });
2108 +
2109 + // find page position of source
2110 + source = $(source);
2111 + var p = source.viewportOffset();
2112 +
2113 + // find coordinate system to use
2114 + element = $(element);
2115 + var delta = [0, 0];
2116 + var parent = null;
2117 + // delta [0,0] will do fine with position: fixed elements,
2118 + // position:absolute needs offsetParent deltas
2119 + if (Element.getStyle(element, 'position') == 'absolute') {
2120 + parent = element.getOffsetParent();
2121 + delta = parent.viewportOffset();
2122 + }
2123 +
2124 + // correct by body offsets (fixes Safari)
2125 + if (parent == document.body) {
2126 + delta[0] -= document.body.offsetLeft;
2127 + delta[1] -= document.body.offsetTop;
2128 + }
2129 +
2130 + // set position
2131 + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2132 + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2133 + if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2134 + if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2135 + return element;
1483 }
2136 }
1484 };
2137 };
1485
2138
1486 - Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
2139 + Element.Methods.identify.counter = 1;
1487 -
2140 +
1488 - Element._attributeTranslations = {};
2141 + Object.extend(Element.Methods, {
1489 -
2142 + getElementsBySelector: Element.Methods.select,
1490 - Element._attributeTranslations.names = {
2143 + childElements: Element.Methods.immediateDescendants
1491 - colspan: "colSpan",
2144 + });
1492 - rowspan: "rowSpan",
2145 +
1493 - valign: "vAlign",
2146 + Element._attributeTranslations = {
1494 - datetime: "dateTime",
2147 + write: {
1495 - accesskey: "accessKey",
2148 + names: {
1496 - tabindex: "tabIndex",
2149 + className: 'class',
1497 - enctype: "encType",
2150 + htmlFor: 'for'
1498 - maxlength: "maxLength",
2151 + },
1499 - readonly: "readOnly",
2152 + values: { }
1500 - longdesc: "longDesc"
2153 + }
2154 + };
2155 +
2156 +
2157 + if (!document.createRange || Prototype.Browser.Opera) {
2158 + Element.Methods.insert = function(element, insertions) {
2159 + element = $(element);
2160 +
2161 + if (Object.isString(insertions) || Object.isNumber(insertions) ||
2162 + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2163 + insertions = { bottom: insertions };
2164 +
2165 + var t = Element._insertionTranslations, content, position, pos, tagName;
2166 +
2167 + for (position in insertions) {
2168 + content = insertions[position];
2169 + position = position.toLowerCase();
2170 + pos = t[position];
2171 +
2172 + if (content && content.toElement) content = content.toElement();
2173 + if (Object.isElement(content)) {
2174 + pos.insert(element, content);
2175 + continue;
2176 + }
2177 +
2178 + content = Object.toHTML(content);
2179 + tagName = ((position == 'before' || position == 'after')
2180 + ? element.parentNode : element).tagName.toUpperCase();
2181 +
2182 + if (t.tags[tagName]) {
2183 + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2184 + if (position == 'top' || position == 'after') fragments.reverse();
2185 + fragments.each(pos.insert.curry(element));
2186 + }
2187 + else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
2188 +
2189 + content.evalScripts.bind(content).defer();
2190 + }
2191 +
2192 + return element;
1501 };
2193 };
1502 -
2194 + }
1503 - Element._attributeTranslations.values = {
2195 +
2196 + if (Prototype.Browser.Opera) {
2197 + Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2198 + function(proceed, element, style) {
2199 + switch (style) {
2200 + case 'left': case 'top': case 'right': case 'bottom':
2201 + if (proceed(element, 'position') === 'static') return null;
2202 + case 'height': case 'width':
2203 + // returns '0px' for hidden elements; we want it to return null
2204 + if (!Element.visible(element)) return null;
2205 +
2206 + // returns the border-box dimensions rather than the content-box
2207 + // dimensions, so we subtract padding and borders from the value
2208 + var dim = parseInt(proceed(element, style), 10);
2209 +
2210 + if (dim !== element['offset' + style.capitalize()])
2211 + return dim + 'px';
2212 +
2213 + var properties;
2214 + if (style === 'height') {
2215 + properties = ['border-top-width', 'padding-top',
2216 + 'padding-bottom', 'border-bottom-width'];
2217 + }
2218 + else {
2219 + properties = ['border-left-width', 'padding-left',
2220 + 'padding-right', 'border-right-width'];
2221 + }
2222 + return properties.inject(dim, function(memo, property) {
2223 + var val = proceed(element, property);
2224 + return val === null ? memo : memo - parseInt(val, 10);
2225 + }) + 'px';
2226 + default: return proceed(element, style);
2227 + }
2228 + }
2229 + );
2230 +
2231 + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2232 + function(proceed, element, attribute) {
2233 + if (attribute === 'title') return element.title;
2234 + return proceed(element, attribute);
2235 + }
2236 + );
2237 + }
2238 +
2239 + else if (Prototype.Browser.IE) {
2240 + $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
2241 + Element.Methods[method] = Element.Methods[method].wrap(
2242 + function(proceed, element) {
2243 + element = $(element);
2244 + var position = element.getStyle('position');
2245 + if (position != 'static') return proceed(element);
2246 + element.setStyle({ position: 'relative' });
2247 + var value = proceed(element);
2248 + element.setStyle({ position: position });
2249 + return value;
2250 + }
2251 + );
2252 + });
2253 +
2254 + Element.Methods.getStyle = function(element, style) {
2255 + element = $(element);
2256 + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2257 + var value = element.style[style];
2258 + if (!value && element.currentStyle) value = element.currentStyle[style];
2259 +
2260 + if (style == 'opacity') {
2261 + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2262 + if (value[1]) return parseFloat(value[1]) / 100;
2263 + return 1.0;
2264 + }
2265 +
2266 + if (value == 'auto') {
2267 + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2268 + return element['offset' + style.capitalize()] + 'px';
2269 + return null;
2270 + }
2271 + return value;
2272 + };
2273 +
2274 + Element.Methods.setOpacity = function(element, value) {
2275 + function stripAlpha(filter){
2276 + return filter.replace(/alpha\([^\)]*\)/gi,'');
2277 + }
2278 + element = $(element);
2279 + var currentStyle = element.currentStyle;
2280 + if ((currentStyle && !currentStyle.hasLayout) ||
2281 + (!currentStyle && element.style.zoom == 'normal'))
2282 + element.style.zoom = 1;
2283 +
2284 + var filter = element.getStyle('filter'), style = element.style;
2285 + if (value == 1 || value === '') {
2286 + (filter = stripAlpha(filter)) ?
2287 + style.filter = filter : style.removeAttribute('filter');
2288 + return element;
2289 + } else if (value < 0.00001) value = 0;
2290 + style.filter = stripAlpha(filter) +
2291 + 'alpha(opacity=' + (value * 100) + ')';
2292 + return element;
2293 + };
2294 +
2295 + Element._attributeTranslations = {
2296 + read: {
2297 + names: {
2298 + 'class': 'className',
2299 + 'for': 'htmlFor'
2300 + },
2301 + values: {
1504 _getAttr: function(element, attribute) {
2302 _getAttr: function(element, attribute) {
1505 return element.getAttribute(attribute, 2);
2303 return element.getAttribute(attribute, 2);
1506 },
2304 },
1507 -
2305 + _getAttrNode: function(element, attribute) {
2306 + var node = element.getAttributeNode(attribute);
2307 + return node ? node.value : "";
2308 + },
2309 + _getEv: function(element, attribute) {
2310 + attribute = element.getAttribute(attribute);
2311 + return attribute ? attribute.toString().slice(23, -2) : null;
2312 + },
1508 _flag: function(element, attribute) {
2313 _flag: function(element, attribute) {
1509 return $(element).hasAttribute(attribute) ? attribute : null;
2314 return $(element).hasAttribute(attribute) ? attribute : null;
1510 },
2315 },
1511 -
1512 style: function(element) {
2316 style: function(element) {
1513 return element.style.cssText.toLowerCase();
2317 return element.style.cssText.toLowerCase();
1514 },
2318 },
1515 -
1516 title: function(element) {
2319 title: function(element) {
1517 - var node = element.getAttributeNode('title');
2320 + return element.title;
1518 - return node.specified ? node.nodeValue : null;
2321 + }
2322 + }
2323 + }
2324 + };
2325 +
2326 + Element._attributeTranslations.write = {
2327 + names: Object.clone(Element._attributeTranslations.read.names),
2328 + values: {
2329 + checked: function(element, value) {
2330 + element.checked = !!value;
2331 + },
2332 +
2333 + style: function(element, value) {
2334 + element.style.cssText = value ? value : '';
2335 + }
1519 }
2336 }
1520 };
2337 };
1521
2338
1522 - Object.extend(Element._attributeTranslations.values, {
2339 + Element._attributeTranslations.has = {};
1523 - href: Element._attributeTranslations.values._getAttr,
2340 +
1524 - src: Element._attributeTranslations.values._getAttr,
2341 + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
1525 - disabled: Element._attributeTranslations.values._flag,
2342 + 'encType maxLength readOnly longDesc').each(function(attr) {
1526 - checked: Element._attributeTranslations.values._flag,
2343 + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
1527 - readonly: Element._attributeTranslations.values._flag,
2344 + Element._attributeTranslations.has[attr.toLowerCase()] = attr;
1528 - multiple: Element._attributeTranslations.values._flag
2345 + });
2346 +
2347 + (function(v) {
2348 + Object.extend(v, {
2349 + href: v._getAttr,
2350 + src: v._getAttr,
2351 + type: v._getAttr,
2352 + action: v._getAttrNode,
2353 + disabled: v._flag,
2354 + checked: v._flag,
2355 + readonly: v._flag,
2356 + multiple: v._flag,
2357 + onload: v._getEv,
2358 + onunload: v._getEv,
2359 + onclick: v._getEv,
2360 + ondblclick: v._getEv,
2361 + onmousedown: v._getEv,
2362 + onmouseup: v._getEv,
2363 + onmouseover: v._getEv,
2364 + onmousemove: v._getEv,
2365 + onmouseout: v._getEv,
2366 + onfocus: v._getEv,
2367 + onblur: v._getEv,
2368 + onkeypress: v._getEv,
2369 + onkeydown: v._getEv,
2370 + onkeyup: v._getEv,
2371 + onsubmit: v._getEv,
2372 + onreset: v._getEv,
2373 + onselect: v._getEv,
2374 + onchange: v._getEv
1529 });
2375 });
2376 + })(Element._attributeTranslations.read.values);
2377 + }
2378 +
2379 + else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2380 + Element.Methods.setOpacity = function(element, value) {
2381 + element = $(element);
2382 + element.style.opacity = (value == 1) ? 0.999999 :
2383 + (value === '') ? '' : (value < 0.00001) ? 0 : value;
2384 + return element;
2385 + };
2386 + }
2387 +
2388 + else if (Prototype.Browser.WebKit) {
2389 + Element.Methods.setOpacity = function(element, value) {
2390 + element = $(element);
2391 + element.style.opacity = (value == 1 || value === '') ? '' :
2392 + (value < 0.00001) ? 0 : value;
2393 +
2394 + if (value == 1)
2395 + if(element.tagName == 'IMG' && element.width) {
2396 + element.width++; element.width--;
2397 + } else try {
2398 + var n = document.createTextNode(' ');
2399 + element.appendChild(n);
2400 + element.removeChild(n);
2401 + } catch (e) { }
2402 +
2403 + return element;
2404 + };
2405 +
2406 + // Safari returns margins on body which is incorrect if the child is absolutely
2407 + // positioned. For performance reasons, redefine Element#cumulativeOffset for
2408 + // KHTML/WebKit only.
2409 + Element.Methods.cumulativeOffset = function(element) {
2410 + var valueT = 0, valueL = 0;
2411 + do {
2412 + valueT += element.offsetTop || 0;
2413 + valueL += element.offsetLeft || 0;
2414 + if (element.offsetParent == document.body)
2415 + if (Element.getStyle(element, 'position') == 'absolute') break;
2416 +
2417 + element = element.offsetParent;
2418 + } while (element);
2419 +
2420 + return Element._returnOffset(valueL, valueT);
2421 + };
2422 + }
2423 +
2424 + if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2425 + // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2426 + Element.Methods.update = function(element, content) {
2427 + element = $(element);
2428 +
2429 + if (content && content.toElement) content = content.toElement();
2430 + if (Object.isElement(content)) return element.update().insert(content);
2431 +
2432 + content = Object.toHTML(content);
2433 + var tagName = element.tagName.toUpperCase();
2434 +
2435 + if (tagName in Element._insertionTranslations.tags) {
2436 + $A(element.childNodes).each(function(node) { element.removeChild(node) });
2437 + Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2438 + .each(function(node) { element.appendChild(node) });
2439 + }
2440 + else element.innerHTML = content.stripScripts();
2441 +
2442 + content.evalScripts.bind(content).defer();
2443 + return element;
2444 + };
2445 + }
2446 +
2447 + if (document.createElement('div').outerHTML) {
2448 + Element.Methods.replace = function(element, content) {
2449 + element = $(element);
2450 +
2451 + if (content && content.toElement) content = content.toElement();
2452 + if (Object.isElement(content)) {
2453 + element.parentNode.replaceChild(content, element);
2454 + return element;
2455 + }
2456 +
2457 + content = Object.toHTML(content);
2458 + var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2459 +
2460 + if (Element._insertionTranslations.tags[tagName]) {
2461 + var nextSibling = element.next();
2462 + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2463 + parent.removeChild(element);
2464 + if (nextSibling)
2465 + fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2466 + else
2467 + fragments.each(function(node) { parent.appendChild(node) });
2468 + }
2469 + else element.outerHTML = content.stripScripts();
2470 +
2471 + content.evalScripts.bind(content).defer();
2472 + return element;
2473 + };
2474 + }
2475 +
2476 + Element._returnOffset = function(l, t) {
2477 + var result = [l, t];
2478 + result.left = l;
2479 + result.top = t;
2480 + return result;
2481 + };
2482 +
2483 + Element._getContentFromAnonymousElement = function(tagName, html) {
2484 + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2485 + div.innerHTML = t[0] + html + t[1];
2486 + t[2].times(function() { div = div.firstChild });
2487 + return $A(div.childNodes);
2488 + };
2489 +
2490 + Element._insertionTranslations = {
2491 + before: {
2492 + adjacency: 'beforeBegin',
2493 + insert: function(element, node) {
2494 + element.parentNode.insertBefore(node, element);
2495 + },
2496 + initializeRange: function(element, range) {
2497 + range.setStartBefore(element);
2498 + }
2499 + },
2500 + top: {
2501 + adjacency: 'afterBegin',
2502 + insert: function(element, node) {
2503 + element.insertBefore(node, element.firstChild);
2504 + },
2505 + initializeRange: function(element, range) {
2506 + range.selectNodeContents(element);
2507 + range.collapse(true);
2508 + }
2509 + },
2510 + bottom: {
2511 + adjacency: 'beforeEnd',
2512 + insert: function(element, node) {
2513 + element.appendChild(node);
2514 + }
2515 + },
2516 + after: {
2517 + adjacency: 'afterEnd',
2518 + insert: function(element, node) {
2519 + element.parentNode.insertBefore(node, element.nextSibling);
2520 + },
2521 + initializeRange: function(element, range) {
2522 + range.setStartAfter(element);
2523 + }
2524 + },
2525 + tags: {
2526 + TABLE: ['<table>', '</table>', 1],
2527 + TBODY: ['<table><tbody>', '</tbody></table>', 2],
2528 + TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2529 + TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2530 + SELECT: ['<select>', '</select>', 1]
2531 + }
2532 + };
2533 +
2534 + (function() {
2535 + this.bottom.initializeRange = this.top.initializeRange;
2536 + Object.extend(this.tags, {
2537 + THEAD: this.tags.TBODY,
2538 + TFOOT: this.tags.TBODY,
2539 + TH: this.tags.TD
2540 + });
2541 + }).call(Element._insertionTranslations);
1530
2542
1531 Element.Methods.Simulated = {
2543 Element.Methods.Simulated = {
1532 hasAttribute: function(element, attribute) {
2544 hasAttribute: function(element, attribute) {
1533 - var t = Element._attributeTranslations;
2545 + attribute = Element._attributeTranslations.has[attribute] || attribute;
1534 - attribute = t.names[attribute] || attribute;
2546 + var node = $(element).getAttributeNode(attribute);
1535 - return $(element).getAttributeNode(attribute).specified;
2547 + return node && node.specified;
1536 }
2548 }
1537 };
2549 };
1538
2550
1539 - // IE is missing .innerHTML support for TABLE-related elements
2551 + Element.Methods.ByTag = { };
1540 - if (document.all && !window.opera){
1541 - Element.Methods.update = function(element, html) {
1542 - element = $(element);
1543 - html = typeof html == 'undefined' ? '' : html.toString();
1544 - var tagName = element.tagName.toUpperCase();
1545 - if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1546 - var div = document.createElement('div');
1547 - switch (tagName) {
1548 - case 'THEAD':
1549 - case 'TBODY':
1550 - div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1551 - depth = 2;
1552 - break;
1553 - case 'TR':
1554 - div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1555 - depth = 3;
1556 - break;
1557 - case 'TD':
1558 - div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1559 - depth = 4;
1560 - }
1561 - $A(element.childNodes).each(function(node){
1562 - element.removeChild(node)
1563 - });
1564 - depth.times(function(){ div = div.firstChild });
1565 -
1566 - $A(div.childNodes).each(
1567 - function(node){ element.appendChild(node) });
1568 - } else {
1569 - element.innerHTML = html.stripScripts();
1570 - }
1571 - setTimeout(function() {html.evalScripts()}, 10);
1572 - return element;
1573 - }
1574 - };
1575
2552
1576 Object.extend(Element, Element.Methods);
2553 Object.extend(Element, Element.Methods);
1577
2554
1578 - var _nativeExtensions = false;
2555 + if (!Prototype.BrowserFeatures.ElementExtensions &&
1579 -
2556 + document.createElement('div').__proto__) {
1580 - if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
2557 + window.HTMLElement = { };
1581 - ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
2558 + window.HTMLElement.prototype = document.createElement('div').__proto__;
1582 - var className = 'HTML' + tag + 'Element';
2559 + Prototype.BrowserFeatures.ElementExtensions = true;
1583 - if(window[className]) return;
2560 + }
1584 - var klass = window[className] = {};
2561 +
1585 - klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
2562 + Element.extend = (function() {
2563 + if (Prototype.BrowserFeatures.SpecificElementExtensions)
2564 + return Prototype.K;
2565 +
2566 + var Methods = { }, ByTag = Element.Methods.ByTag;
2567 +
2568 + var extend = Object.extend(function(element) {
2569 + if (!element || element._extendedByPrototype ||
2570 + element.nodeType != 1 || element == window) return element;
2571 +
2572 + var methods = Object.clone(Methods),
2573 + tagName = element.tagName, property, value;
2574 +
2575 + // extend methods for specific tags
2576 + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2577 +
2578 + for (property in methods) {
2579 + value = methods[property];
2580 + if (Object.isFunction(value) && !(property in element))
2581 + element[property] = value.methodize();
2582 + }
2583 +
2584 + element._extendedByPrototype = Prototype.emptyFunction;
2585 + return element;
2586 +
2587 + }, {
2588 + refresh: function() {
2589 + // extend methods for all tags (Safari doesn't need this)
2590 + if (!Prototype.BrowserFeatures.ElementExtensions) {
2591 + Object.extend(Methods, Element.Methods);
2592 + Object.extend(Methods, Element.Methods.Simulated);
2593 + }
2594 + }
1586 });
2595 });
1587
2596
2597 + extend.refresh();
2598 + return extend;
2599 + })();
2600 +
2601 + Element.hasAttribute = function(element, attribute) {
2602 + if (element.hasAttribute) return element.hasAttribute(attribute);
2603 + return Element.Methods.Simulated.hasAttribute(element, attribute);
2604 + };
2605 +
1588 Element.addMethods = function(methods) {
2606 Element.addMethods = function(methods) {
1589 - Object.extend(Element.Methods, methods || {});
2607 + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2608 +
2609 + if (!methods) {
2610 + Object.extend(Form, Form.Methods);
2611 + Object.extend(Form.Element, Form.Element.Methods);
2612 + Object.extend(Element.Methods.ByTag, {
2613 + "FORM": Object.clone(Form.Methods),
2614 + "INPUT": Object.clone(Form.Element.Methods),
2615 + "SELECT": Object.clone(Form.Element.Methods),
2616 + "TEXTAREA": Object.clone(Form.Element.Methods)
2617 + });
2618 + }
2619 +
2620 + if (arguments.length == 2) {
2621 + var tagName = methods;
2622 + methods = arguments[1];
2623 + }
2624 +
2625 + if (!tagName) Object.extend(Element.Methods, methods || { });
2626 + else {
2627 + if (Object.isArray(tagName)) tagName.each(extend);
2628 + else extend(tagName);
2629 + }
2630 +
2631 + function extend(tagName) {
2632 + tagName = tagName.toUpperCase();
2633 + if (!Element.Methods.ByTag[tagName])
2634 + Element.Methods.ByTag[tagName] = { };
2635 + Object.extend(Element.Methods.ByTag[tagName], methods);
2636 + }
1590
2637
1591 function copy(methods, destination, onlyIfAbsent) {
2638 function copy(methods, destination, onlyIfAbsent) {
1592 onlyIfAbsent = onlyIfAbsent || false;
2639 onlyIfAbsent = onlyIfAbsent || false;
1593 - var cache = Element.extend.cache;
1594 for (var property in methods) {
2640 for (var property in methods) {
1595 var value = methods[property];
2641 var value = methods[property];
2642 + if (!Object.isFunction(value)) continue;
1596 if (!onlyIfAbsent || !(property in destination))
2643 if (!onlyIfAbsent || !(property in destination))
1597 - destination[property] = cache.findOrStore(value);
2644 + destination[property] = value.methodize();
1598 - }
2645 + }
1599 - }
2646 + }
1600 -
2647 +
1601 - if (typeof HTMLElement != 'undefined') {
2648 + function findDOMClass(tagName) {
2649 + var klass;
2650 + var trans = {
2651 + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2652 + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2653 + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2654 + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2655 + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2656 + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2657 + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2658 + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2659 + "FrameSet", "IFRAME": "IFrame"
2660 + };
2661 + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2662 + if (window[klass]) return window[klass];
2663 + klass = 'HTML' + tagName + 'Element';
2664 + if (window[klass]) return window[klass];
2665 + klass = 'HTML' + tagName.capitalize() + 'Element';
2666 + if (window[klass]) return window[klass];
2667 +
2668 + window[klass] = { };
2669 + window[klass].prototype = document.createElement(tagName).__proto__;
2670 + return window[klass];
2671 + }
2672 +
2673 + if (F.ElementExtensions) {
1602 copy(Element.Methods, HTMLElement.prototype);
2674 copy(Element.Methods, HTMLElement.prototype);
1603 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2675 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1604 - copy(Form.Methods, HTMLFormElement.prototype);
2676 + }
1605 - [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
2677 +
1606 - copy(Form.Element.Methods, klass.prototype);
2678 + if (F.SpecificElementExtensions) {
2679 + for (var tag in Element.Methods.ByTag) {
2680 + var klass = findDOMClass(tag);
2681 + if (Object.isUndefined(klass)) continue;
2682 + copy(T[tag], klass.prototype);
2683 + }
2684 + }
2685 +
2686 + Object.extend(Element, Element.Methods);
2687 + delete Element.ByTag;
2688 +
2689 + if (Element.extend.refresh) Element.extend.refresh();
2690 + Element.cache = { };
2691 + };
2692 +
2693 + document.viewport = {
2694 + getDimensions: function() {
2695 + var dimensions = { };
2696 + var B = Prototype.Browser;
2697 + $w('width height').each(function(d) {
2698 + var D = d.capitalize();
2699 + dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
2700 + (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
2701 + });
2702 + return dimensions;
2703 + },
2704 +
2705 + getWidth: function() {
2706 + return this.getDimensions().width;
2707 + },
2708 +
2709 + getHeight: function() {
2710 + return this.getDimensions().height;
2711 + },
2712 +
2713 + getScrollOffsets: function() {
2714 + return Element._returnOffset(
2715 + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
2716 + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
2717 + }
2718 + };
2719 + /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2720 + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2721 + * license. Please see http://www.yui-ext.com/ for more information. */
2722 +
2723 + var Selector = Class.create({
2724 + initialize: function(expression) {
2725 + this.expression = expression.strip();
2726 + this.compileMatcher();
2727 + },
2728 +
2729 + shouldUseXPath: function() {
2730 + if (!Prototype.BrowserFeatures.XPath) return false;
2731 +
2732 + var e = this.expression;
2733 +
2734 + // Safari 3 chokes on :*-of-type and :empty
2735 + if (Prototype.Browser.WebKit &&
2736 + (e.include("-of-type") || e.include(":empty")))
2737 + return false;
2738 +
2739 + // XPath can't do namespaced attributes, nor can it read
2740 + // the "checked" property from DOM nodes
2741 + if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
2742 + return false;
2743 +
2744 + return true;
2745 + },
2746 +
2747 + compileMatcher: function() {
2748 + if (this.shouldUseXPath())
2749 + return this.compileXPathMatcher();
2750 +
2751 + var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2752 + c = Selector.criteria, le, p, m;
2753 +
2754 + if (Selector._cache[e]) {
2755 + this.matcher = Selector._cache[e];
2756 + return;
2757 + }
2758 +
2759 + this.matcher = ["this.matcher = function(root) {",
2760 + "var r = root, h = Selector.handlers, c = false, n;"];
2761 +
2762 + while (e && le != e && (/\S/).test(e)) {
2763 + le = e;
2764 + for (var i in ps) {
2765 + p = ps[i];
2766 + if (m = e.match(p)) {
2767 + this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2768 + new Template(c[i]).evaluate(m));
2769 + e = e.replace(m[0], '');
2770 + break;
2771 + }
2772 + }
2773 + }
2774 +
2775 + this.matcher.push("return h.unique(n);\n}");
2776 + eval(this.matcher.join('\n'));
2777 + Selector._cache[this.expression] = this.matcher;
2778 + },
2779 +
2780 + compileXPathMatcher: function() {
2781 + var e = this.expression, ps = Selector.patterns,
2782 + x = Selector.xpath, le, m;
2783 +
2784 + if (Selector._cache[e]) {
2785 + this.xpath = Selector._cache[e]; return;
2786 + }
2787 +
2788 + this.matcher = ['.//*'];
2789 + while (e && le != e && (/\S/).test(e)) {
2790 + le = e;
2791 + for (var i in ps) {
2792 + if (m = e.match(ps[i])) {
2793 + this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2794 + new Template(x[i]).evaluate(m));
2795 + e = e.replace(m[0], '');
2796 + break;
2797 + }
2798 + }
2799 + }
2800 +
2801 + this.xpath = this.matcher.join('');
2802 + Selector._cache[this.expression] = this.xpath;
2803 + },
2804 +
2805 + findElements: function(root) {
2806 + root = root || document;
2807 + if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2808 + return this.matcher(root);
2809 + },
2810 +
2811 + match: function(element) {
2812 + this.tokens = [];
2813 +
2814 + var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2815 + var le, p, m;
2816 +
2817 + while (e && le !== e && (/\S/).test(e)) {
2818 + le = e;
2819 + for (var i in ps) {
2820 + p = ps[i];
2821 + if (m = e.match(p)) {
2822 + // use the Selector.assertions methods unless the selector
2823 + // is too complex.
2824 + if (as[i]) {
2825 + this.tokens.push([i, Object.clone(m)]);
2826 + e = e.replace(m[0], '');
2827 + } else {
2828 + // reluctantly do a document-wide search
2829 + // and look for a match in the array
2830 + return this.findElements(document).include(element);
2831 + }
2832 + }
2833 + }
2834 + }
2835 +
2836 + var match = true, name, matches;
2837 + for (var i = 0, token; token = this.tokens[i]; i++) {
2838 + name = token[0], matches = token[1];
2839 + if (!Selector.assertions[name](element, matches)) {
2840 + match = false; break;
2841 + }
2842 + }
2843 +
2844 + return match;
2845 + },
2846 +
2847 + toString: function() {
2848 + return this.expression;
2849 + },
2850 +
2851 + inspect: function() {
2852 + return "#<Selector:" + this.expression.inspect() + ">";
2853 + }
1607 });
2854 });
1608 - _nativeExtensions = true;
2855 +
1609 - }
2856 + Object.extend(Selector, {
1610 - }
2857 + _cache: { },
1611 -
2858 +
1612 - var Toggle = new Object();
2859 + xpath: {
1613 - Toggle.display = Element.toggle;
2860 + descendant: "//*",
1614 -
2861 + child: "/*",
1615 - /*--------------------------------------------------------------------------*/
2862 + adjacent: "/following-sibling::*[1]",
1616 -
2863 + laterSibling: '/following-sibling::*',
1617 - Abstract.Insertion = function(adjacency) {
2864 + tagName: function(m) {
1618 - this.adjacency = adjacency;
2865 + if (m[1] == '*') return '';
1619 - }
2866 + return "[local-name()='" + m[1].toLowerCase() +
1620 -
2867 + "' or local-name()='" + m[1].toUpperCase() + "']";
1621 - Abstract.Insertion.prototype = {
2868 + },
1622 - initialize: function(element, content) {
2869 + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
1623 - this.element = $(element);
2870 + id: "[@id='#{1}']",
1624 - this.content = content.stripScripts();
2871 + attrPresence: function(m) {
1625 -
2872 + m[1] = m[1].toLowerCase();
1626 - if (this.adjacency && this.element.insertAdjacentHTML) {
2873 + return new Template("[@#{1}]").evaluate(m);
1627 - try {
2874 + },
1628 - this.element.insertAdjacentHTML(this.adjacency, this.content);
2875 + attr: function(m) {
1629 - } catch (e) {
2876 + m[1] = m[1].toLowerCase();
1630 - var tagName = this.element.tagName.toUpperCase();
2877 + m[3] = m[5] || m[6];
1631 - if (['TBODY', 'TR'].include(tagName)) {
2878 + return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
1632 - this.insertContent(this.contentFromAnonymousTable());
2879 + },
1633 - } else {
2880 + pseudo: function(m) {
1634 - throw e;
2881 + var h = Selector.xpath.pseudos[m[1]];
1635 - }
2882 + if (!h) return '';
2883 + if (Object.isFunction(h)) return h(m);
2884 + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2885 + },
2886 + operators: {
2887 + '=': "[@#{1}='#{3}']",
2888 + '!=': "[@#{1}!='#{3}']",
2889 + '^=': "[starts-with(@#{1}, '#{3}')]",
2890 + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2891 + '*=': "[contains(@#{1}, '#{3}')]",
2892 + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2893 + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2894 + },
2895 + pseudos: {
2896 + 'first-child': '[not(preceding-sibling::*)]',
2897 + 'last-child': '[not(following-sibling::*)]',
2898 + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2899 + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2900 + 'checked': "[@checked]",
2901 + 'disabled': "[@disabled]",
2902 + 'enabled': "[not(@disabled)]",
2903 + 'not': function(m) {
2904 + var e = m[6], p = Selector.patterns,
2905 + x = Selector.xpath, le, v;
2906 +
2907 + var exclusion = [];
2908 + while (e && le != e && (/\S/).test(e)) {
2909 + le = e;
2910 + for (var i in p) {
2911 + if (m = e.match(p[i])) {
2912 + v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2913 + exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2914 + e = e.replace(m[0], '');
2915 + break;
2916 + }
2917 + }
2918 + }
2919 + return "[not(" + exclusion.join(" and ") + ")]";
2920 + },
2921 + 'nth-child': function(m) {
2922 + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2923 + },
2924 + 'nth-last-child': function(m) {
2925 + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2926 + },
2927 + 'nth-of-type': function(m) {
2928 + return Selector.xpath.pseudos.nth("position() ", m);
2929 + },
2930 + 'nth-last-of-type': function(m) {
2931 + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2932 + },
2933 + 'first-of-type': function(m) {
2934 + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2935 + },
2936 + 'last-of-type': function(m) {
2937 + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2938 + },
2939 + 'only-of-type': function(m) {
2940 + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2941 + },
2942 + nth: function(fragment, m) {
2943 + var mm, formula = m[6], predicate;
2944 + if (formula == 'even') formula = '2n+0';
2945 + if (formula == 'odd') formula = '2n+1';
2946 + if (mm = formula.match(/^(\d+)$/)) // digit only
2947 + return '[' + fragment + "= " + mm[1] + ']';
2948 + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2949 + if (mm[1] == "-") mm[1] = -1;
2950 + var a = mm[1] ? Number(mm[1]) : 1;
2951 + var b = mm[2] ? Number(mm[2]) : 0;
2952 + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2953 + "((#{fragment} - #{b}) div #{a} >= 0)]";
2954 + return new Template(predicate).evaluate({
2955 + fragment: fragment, a: a, b: b });
2956 + }
2957 + }
2958 + }
2959 + },
2960 +
2961 + criteria: {
2962 + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2963 + className: 'n = h.className(n, r, "#{1}", c); c = false;',
2964 + id: 'n = h.id(n, r, "#{1}", c); c = false;',
2965 + attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2966 + attr: function(m) {
2967 + m[3] = (m[5] || m[6]);
2968 + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2969 + },
2970 + pseudo: function(m) {
2971 + if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2972 + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2973 + },
2974 + descendant: 'c = "descendant";',
2975 + child: 'c = "child";',
2976 + adjacent: 'c = "adjacent";',
2977 + laterSibling: 'c = "laterSibling";'
2978 + },
2979 +
2980 + patterns: {
2981 + // combinators must be listed first
2982 + // (and descendant needs to be last combinator)
2983 + laterSibling: /^\s*~\s*/,
2984 + child: /^\s*>\s*/,
2985 + adjacent: /^\s*\+\s*/,
2986 + descendant: /^\s/,
2987 +
2988 + // selectors follow
2989 + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2990 + id: /^#([\w\-\*]+)(\b|$)/,
2991 + className: /^\.([\w\-\*]+)(\b|$)/,
2992 + pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
2993 + attrPresence: /^\[([\w]+)\]/,
2994 + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2995 + },
2996 +
2997 + // for Selector.match and Element#match
2998 + assertions: {
2999 + tagName: function(element, matches) {
3000 + return matches[1].toUpperCase() == element.tagName.toUpperCase();
3001 + },
3002 +
3003 + className: function(element, matches) {
3004 + return Element.hasClassName(element, matches[1]);
3005 + },
3006 +
3007 + id: function(element, matches) {
3008 + return element.id === matches[1];
3009 + },
3010 +
3011 + attrPresence: function(element, matches) {
3012 + return Element.hasAttribute(element, matches[1]);
3013 + },
3014 +
3015 + attr: function(element, matches) {
3016 + var nodeValue = Element.readAttribute(element, matches[1]);
3017 + return Selector.operators[matches[2]](nodeValue, matches[3]);
3018 + }
3019 + },
3020 +
3021 + handlers: {
3022 + // UTILITY FUNCTIONS
3023 + // joins two collections
3024 + concat: function(a, b) {
3025 + for (var i = 0, node; node = b[i]; i++)
3026 + a.push(node);
3027 + return a;
3028 + },
3029 +
3030 + // marks an array of nodes for counting
3031 + mark: function(nodes) {
3032 + for (var i = 0, node; node = nodes[i]; i++)
3033 + node._counted = true;
3034 + return nodes;
3035 + },
3036 +
3037 + unmark: function(nodes) {
3038 + for (var i = 0, node; node = nodes[i]; i++)
3039 + node._counted = undefined;
3040 + return nodes;
3041 + },
3042 +
3043 + // mark each child node with its position (for nth calls)
3044 + // "ofType" flag indicates whether we're indexing for nth-of-type
3045 + // rather than nth-child
3046 + index: function(parentNode, reverse, ofType) {
3047 + parentNode._counted = true;
3048 + if (reverse) {
3049 + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3050 + var node = nodes[i];
3051 + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
1636 }
3052 }
1637 } else {
3053 } else {
1638 - this.range = this.element.ownerDocument.createRange();
3054 + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
1639 - if (this.initializeRange) this.initializeRange();
3055 + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
1640 - this.insertContent([this.range.createContextualFragment(this.content)]);
3056 + }
1641 - }
3057 + },
1642 -
3058 +
1643 - setTimeout(function() {content.evalScripts()}, 10);
3059 + // filters out duplicates and extends all nodes
1644 - },
3060 + unique: function(nodes) {
1645 -
3061 + if (nodes.length == 0) return nodes;
1646 - contentFromAnonymousTable: function() {
3062 + var results = [], n;
1647 - var div = document.createElement('div');
3063 + for (var i = 0, l = nodes.length; i < l; i++)
1648 - div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
3064 + if (!(n = nodes[i])._counted) {
1649 - return $A(div.childNodes[0].childNodes[0].childNodes);
3065 + n._counted = true;
1650 - }
3066 + results.push(Element.extend(n));
1651 - }
3067 + }
1652 -
3068 + return Selector.handlers.unmark(results);
1653 - var Insertion = new Object();
3069 + },
1654 -
3070 +
1655 - Insertion.Before = Class.create();
3071 + // COMBINATOR FUNCTIONS
1656 - Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
3072 + descendant: function(nodes) {
1657 - initializeRange: function() {
3073 + var h = Selector.handlers;
1658 - this.range.setStartBefore(this.element);
3074 + for (var i = 0, results = [], node; node = nodes[i]; i++)
1659 - },
3075 + h.concat(results, node.getElementsByTagName('*'));
1660 -
3076 + return results;
1661 - insertContent: function(fragments) {
3077 + },
1662 - fragments.each((function(fragment) {
3078 +
1663 - this.element.parentNode.insertBefore(fragment, this.element);
3079 + child: function(nodes) {
1664 - }).bind(this));
3080 + var h = Selector.handlers;
3081 + for (var i = 0, results = [], node; node = nodes[i]; i++) {
3082 + for (var j = 0, child; child = node.childNodes[j]; j++)
3083 + if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3084 + }
3085 + return results;
3086 + },
3087 +
3088 + adjacent: function(nodes) {
3089 + for (var i = 0, results = [], node; node = nodes[i]; i++) {
3090 + var next = this.nextElementSibling(node);
3091 + if (next) results.push(next);
3092 + }
3093 + return results;
3094 + },
3095 +
3096 + laterSibling: function(nodes) {
3097 + var h = Selector.handlers;
3098 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3099 + h.concat(results, Element.nextSiblings(node));
3100 + return results;
3101 + },
3102 +
3103 + nextElementSibling: function(node) {
3104 + while (node = node.nextSibling)
3105 + if (node.nodeType == 1) return node;
3106 + return null;
3107 + },
3108 +
3109 + previousElementSibling: function(node) {
3110 + while (node = node.previousSibling)
3111 + if (node.nodeType == 1) return node;
3112 + return null;
3113 + },
3114 +
3115 + // TOKEN FUNCTIONS
3116 + tagName: function(nodes, root, tagName, combinator) {
3117 + tagName = tagName.toUpperCase();
3118 + var results = [], h = Selector.handlers;
3119 + if (nodes) {
3120 + if (combinator) {
3121 + // fastlane for ordinary descendant combinators
3122 + if (combinator == "descendant") {
3123 + for (var i = 0, node; node = nodes[i]; i++)
3124 + h.concat(results, node.getElementsByTagName(tagName));
3125 + return results;
3126 + } else nodes = this[combinator](nodes);
3127 + if (tagName == "*") return nodes;
3128 + }
3129 + for (var i = 0, node; node = nodes[i]; i++)
3130 + if (node.tagName.toUpperCase() == tagName) results.push(node);
3131 + return results;
3132 + } else return root.getElementsByTagName(tagName);
3133 + },
3134 +
3135 + id: function(nodes, root, id, combinator) {
3136 + var targetNode = $(id), h = Selector.handlers;
3137 + if (!targetNode) return [];
3138 + if (!nodes && root == document) return [targetNode];
3139 + if (nodes) {
3140 + if (combinator) {
3141 + if (combinator == 'child') {
3142 + for (var i = 0, node; node = nodes[i]; i++)
3143 + if (targetNode.parentNode == node) return [targetNode];
3144 + } else if (combinator == 'descendant') {
3145 + for (var i = 0, node; node = nodes[i]; i++)
3146 + if (Element.descendantOf(targetNode, node)) return [targetNode];
3147 + } else if (combinator == 'adjacent') {
3148 + for (var i = 0, node; node = nodes[i]; i++)
3149 + if (Selector.handlers.previousElementSibling(targetNode) == node)
3150 + return [targetNode];
3151 + } else nodes = h[combinator](nodes);
3152 + }
3153 + for (var i = 0, node; node = nodes[i]; i++)
3154 + if (node == targetNode) return [targetNode];
3155 + return [];
3156 + }
3157 + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3158 + },
3159 +
3160 + className: function(nodes, root, className, combinator) {
3161 + if (nodes && combinator) nodes = this[combinator](nodes);
3162 + return Selector.handlers.byClassName(nodes, root, className);
3163 + },
3164 +
3165 + byClassName: function(nodes, root, className) {
3166 + if (!nodes) nodes = Selector.handlers.descendant([root]);
3167 + var needle = ' ' + className + ' ';
3168 + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3169 + nodeClassName = node.className;
3170 + if (nodeClassName.length == 0) continue;
3171 + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3172 + results.push(node);
3173 + }
3174 + return results;
3175 + },
3176 +
3177 + attrPresence: function(nodes, root, attr) {
3178 + if (!nodes) nodes = root.getElementsByTagName("*");
3179 + var results = [];
3180 + for (var i = 0, node; node = nodes[i]; i++)
3181 + if (Element.hasAttribute(node, attr)) results.push(node);
3182 + return results;
3183 + },
3184 +
3185 + attr: function(nodes, root, attr, value, operator) {
3186 + if (!nodes) nodes = root.getElementsByTagName("*");
3187 + var handler = Selector.operators[operator], results = [];
3188 + for (var i = 0, node; node = nodes[i]; i++) {
3189 + var nodeValue = Element.readAttribute(node, attr);
3190 + if (nodeValue === null) continue;
3191 + if (handler(nodeValue, value)) results.push(node);
3192 + }
3193 + return results;
3194 + },
3195 +
3196 + pseudo: function(nodes, name, value, root, combinator) {
3197 + if (nodes && combinator) nodes = this[combinator](nodes);
3198 + if (!nodes) nodes = root.getElementsByTagName("*");
3199 + return Selector.pseudos[name](nodes, value, root);
3200 + }
3201 + },
3202 +
3203 + pseudos: {
3204 + 'first-child': function(nodes, value, root) {
3205 + for (var i = 0, results = [], node; node = nodes[i]; i++) {
3206 + if (Selector.handlers.previousElementSibling(node)) continue;
3207 + results.push(node);
3208 + }
3209 + return results;
3210 + },
3211 + 'last-child': function(nodes, value, root) {
3212 + for (var i = 0, results = [], node; node = nodes[i]; i++) {
3213 + if (Selector.handlers.nextElementSibling(node)) continue;
3214 + results.push(node);
3215 + }
3216 + return results;
3217 + },
3218 + 'only-child': function(nodes, value, root) {
3219 + var h = Selector.handlers;
3220 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3221 + if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3222 + results.push(node);
3223 + return results;
3224 + },
3225 + 'nth-child': function(nodes, formula, root) {
3226 + return Selector.pseudos.nth(nodes, formula, root);
3227 + },
3228 + 'nth-last-child': function(nodes, formula, root) {
3229 + return Selector.pseudos.nth(nodes, formula, root, true);
3230 + },
3231 + 'nth-of-type': function(nodes, formula, root) {
3232 + return Selector.pseudos.nth(nodes, formula, root, false, true);
3233 + },
3234 + 'nth-last-of-type': function(nodes, formula, root) {
3235 + return Selector.pseudos.nth(nodes, formula, root, true, true);
3236 + },
3237 + 'first-of-type': function(nodes, formula, root) {
3238 + return Selector.pseudos.nth(nodes, "1", root, false, true);
3239 + },
3240 + 'last-of-type': function(nodes, formula, root) {
3241 + return Selector.pseudos.nth(nodes, "1", root, true, true);
3242 + },
3243 + 'only-of-type': function(nodes, formula, root) {
3244 + var p = Selector.pseudos;
3245 + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3246 + },
3247 +
3248 + // handles the an+b logic
3249 + getIndices: function(a, b, total) {
3250 + if (a == 0) return b > 0 ? [b] : [];
3251 + return $R(1, total).inject([], function(memo, i) {
3252 + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3253 + return memo;
3254 + });
3255 + },
3256 +
3257 + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3258 + nth: function(nodes, formula, root, reverse, ofType) {
3259 + if (nodes.length == 0) return [];
3260 + if (formula == 'even') formula = '2n+0';
3261 + if (formula == 'odd') formula = '2n+1';
3262 + var h = Selector.handlers, results = [], indexed = [], m;
3263 + h.mark(nodes);
3264 + for (var i = 0, node; node = nodes[i]; i++) {
3265 + if (!node.parentNode._counted) {
3266 + h.index(node.parentNode, reverse, ofType);
3267 + indexed.push(node.parentNode);
3268 + }
3269 + }
3270 + if (formula.match(/^\d+$/)) { // just a number
3271 + formula = Number(formula);
3272 + for (var i = 0, node; node = nodes[i]; i++)
3273 + if (node.nodeIndex == formula) results.push(node);
3274 + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3275 + if (m[1] == "-") m[1] = -1;
3276 + var a = m[1] ? Number(m[1]) : 1;
3277 + var b = m[2] ? Number(m[2]) : 0;
3278 + var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3279 + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3280 + for (var j = 0; j < l; j++)
3281 + if (node.nodeIndex == indices[j]) results.push(node);
3282 + }
3283 + }
3284 + h.unmark(nodes);
3285 + h.unmark(indexed);
3286 + return results;
3287 + },
3288 +
3289 + 'empty': function(nodes, value, root) {
3290 + for (var i = 0, results = [], node; node = nodes[i]; i++) {
3291 + // IE treats comments as element nodes
3292 + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3293 + results.push(node);
3294 + }
3295 + return results;
3296 + },
3297 +
3298 + 'not': function(nodes, selector, root) {
3299 + var h = Selector.handlers, selectorType, m;
3300 + var exclusions = new Selector(selector).findElements(root);
3301 + h.mark(exclusions);
3302 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3303 + if (!node._counted) results.push(node);
3304 + h.unmark(exclusions);
3305 + return results;
3306 + },
3307 +
3308 + 'enabled': function(nodes, value, root) {
3309 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3310 + if (!node.disabled) results.push(node);
3311 + return results;
3312 + },
3313 +
3314 + 'disabled': function(nodes, value, root) {
3315 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3316 + if (node.disabled) results.push(node);
3317 + return results;
3318 + },
3319 +
3320 + 'checked': function(nodes, value, root) {
3321 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3322 + if (node.checked) results.push(node);
3323 + return results;
3324 + }
3325 + },
3326 +
3327 + operators: {
3328 + '=': function(nv, v) { return nv == v; },
3329 + '!=': function(nv, v) { return nv != v; },
3330 + '^=': function(nv, v) { return nv.startsWith(v); },
3331 + '$=': function(nv, v) { return nv.endsWith(v); },
3332 + '*=': function(nv, v) { return nv.include(v); },
3333 + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3334 + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3335 + },
3336 +
3337 + matchElements: function(elements, expression) {
3338 + var matches = new Selector(expression).findElements(), h = Selector.handlers;
3339 + h.mark(matches);
3340 + for (var i = 0, results = [], element; element = elements[i]; i++)
3341 + if (element._counted) results.push(element);
3342 + h.unmark(matches);
3343 + return results;
3344 + },
3345 +
3346 + findElement: function(elements, expression, index) {
3347 + if (Object.isNumber(expression)) {
3348 + index = expression; expression = false;
3349 + }
3350 + return Selector.matchElements(elements, expression || '*')[index || 0];
3351 + },
3352 +
3353 + findChildElements: function(element, expressions) {
3354 + var exprs = expressions.join(',');
3355 + expressions = [];
3356 + exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3357 + expressions.push(m[1].strip());
3358 + });
3359 + var results = [], h = Selector.handlers;
3360 + for (var i = 0, l = expressions.length, selector; i < l; i++) {
3361 + selector = new Selector(expressions[i].strip());
3362 + h.concat(results, selector.findElements(element));
3363 + }
3364 + return (l > 1) ? h.unique(results) : results;
1665 }
3365 }
1666 });
3366 });
1667
3367
1668 - Insertion.Top = Class.create();
3368 + if (Prototype.Browser.IE) {
1669 - Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
3369 + // IE returns comment nodes on getElementsByTagName("*").
1670 - initializeRange: function() {
3370 + // Filter them out.
1671 - this.range.selectNodeContents(this.element);
3371 + Selector.handlers.concat = function(a, b) {
1672 - this.range.collapse(true);
3372 + for (var i = 0, node; node = b[i]; i++)
1673 - },
3373 + if (node.tagName !== "!") a.push(node);
1674 -
3374 + return a;
1675 - insertContent: function(fragments) {
3375 + };
1676 - fragments.reverse(false).each((function(fragment) {
3376 + }
1677 - this.element.insertBefore(fragment, this.element.firstChild);
3377 +
1678 - }).bind(this));
3378 + function $$() {
3379 + return Selector.findChildElements(document, $A(arguments));
3380 + }
3381 + var Form = {
3382 + reset: function(form) {
3383 + $(form).reset();
3384 + return form;
3385 + },
3386 +
3387 + serializeElements: function(elements, options) {
3388 + if (typeof options != 'object') options = { hash: !!options };
3389 + else if (Object.isUndefined(options.hash)) options.hash = true;
3390 + var key, value, submitted = false, submit = options.submit;
3391 +
3392 + var data = elements.inject({ }, function(result, element) {
3393 + if (!element.disabled && element.name) {
3394 + key = element.name; value = $(element).getValue();
3395 + if (value != null && (element.type != 'submit' || (!submitted &&
3396 + submit !== false && (!submit || key == submit) && (submitted = true)))) {
3397 + if (key in result) {
3398 + // a key is already present; construct an array of values
3399 + if (!Object.isArray(result[key])) result[key] = [result[key]];
3400 + result[key].push(value);
3401 + }
3402 + else result[key] = value;
3403 + }
3404 + }
3405 + return result;
3406 + });
3407 +
3408 + return options.hash ? data : Object.toQueryString(data);
3409 + }
3410 + };
3411 +
3412 + Form.Methods = {
3413 + serialize: function(form, options) {
3414 + return Form.serializeElements(Form.getElements(form), options);
3415 + },
3416 +
3417 + getElements: function(form) {
3418 + return $A($(form).getElementsByTagName('*')).inject([],
3419 + function(elements, child) {
3420 + if (Form.Element.Serializers[child.tagName.toLowerCase()])
3421 + elements.push(Element.extend(child));
3422 + return elements;
3423 + }
3424 + );
3425 + },
3426 +
3427 + getInputs: function(form, typeName, name) {
3428 + form = $(form);
3429 + var inputs = form.getElementsByTagName('input');
3430 +
3431 + if (!typeName && !name) return $A(inputs).map(Element.extend);
3432 +
3433 + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3434 + var input = inputs[i];
3435 + if ((typeName && input.type != typeName) || (name && input.name != name))
3436 + continue;
3437 + matchingInputs.push(Element.extend(input));
3438 + }
3439 +
3440 + return matchingInputs;
3441 + },
3442 +
3443 + disable: function(form) {
3444 + form = $(form);
3445 + Form.getElements(form).invoke('disable');
3446 + return form;
3447 + },
3448 +
3449 + enable: function(form) {
3450 + form = $(form);
3451 + Form.getElements(form).invoke('enable');
3452 + return form;
3453 + },
3454 +
3455 + findFirstElement: function(form) {
3456 + var elements = $(form).getElements().findAll(function(element) {
3457 + return 'hidden' != element.type && !element.disabled;
3458 + });
3459 + var firstByIndex = elements.findAll(function(element) {
3460 + return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
3461 + }).sortBy(function(element) { return element.tabIndex }).first();
3462 +
3463 + return firstByIndex ? firstByIndex : elements.find(function(element) {
3464 + return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
3465 + });
3466 + },
3467 +
3468 + focusFirstElement: function(form) {
3469 + form = $(form);
3470 + form.findFirstElement().activate();
3471 + return form;
3472 + },
3473 +
3474 + request: function(form, options) {
3475 + form = $(form), options = Object.clone(options || { });
3476 +
3477 + var params = options.parameters, action = form.readAttribute('action') || '';
3478 + if (action.blank()) action = window.location.href;
3479 + options.parameters = form.serialize(true);
3480 +
3481 + if (params) {
3482 + if (Object.isString(params)) params = params.toQueryParams();
3483 + Object.extend(options.parameters, params);
3484 + }
3485 +
3486 + if (form.hasAttribute('method') && !options.method)
3487 + options.method = form.method;
3488 +
3489 + return new Ajax.Request(action, options);
3490 + }
3491 + };
3492 +
3493 + /*--------------------------------------------------------------------------*/
3494 +
3495 + Form.Element = {
3496 + focus: function(element) {
3497 + $(element).focus();
3498 + return element;
3499 + },
3500 +
3501 + select: function(element) {
3502 + $(element).select();
3503 + return element;
3504 + }
3505 + };
3506 +
3507 + Form.Element.Methods = {
3508 + serialize: function(element) {
3509 + element = $(element);
3510 + if (!element.disabled && element.name) {
3511 + var value = element.getValue();
3512 + if (value != undefined) {
3513 + var pair = { };
3514 + pair[element.name] = value;
3515 + return Object.toQueryString(pair);
3516 + }
3517 + }
3518 + return '';
3519 + },
3520 +
3521 + getValue: function(element) {
3522 + element = $(element);
3523 + var method = element.tagName.toLowerCase();
3524 + return Form.Element.Serializers[method](element);
3525 + },
3526 +
3527 + setValue: function(element, value) {
3528 + element = $(element);
3529 + var method = element.tagName.toLowerCase();
3530 + Form.Element.Serializers[method](element, value);
3531 + return element;
3532 + },
3533 +
3534 + clear: function(element) {
3535 + $(element).value = '';
3536 + return element;
3537 + },
3538 +
3539 + present: function(element) {
3540 + return $(element).value != '';
3541 + },
3542 +
3543 + activate: function(element) {
3544 + element = $(element);
3545 + try {
3546 + element.focus();
3547 + if (element.select && (element.tagName.toLowerCase() != 'input' ||
3548 + !['button', 'reset', 'submit'].include(element.type)))
3549 + element.select();
3550 + } catch (e) { }
3551 + return element;
3552 + },
3553 +
3554 + disable: function(element) {
3555 + element = $(element);
3556 + element.blur();
3557 + element.disabled = true;
3558 + return element;
3559 + },
3560 +
3561 + enable: function(element) {
3562 + element = $(element);
3563 + element.disabled = false;
3564 + return element;
3565 + }
3566 + };
3567 +
3568 + /*--------------------------------------------------------------------------*/
3569 +
3570 + var Field = Form.Element;
3571 + var $F = Form.Element.Methods.getValue;
3572 +
3573 + /*--------------------------------------------------------------------------*/
3574 +
3575 + Form.Element.Serializers = {
3576 + input: function(element, value) {
3577 + switch (element.type.toLowerCase()) {
3578 + case 'checkbox':
3579 + case 'radio':
3580 + return Form.Element.Serializers.inputSelector(element, value);
3581 + default:
3582 + return Form.Element.Serializers.textarea(element, value);
3583 + }
3584 + },
3585 +
3586 + inputSelector: function(element, value) {
3587 + if (Object.isUndefined(value)) return element.checked ? element.value : null;
3588 + else element.checked = !!value;
3589 + },
3590 +
3591 + textarea: function(element, value) {
3592 + if (Object.isUndefined(value)) return element.value;
3593 + else element.value = value;
3594 + },
3595 +
3596 + select: function(element, index) {
3597 + if (Object.isUndefined(index))
3598 + return this[element.type == 'select-one' ?
3599 + 'selectOne' : 'selectMany'](element);
3600 + else {
3601 + var opt, value, single = !Object.isArray(index);
3602 + for (var i = 0, length = element.length; i < length; i++) {
3603 + opt = element.options[i];
3604 + value = this.optionValue(opt);
3605 + if (single) {
3606 + if (value == index) {
3607 + opt.selected = true;
3608 + return;
3609 + }
3610 + }
3611 + else opt.selected = index.include(value);
3612 + }
3613 + }
3614 + },
3615 +
3616 + selectOne: function(element) {
3617 + var index = element.selectedIndex;
3618 + return index >= 0 ? this.optionValue(element.options[index]) : null;
3619 + },
3620 +
3621 + selectMany: function(element) {
3622 + var values, length = element.length;
3623 + if (!length) return null;
3624 +
3625 + for (var i = 0, values = []; i < length; i++) {
3626 + var opt = element.options[i];
3627 + if (opt.selected) values.push(this.optionValue(opt));
3628 + }
3629 + return values;
3630 + },
3631 +
3632 + optionValue: function(opt) {
3633 + // extend element because hasAttribute may not be native
3634 + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
3635 + }
3636 + };
3637 +
3638 + /*--------------------------------------------------------------------------*/
3639 +
3640 + Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3641 + initialize: function($super, element, frequency, callback) {
3642 + $super(callback, frequency);
3643 + this.element = $(element);
3644 + this.lastValue = this.getValue();
3645 + },
3646 +
3647 + execute: function() {
3648 + var value = this.getValue();
3649 + if (Object.isString(this.lastValue) && Object.isString(value) ?
3650 + this.lastValue != value : String(this.lastValue) != String(value)) {
3651 + this.callback(this.element, value);
3652 + this.lastValue = value;
3653 + }
3654 + }
3655 + });
3656 +
3657 + Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3658 + getValue: function() {
3659 + return Form.Element.getValue(this.element);
3660 + }
3661 + });
3662 +
3663 + Form.Observer = Class.create(Abstract.TimedObserver, {
3664 + getValue: function() {
3665 + return Form.serialize(this.element);
3666 + }
3667 + });
3668 +
3669 + /*--------------------------------------------------------------------------*/
3670 +
3671 + Abstract.EventObserver = Class.create({
3672 + initialize: function(element, callback) {
3673 + this.element = $(element);
3674 + this.callback = callback;
3675 +
3676 + this.lastValue = this.getValue();
3677 + if (this.element.tagName.toLowerCase() == 'form')
3678 + this.registerFormCallbacks();
3679 + else
3680 + this.registerCallback(this.element);
3681 + },
3682 +
3683 + onElementEvent: function() {
3684 + var value = this.getValue();
3685 + if (this.lastValue != value) {
3686 + this.callback(this.element, value);
3687 + this.lastValue = value;
3688 + }
3689 + },
3690 +
3691 + registerFormCallbacks: function() {
3692 + Form.getElements(this.element).each(this.registerCallback, this);
3693 + },
3694 +
3695 + registerCallback: function(element) {
3696 + if (element.type) {
3697 + switch (element.type.toLowerCase()) {
3698 + case 'checkbox':
3699 + case 'radio':
3700 + Event.observe(element, 'click', this.onElementEvent.bind(this));
3701 + break;
3702 + default:
3703 + Event.observe(element, 'change', this.onElementEvent.bind(this));
3704 + break;
3705 + }
3706 + }
3707 + }
3708 + });
3709 +
3710 + Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
3711 + getValue: function() {
3712 + return Form.Element.getValue(this.element);
3713 + }
3714 + });
3715 +
3716 + Form.EventObserver = Class.create(Abstract.EventObserver, {
3717 + getValue: function() {
3718 + return Form.serialize(this.element);
3719 + }
3720 + });
3721 + if (!window.Event) var Event = { };
3722 +
3723 + Object.extend(Event, {
3724 + KEY_BACKSPACE: 8,
3725 + KEY_TAB: 9,
3726 + KEY_RETURN: 13,
3727 + KEY_ESC: 27,
3728 + KEY_LEFT: 37,
3729 + KEY_UP: 38,
3730 + KEY_RIGHT: 39,
3731 + KEY_DOWN: 40,
3732 + KEY_DELETE: 46,
3733 + KEY_HOME: 36,
3734 + KEY_END: 35,
3735 + KEY_PAGEUP: 33,
3736 + KEY_PAGEDOWN: 34,
3737 + KEY_INSERT: 45,
3738 +
3739 + cache: { },
3740 +
3741 + relatedTarget: function(event) {
3742 + var element;
3743 + switch(event.type) {
3744 + case 'mouseover': element = event.fromElement; break;
3745 + case 'mouseout': element = event.toElement; break;
3746 + default: return null;
3747 + }
3748 + return Element.extend(element);
1679 }
3749 }
1680 });
3750 });
1681
3751
1682 - Insertion.Bottom = Class.create();
3752 + Event.Methods = (function() {
1683 - Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
3753 + var isButton;
1684 - initializeRange: function() {
3754 +
1685 - this.range.selectNodeContents(this.element);
3755 + if (Prototype.Browser.IE) {
1686 - this.range.collapse(this.element);
3756 + var buttonMap = { 0: 1, 1: 4, 2: 2 };
1687 - },
3757 + isButton = function(event, code) {
1688 -
3758 + return event.button == buttonMap[code];
1689 - insertContent: function(fragments) {
3759 + };
1690 - fragments.each((function(fragment) {
3760 +
1691 - this.element.appendChild(fragment);
3761 + } else if (Prototype.Browser.WebKit) {
1692 - }).bind(this));
3762 + isButton = function(event, code) {
1693 - }
3763 + switch (code) {
3764 + case 0: return event.which == 1 && !event.metaKey;
3765 + case 1: return event.which == 1 && event.metaKey;
3766 + default: return false;
3767 + }
3768 + };
3769 +
3770 + } else {
3771 + isButton = function(event, code) {
3772 + return event.which ? (event.which === code + 1) : (event.button === code);
3773 + };
3774 + }
3775 +
3776 + return {
3777 + isLeftClick: function(event) { return isButton(event, 0) },
3778 + isMiddleClick: function(event) { return isButton(event, 1) },
3779 + isRightClick: function(event) { return isButton(event, 2) },
3780 +
3781 + element: function(event) {
3782 + var node = Event.extend(event).target;
3783 + return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
3784 + },
3785 +
3786 + findElement: function(event, expression) {
3787 + var element = Event.element(event);
3788 + if (!expression) return element;
3789 + var elements = [element].concat(element.ancestors());
3790 + return Selector.findElement(elements, expression, 0);
3791 + },
3792 +
3793 + pointer: function(event) {
3794 + return {
3795 + x: event.pageX || (event.clientX +
3796 + (document.documentElement.scrollLeft || document.body.scrollLeft)),
3797 + y: event.pageY || (event.clientY +
3798 + (document.documentElement.scrollTop || document.body.scrollTop))
3799 + };
3800 + },
3801 +
3802 + pointerX: function(event) { return Event.pointer(event).x },
3803 + pointerY: function(event) { return Event.pointer(event).y },
3804 +
3805 + stop: function(event) {
3806 + Event.extend(event);
3807 + event.preventDefault();
3808 + event.stopPropagation();
3809 + event.stopped = true;
3810 + }
3811 + };
3812 + })();
3813 +
3814 + Event.extend = (function() {
3815 + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
3816 + m[name] = Event.Methods[name].methodize();
3817 + return m;
3818 + });
3819 +
3820 + if (Prototype.Browser.IE) {
3821 + Object.extend(methods, {
3822 + stopPropagation: function() { this.cancelBubble = true },
3823 + preventDefault: function() { this.returnValue = false },
3824 + inspect: function() { return "[object Event]" }
3825 + });
3826 +
3827 + return function(event) {
3828 + if (!event) return false;
3829 + if (event._extendedByPrototype) return event;
3830 +
3831 + event._extendedByPrototype = Prototype.emptyFunction;
3832 + var pointer = Event.pointer(event);
3833 + Object.extend(event, {
3834 + target: event.srcElement,
3835 + relatedTarget: Event.relatedTarget(event),
3836 + pageX: pointer.x,
3837 + pageY: pointer.y
3838 + });
3839 + return Object.extend(event, methods);
3840 + };
3841 +
3842 + } else {
3843 + Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
3844 + Object.extend(Event.prototype, methods);
3845 + return Prototype.K;
3846 + }
3847 + })();
3848 +
3849 + Object.extend(Event, (function() {
3850 + var cache = Event.cache;
3851 +
3852 + function getEventID(element) {
3853 + if (element._eventID) return element._eventID;
3854 + arguments.callee.id = arguments.callee.id || 1;
3855 + return element._eventID = ++arguments.callee.id;
3856 + }
3857 +
3858 + function getDOMEventName(eventName) {
3859 + if (eventName && eventName.include(':')) return "dataavailable";
3860 + return eventName;
3861 + }
3862 +
3863 + function getCacheForID(id) {
3864 + return cache[id] = cache[id] || { };
3865 + }
3866 +
3867 + function getWrappersForEventName(id, eventName) {
3868 + var c = getCacheForID(id);
3869 + return c[eventName] = c[eventName] || [];
3870 + }
3871 +
3872 + function createWrapper(element, eventName, handler) {
3873 + var id = getEventID(element);
3874 + var c = getWrappersForEventName(id, eventName);
3875 + if (c.pluck("handler").include(handler)) return false;
3876 +
3877 + var wrapper = function(event) {
3878 + if (!Event || !Event.extend ||
3879 + (event.eventName && event.eventName != eventName))
3880 + return false;
3881 +
3882 + Event.extend(event);
3883 + handler.call(element, event)
3884 + };
3885 +
3886 + wrapper.handler = handler;
3887 + c.push(wrapper);
3888 + return wrapper;
3889 + }
3890 +
3891 + function findWrapper(id, eventName, handler) {
3892 + var c = getWrappersForEventName(id, eventName);
3893 + return c.find(function(wrapper) { return wrapper.handler == handler });
3894 + }
3895 +
3896 + function destroyWrapper(id, eventName, handler) {
3897 + var c = getCacheForID(id);
3898 + if (!c[eventName]) return false;
3899 + c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
3900 + }
3901 +
3902 + function destroyCache() {
3903 + for (var id in cache)
3904 + for (var eventName in cache[id])
3905 + cache[id][eventName] = null;
3906 + }
3907 +
3908 + if (window.attachEvent) {
3909 + window.attachEvent("onunload", destroyCache);
3910 + }
3911 +
3912 + return {
3913 + observe: function(element, eventName, handler) {
3914 + element = $(element);
3915 + var name = getDOMEventName(eventName);
3916 +
3917 + var wrapper = createWrapper(element, eventName, handler);
3918 + if (!wrapper) return element;
3919 +
3920 + if (element.addEventListener) {
3921 + element.addEventListener(name, wrapper, false);
3922 + } else {
3923 + element.attachEvent("on" + name, wrapper);
3924 + }
3925 +
3926 + return element;
3927 + },
3928 +
3929 + stopObserving: function(element, eventName, handler) {
3930 + element = $(element);
3931 + var id = getEventID(element), name = getDOMEventName(eventName);
3932 +
3933 + if (!handler && eventName) {
3934 + getWrappersForEventName(id, eventName).each(function(wrapper) {
3935 + element.stopObserving(eventName, wrapper.handler);
3936 + });
3937 + return element;
3938 +
3939 + } else if (!eventName) {
3940 + Object.keys(getCacheForID(id)).each(function(eventName) {
3941 + element.stopObserving(eventName);
1694 });
3942 });
1695 -
3943 + return element;
1696 - Insertion.After = Class.create();
3944 + }
1697 - Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
3945 +
1698 - initializeRange: function() {
3946 + var wrapper = findWrapper(id, eventName, handler);
1699 - this.range.setStartAfter(this.element);
3947 + if (!wrapper) return element;
1700 - },
3948 +
1701 -
3949 + if (element.removeEventListener) {
1702 - insertContent: function(fragments) {
3950 + element.removeEventListener(name, wrapper, false);
1703 - fragments.each((function(fragment) {
3951 + } else {
1704 - this.element.parentNode.insertBefore(fragment,
3952 + element.detachEvent("on" + name, wrapper);
1705 - this.element.nextSibling);
3953 + }
1706 - }).bind(this));
3954 +
1707 - }
3955 + destroyWrapper(id, eventName, handler);
3956 +
3957 + return element;
3958 + },
3959 +
3960 + fire: function(element, eventName, memo) {
3961 + element = $(element);
3962 + if (element == document && document.createEvent && !element.dispatchEvent)
3963 + element = document.documentElement;
3964 +
3965 + if (document.createEvent) {
3966 + var event = document.createEvent("HTMLEvents");
3967 + event.initEvent("dataavailable", true, true);
3968 + } else {
3969 + var event = document.createEventObject();
3970 + event.eventType = "ondataavailable";
3971 + }
3972 +
3973 + event.eventName = eventName;
3974 + event.memo = memo || { };
3975 +
3976 + if (document.createEvent) {
3977 + element.dispatchEvent(event);
3978 + } else {
3979 + element.fireEvent(event.eventType, event);
3980 + }
3981 +
3982 + return Event.extend(event);
3983 + }
3984 + };
3985 + })());
3986 +
3987 + Object.extend(Event, Event.Methods);
3988 +
3989 + Element.addMethods({
3990 + fire: Event.fire,
3991 + observe: Event.observe,
3992 + stopObserving: Event.stopObserving
3993 + });
3994 +
3995 + Object.extend(document, {
3996 + fire: Element.Methods.fire.methodize(),
3997 + observe: Element.Methods.observe.methodize(),
3998 + stopObserving: Element.Methods.stopObserving.methodize()
1708 });
3999 });
1709
4000
4001 + (function() {
4002 + /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4003 + Matthias Miller, Dean Edwards and John Resig. */
4004 +
4005 + var timer, fired = false;
4006 +
4007 + function fireContentLoadedEvent() {
4008 + if (fired) return;
4009 + if (timer) window.clearInterval(timer);
4010 + document.fire("dom:loaded");
4011 + fired = true;
4012 + }
4013 +
4014 + if (document.addEventListener) {
4015 + if (Prototype.Browser.WebKit) {
4016 + timer = window.setInterval(function() {
4017 + if (/loaded|complete/.test(document.readyState))
4018 + fireContentLoadedEvent();
4019 + }, 0);
4020 +
4021 + Event.observe(window, "load", fireContentLoadedEvent);
4022 +
4023 + } else {
4024 + document.addEventListener("DOMContentLoaded",
4025 + fireContentLoadedEvent, false);
4026 + }
4027 +
4028 + } else {
4029 + document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
4030 + $("__onDOMContentLoaded").onreadystatechange = function() {
4031 + if (this.readyState == "complete") {
4032 + this.onreadystatechange = null;
4033 + fireContentLoadedEvent();
4034 + }
4035 + };
4036 + }
4037 + })();
4038 + /*------------------------------- DEPRECATED -------------------------------*/
4039 +
4040 + Hash.toQueryString = Object.toQueryString;
4041 +
4042 + var Toggle = { display: Element.toggle };
4043 +
4044 + Element.Methods.childOf = Element.Methods.descendantOf;
4045 +
4046 + var Insertion = {
4047 + Before: function(element, content) {
4048 + return Element.insert(element, {before:content});
4049 + },
4050 +
4051 + Top: function(element, content) {
4052 + return Element.insert(element, {top:content});
4053 + },
4054 +
4055 + Bottom: function(element, content) {
4056 + return Element.insert(element, {bottom:content});
4057 + },
4058 +
4059 + After: function(element, content) {
4060 + return Element.insert(element, {after:content});
4061 + }
4062 + };
4063 +
4064 + var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4065 +
4066 + // This should be moved to script.aculo.us; notice the deprecated methods
4067 + // further below, that map to the newer Element methods.
4068 + var Position = {
4069 + // set to true if needed, warning: firefox performance problems
4070 + // NOT neeeded for page scrolling, only if draggable contained in
4071 + // scrollable elements
4072 + includeScrollOffsets: false,
4073 +
4074 + // must be called before calling withinIncludingScrolloffset, every time the
4075 + // page is scrolled
4076 + prepare: function() {
4077 + this.deltaX = window.pageXOffset
4078 + || document.documentElement.scrollLeft
4079 + || document.body.scrollLeft
4080 + || 0;
4081 + this.deltaY = window.pageYOffset
4082 + || document.documentElement.scrollTop
4083 + || document.body.scrollTop
4084 + || 0;
4085 + },
4086 +
4087 + // caches x/y coordinate pair to use with overlap
4088 + within: function(element, x, y) {
4089 + if (this.includeScrollOffsets)
4090 + return this.withinIncludingScrolloffsets(element, x, y);
4091 + this.xcomp = x;
4092 + this.ycomp = y;
4093 + this.offset = Element.cumulativeOffset(element);
4094 +
4095 + return (y >= this.offset[1] &&
4096 + y < this.offset[1] + element.offsetHeight &&
4097 + x >= this.offset[0] &&
4098 + x < this.offset[0] + element.offsetWidth);
4099 + },
4100 +
4101 + withinIncludingScrolloffsets: function(element, x, y) {
4102 + var offsetcache = Element.cumulativeScrollOffset(element);
4103 +
4104 + this.xcomp = x + offsetcache[0] - this.deltaX;
4105 + this.ycomp = y + offsetcache[1] - this.deltaY;
4106 + this.offset = Element.cumulativeOffset(element);
4107 +
4108 + return (this.ycomp >= this.offset[1] &&
4109 + this.ycomp < this.offset[1] + element.offsetHeight &&
4110 + this.xcomp >= this.offset[0] &&
4111 + this.xcomp < this.offset[0] + element.offsetWidth);
4112 + },
4113 +
4114 + // within must be called directly before
4115 + overlap: function(mode, element) {
4116 + if (!mode) return 0;
4117 + if (mode == 'vertical')
4118 + return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4119 + element.offsetHeight;
4120 + if (mode == 'horizontal')
4121 + return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4122 + element.offsetWidth;
4123 + },
4124 +
4125 + // Deprecation layer -- use newer Element methods now (1.5.2).
4126 +
4127 + cumulativeOffset: Element.Methods.cumulativeOffset,
4128 +
4129 + positionedOffset: Element.Methods.positionedOffset,
4130 +
4131 + absolutize: function(element) {
4132 + Position.prepare();
4133 + return Element.absolutize(element);
4134 + },
4135 +
4136 + relativize: function(element) {
4137 + Position.prepare();
4138 + return Element.relativize(element);
4139 + },
4140 +
4141 + realOffset: Element.Methods.cumulativeScrollOffset,
4142 +
4143 + offsetParent: Element.Methods.getOffsetParent,
4144 +
4145 + page: Element.Methods.viewportOffset,
4146 +
4147 + clone: function(source, target, options) {
4148 + options = options || { };
4149 + return Element.clonePosition(target, source, options);
4150 + }
4151 + };
4152 +
4153 + /*--------------------------------------------------------------------------*/
4154 +
4155 + if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4156 + function iter(name) {
4157 + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4158 + }
4159 +
4160 + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4161 + function(element, className) {
4162 + className = className.toString().strip();
4163 + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4164 + return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4165 + } : function(element, className) {
4166 + className = className.toString().strip();
4167 + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4168 + if (!classNames && !className) return elements;
4169 +
4170 + var nodes = $(element).getElementsByTagName('*');
4171 + className = ' ' + className + ' ';
4172 +
4173 + for (var i = 0, child, cn; child = nodes[i]; i++) {
4174 + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4175 + (classNames && classNames.all(function(name) {
4176 + return !name.toString().blank() && cn.include(' ' + name + ' ');
4177 + }))))
4178 + elements.push(Element.extend(child));
4179 + }
4180 + return elements;
4181 + };
4182 +
4183 + return function(className, parentElement) {
4184 + return $(parentElement || document.body).getElementsByClassName(className);
4185 + };
4186 + }(Element.Methods);
4187 +
1710 /*--------------------------------------------------------------------------*/
4188 /*--------------------------------------------------------------------------*/
1711
4189
1712 Element.ClassNames = Class.create();
4190 Element.ClassNames = Class.create();
1713 Element.ClassNames.prototype = {
4191 Element.ClassNames.prototype = {
1714 initialize: function(element) {
4192 initialize: function(element) {
1715 this.element = $(element);
4193 this.element = $(element);
1716 },
4194 },
1717
4195
1718 _each: function(iterator) {
4196 _each: function(iterator) {
1719 this.element.className.split(/\s+/).select(function(name) {
4197 this.element.className.split(/\s+/).select(function(name) {
1720 return name.length > 0;
4198 return name.length > 0;
1721 })._each(iterator);
4199 })._each(iterator);
1722 },
4200 },
1723
4201
1724 set: function(className) {
4202 set: function(className) {
1725 this.element.className = className;
4203 this.element.className = className;
1726 },
4204 },
1727
4205
1728 add: function(classNameToAdd) {
4206 add: function(classNameToAdd) {
1729 if (this.include(classNameToAdd)) return;
4207 if (this.include(classNameToAdd)) return;
1730 this.set($A(this).concat(classNameToAdd).join(' '));
4208 this.set($A(this).concat(classNameToAdd).join(' '));
1731 },
4209 },
1732
4210
1733 remove: function(classNameToRemove) {
4211 remove: function(classNameToRemove) {
1734 if (!this.include(classNameToRemove)) return;
4212 if (!this.include(classNameToRemove)) return;
1735 this.set($A(this).without(classNameToRemove).join(' '));
4213 this.set($A(this).without(classNameToRemove).join(' '));
1736 },
4214 },
1737
4215
1738 toString: function() {
4216 toString: function() {
1739 return $A(this).join(' ');
4217 return $A(this).join(' ');
1740 }
4218 }
1741 };
4219 };
1742
4220
1743 Object.extend(Element.ClassNames.prototype, Enumerable);
4221 Object.extend(Element.ClassNames.prototype, Enumerable);
1744 - var Selector = Class.create();
1745 - Selector.prototype = {
1746 - initialize: function(expression) {
1747 - this.params = {classNames: []};
1748 - this.expression = expression.toString().strip();
1749 - this.parseExpression();
1750 - this.compileMatcher();
1751 - },
1752 -
1753 - parseExpression: function() {
1754 - function abort(message) { throw 'Parse error in selector: ' + message; }
1755 -
1756 - if (this.expression == '') abort('empty expression');
1757 -
1758 - var params = this.params, expr = this.expression, match, modifier, clause, rest;
1759 - while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
1760 - params.attributes = params.attributes || [];
1761 - params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
1762 - expr = match[1];
1763 - }
1764 -
1765 - if (expr == '*') return this.params.wildcard = true;
1766 -
1767 - while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1768 - modifier = match[1], clause = match[2], rest = match[3];
1769 - switch (modifier) {
1770 - case '#': params.id = clause; break;
1771 - case '.': params.classNames.push(clause); break;
1772 - case '':
1773 - case undefined: params.tagName = clause.toUpperCase(); break;
1774 - default: abort(expr.inspect());
1775 - }
1776 - expr = rest;
1777 - }
1778 -
1779 - if (expr.length > 0) abort(expr.inspect());
1780 - },
1781 -
1782 - buildMatchExpression: function() {
1783 - var params = this.params, conditions = [], clause;
1784 -
1785 - if (params.wildcard)
1786 - conditions.push('true');
1787 - if (clause = params.id)
1788 - conditions.push('element.readAttribute("id") == ' + clause.inspect());
1789 - if (clause = params.tagName)
1790 - conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1791 - if ((clause = params.classNames).length > 0)
1792 - for (var i = 0, length = clause.length; i < length; i++)
1793 - conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1794 - if (clause = params.attributes) {
1795 - clause.each(function(attribute) {
1796 - var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1797 - var splitValueBy = function(delimiter) {
1798 - return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1799 - }
1800 -
1801 - switch (attribute.operator) {
1802 - case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
1803 - case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
1804 - case '|=': conditions.push(
1805 - splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
1806 - ); break;
1807 - case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1808 - case '':
1809 - case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1810 - default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1811 - }
1812 - });
1813 - }
1814 -
1815 - return conditions.join(' && ');
1816 - },
1817 -
1818 - compileMatcher: function() {
1819 - this.match = new Function('element', 'if (!element.tagName) return false; \
1820 - element = $(element); \
1821 - return ' + this.buildMatchExpression());
1822 - },
1823 -
1824 - findElements: function(scope) {
1825 - var element;
1826 -
1827 - if (element = $(this.params.id))
1828 - if (this.match(element))
1829 - if (!scope || Element.childOf(element, scope))
1830 - return [element];
1831 -
1832 - scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
1833 -
1834 - var results = [];
1835 - for (var i = 0, length = scope.length; i < length; i++)
1836 - if (this.match(element = scope[i]))
1837 - results.push(Element.extend(element));
1838 -
1839 - return results;
1840 - },
1841 -
1842 - toString: function() {
1843 - return this.expression;
1844 - }
1845 - }
1846 -
1847 - Object.extend(Selector, {
1848 - matchElements: function(elements, expression) {
1849 - var selector = new Selector(expression);
1850 - return elements.select(selector.match.bind(selector)).map(Element.extend);
1851 - },
1852 -
1853 - findElement: function(elements, expression, index) {
1854 - if (typeof expression == 'number') index = expression, expression = false;
1855 - return Selector.matchElements(elements, expression || '*')[index || 0];
1856 - },
1857 -
1858 - findChildElements: function(element, expressions) {
1859 - return expressions.map(function(expression) {
1860 - return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1861 - var selector = new Selector(expr);
1862 - return results.inject([], function(elements, result) {
1863 - return elements.concat(selector.findElements(result || element));
1864 - });
1865 - });
1866 - }).flatten();
1867 - }
1868 - });
1869 -
1870 - function $$() {
1871 - return Selector.findChildElements(document, $A(arguments));
1872 - }
1873 - var Form = {
1874 - reset: function(form) {
1875 - $(form).reset();
1876 - return form;
1877 - },
1878 -
1879 - serializeElements: function(elements, getHash) {
1880 - var data = elements.inject({}, function(result, element) {
1881 - if (!element.disabled && element.name) {
1882 - var key = element.name, value = $(element).getValue();
1883 - if (value != undefined) {
1884 - if (result[key]) {
1885 - if (result[key].constructor != Array) result[key] = [result[key]];
1886 - result[key].push(value);
1887 - }
1888 - else result[key] = value;
1889 - }
1890 - }
1891 - return result;
1892 - });
1893 -
1894 - return getHash ? data : Hash.toQueryString(data);
1895 - }
1896 - };
1897 -
1898 - Form.Methods = {
1899 - serialize: function(form, getHash) {
1900 - return Form.serializeElements(Form.getElements(form), getHash);
1901 - },
1902 -
1903 - getElements: function(form) {
1904 - return $A($(form).getElementsByTagName('*')).inject([],
1905 - function(elements, child) {
1906 - if (Form.Element.Serializers[child.tagName.toLowerCase()])
1907 - elements.push(Element.extend(child));
1908 - return elements;
1909 - }
1910 - );
1911 - },
1912 -
1913 - getInputs: function(form, typeName, name) {
1914 - form = $(form);
1915 - var inputs = form.getElementsByTagName('input');
1916 -
1917 - if (!typeName && !name) return $A(inputs).map(Element.extend);
1918 -
1919 - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1920 - var input = inputs[i];
1921 - if ((typeName && input.type != typeName) || (name && input.name != name))
1922 - continue;
1923 - matchingInputs.push(Element.extend(input));
1924 - }
1925 -
1926 - return matchingInputs;
1927 - },
1928 -
1929 - disable: function(form) {
1930 - form = $(form);
1931 - form.getElements().each(function(element) {
1932 - element.blur();
1933 - element.disabled = 'true';
1934 - });
1935 - return form;
1936 - },
1937 -
1938 - enable: function(form) {
1939 - form = $(form);
1940 - form.getElements().each(function(element) {
1941 - element.disabled = '';
1942 - });
1943 - return form;
1944 - },
1945 -
1946 - findFirstElement: function(form) {
1947 - return $(form).getElements().find(function(element) {
1948 - return element.type != 'hidden' && !element.disabled &&
1949 - ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1950 - });
1951 - },
1952 -
1953 - focusFirstElement: function(form) {
1954 - form = $(form);
1955 - form.findFirstElement().activate();
1956 - return form;
1957 - }
1958 - }
1959 -
1960 - Object.extend(Form, Form.Methods);
1961 -
1962 - /*--------------------------------------------------------------------------*/
1963 -
1964 - Form.Element = {
1965 - focus: function(element) {
1966 - $(element).focus();
1967 - return element;
1968 - },
1969 -
1970 - select: function(element) {
1971 - $(element).select();
1972 - return element;
1973 - }
1974 - }
1975 -
1976 - Form.Element.Methods = {
1977 - serialize: function(element) {
1978 - element = $(element);
1979 - if (!element.disabled && element.name) {
1980 - var value = element.getValue();
1981 - if (value != undefined) {
1982 - var pair = {};
1983 - pair[element.name] = value;
1984 - return Hash.toQueryString(pair);
1985 - }
1986 - }
1987 - return '';
1988 - },
1989 -
1990 - getValue: function(element) {
1991 - element = $(element);
1992 - var method = element.tagName.toLowerCase();
1993 - return Form.Element.Serializers[method](element);
1994 - },
1995 -
1996 - clear: function(element) {
1997 - $(element).value = '';
1998 - return element;
1999 - },
2000 -
2001 - present: function(element) {
2002 - return $(element).value != '';
2003 - },
2004 -
2005 - activate: function(element) {
2006 - element = $(element);
2007 - element.focus();
2008 - if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2009 - !['button', 'reset', 'submit'].include(element.type) ) )
2010 - element.select();
2011 - return element;
2012 - },
2013 -
2014 - disable: function(element) {
2015 - element = $(element);
2016 - element.disabled = true;
2017 - return element;
2018 - },
2019 -
2020 - enable: function(element) {
2021 - element = $(element);
2022 - element.blur();
2023 - element.disabled = false;
2024 - return element;
2025 - }
2026 - }
2027 -
2028 - Object.extend(Form.Element, Form.Element.Methods);
2029 - var Field = Form.Element;
2030 - var $F = Form.Element.getValue;
2031 -
2032 - /*--------------------------------------------------------------------------*/
2033 -
2034 - Form.Element.Serializers = {
2035 - input: function(element) {
2036 - switch (element.type.toLowerCase()) {
2037 - case 'checkbox':
2038 - case 'radio':
2039 - return Form.Element.Serializers.inputSelector(element);
2040 - default:
2041 - return Form.Element.Serializers.textarea(element);
2042 - }
2043 - },
2044 -
2045 - inputSelector: function(element) {
2046 - return element.checked ? element.value : null;
2047 - },
2048 -
2049 - textarea: function(element) {
2050 - return element.value;
2051 - },
2052 -
2053 - select: function(element) {
2054 - return this[element.type == 'select-one' ?
2055 - 'selectOne' : 'selectMany'](element);
2056 - },
2057 -
2058 - selectOne: function(element) {
2059 - var index = element.selectedIndex;
2060 - return index >= 0 ? this.optionValue(element.options[index]) : null;
2061 - },
2062 -
2063 - selectMany: function(element) {
2064 - var values, length = element.length;
2065 - if (!length) return null;
2066 -
2067 - for (var i = 0, values = []; i < length; i++) {
2068 - var opt = element.options[i];
2069 - if (opt.selected) values.push(this.optionValue(opt));
2070 - }
2071 - return values;
2072 - },
2073 -
2074 - optionValue: function(opt) {
2075 - // extend element because hasAttribute may not be native
2076 - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2077 - }
2078 - }
2079 -
2080 - /*--------------------------------------------------------------------------*/
2081 -
2082 - Abstract.TimedObserver = function() {}
2083 - Abstract.TimedObserver.prototype = {
2084 - initialize: function(element, frequency, callback) {
2085 - this.frequency = frequency;
2086 - this.element = $(element);
2087 - this.callback = callback;
2088 -
2089 - this.lastValue = this.getValue();
2090 - this.registerCallback();
2091 - },
2092 -
2093 - registerCallback: function() {
2094 - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2095 - },
2096 -
2097 - onTimerEvent: function() {
2098 - var value = this.getValue();
2099 - var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100 - ? this.lastValue != value : String(this.lastValue) != String(value));
2101 - if (changed) {
2102 - this.callback(this.element, value);
2103 - this.lastValue = value;
2104 - }
2105 - }
2106 - }
2107 -
2108 - Form.Element.Observer = Class.create();
2109 - Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2110 - getValue: function() {
2111 - return Form.Element.getValue(this.element);
2112 - }
2113 - });
2114 -
2115 - Form.Observer = Class.create();
2116 - Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2117 - getValue: function() {
2118 - return Form.serialize(this.element);
2119 - }
2120 - });
2121
4222
2122 /*--------------------------------------------------------------------------*/
4223 /*--------------------------------------------------------------------------*/
2123
4224
2124 - Abstract.EventObserver = function() {}
2125 - Abstract.EventObserver.prototype = {
2126 - initialize: function(element, callback) {
2127 - this.element = $(element);
2128 - this.callback = callback;
2129 -
2130 - this.lastValue = this.getValue();
2131 - if (this.element.tagName.toLowerCase() == 'form')
2132 - this.registerFormCallbacks();
2133 - else
2134 - this.registerCallback(this.element);
2135 - },
2136 -
2137 - onElementEvent: function() {
2138 - var value = this.getValue();
2139 - if (this.lastValue != value) {
2140 - this.callback(this.element, value);
2141 - this.lastValue = value;
2142 - }
2143 - },
2144 -
2145 - registerFormCallbacks: function() {
2146 - Form.getElements(this.element).each(this.registerCallback.bind(this));
2147 - },
2148 -
2149 - registerCallback: function(element) {
2150 - if (element.type) {
2151 - switch (element.type.toLowerCase()) {
2152 - case 'checkbox':
2153 - case 'radio':
2154 - Event.observe(element, 'click', this.onElementEvent.bind(this));
2155 - break;
2156 - default:
2157 - Event.observe(element, 'change', this.onElementEvent.bind(this));
2158 - break;
2159 - }
2160 - }
2161 - }
2162 - }
2163 -
2164 - Form.Element.EventObserver = Class.create();
2165 - Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2166 - getValue: function() {
2167 - return Form.Element.getValue(this.element);
2168 - }
2169 - });
2170 -
2171 - Form.EventObserver = Class.create();
2172 - Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2173 - getValue: function() {
2174 - return Form.serialize(this.element);
2175 - }
2176 - });
2177 - if (!window.Event) {
2178 - var Event = new Object();
2179 - }
2180 -
2181 - Object.extend(Event, {
2182 - KEY_BACKSPACE: 8,
2183 - KEY_TAB: 9,
2184 - KEY_RETURN: 13,
2185 - KEY_ESC: 27,
2186 - KEY_LEFT: 37,
2187 - KEY_UP: 38,
2188 - KEY_RIGHT: 39,
2189 - KEY_DOWN: 40,
2190 - KEY_DELETE: 46,
2191 - KEY_HOME: 36,
2192 - KEY_END: 35,
2193 - KEY_PAGEUP: 33,
2194 - KEY_PAGEDOWN: 34,
2195 -
2196 - element: function(event) {
2197 - return event.target || event.srcElement;
2198 - },
2199 -
2200 - isLeftClick: function(event) {
2201 - return (((event.which) && (event.which == 1)) ||
2202 - ((event.button) && (event.button == 1)));
2203 - },
2204 -
2205 - pointerX: function(event) {
2206 - return event.pageX || (event.clientX +
2207 - (document.documentElement.scrollLeft || document.body.scrollLeft));
2208 - },
2209 -
2210 - pointerY: function(event) {
2211 - return event.pageY || (event.clientY +
2212 - (document.documentElement.scrollTop || document.body.scrollTop));
2213 - },
2214 -
2215 - stop: function(event) {
2216 - if (event.preventDefault) {
2217 - event.preventDefault();
2218 - event.stopPropagation();
2219 - } else {
2220 - event.returnValue = false;
2221 - event.cancelBubble = true;
2222 - }
2223 - },
2224 -
2225 - // find the first node with the given tagName, starting from the
2226 - // node the event was triggered on; traverses the DOM upwards
2227 - findElement: function(event, tagName) {
2228 - var element = Event.element(event);
2229 - while (element.parentNode && (!element.tagName ||
2230 - (element.tagName.toUpperCase() != tagName.toUpperCase())))
2231 - element = element.parentNode;
2232 - return element;
2233 - },
2234 -
2235 - observers: false,
2236 -
2237 - _observeAndCache: function(element, name, observer, useCapture) {
2238 - if (!this.observers) this.observers = [];
2239 - if (element.addEventListener) {
2240 - this.observers.push([element, name, observer, useCapture]);
2241 - element.addEventListener(name, observer, useCapture);
2242 - } else if (element.attachEvent) {
2243 - this.observers.push([element, name, observer, useCapture]);
2244 - element.attachEvent('on' + name, observer);
2245 - }
2246 - },
2247 -
2248 - unloadCache: function() {
2249 - if (!Event.observers) return;
2250 - for (var i = 0, length = Event.observers.length; i < length; i++) {
2251 - Event.stopObserving.apply(this, Event.observers[i]);
2252 - Event.observers[i][0] = null;
2253 - }
2254 - Event.observers = false;
2255 - },
2256 -
2257 - observe: function(element, name, observer, useCapture) {
2258 - element = $(element);
2259 - useCapture = useCapture || false;
2260 -
2261 - if (name == 'keypress' &&
2262 - (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2263 - || element.attachEvent))
2264 - name = 'keydown';
2265 -
2266 - Event._observeAndCache(element, name, observer, useCapture);
2267 - },
2268 -
2269 - stopObserving: function(element, name, observer, useCapture) {
2270 - element = $(element);
2271 - useCapture = useCapture || false;
2272 -
2273 - if (name == 'keypress' &&
2274 - (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2275 - || element.detachEvent))
2276 - name = 'keydown';
2277 -
2278 - if (element.removeEventListener) {
2279 - element.removeEventListener(name, observer, useCapture);
2280 - } else if (element.detachEvent) {
2281 - try {
2282 - element.detachEvent('on' + name, observer);
2283 - } catch (e) {}
2284 - }
2285 - }
2286 - });
2287 -
2288 - /* prevent memory leaks in IE */
2289 - if (navigator.appVersion.match(/\bMSIE\b/))
2290 - Event.observe(window, 'unload', Event.unloadCache, false);
2291 - var Position = {
2292 - // set to true if needed, warning: firefox performance problems
2293 - // NOT neeeded for page scrolling, only if draggable contained in
2294 - // scrollable elements
2295 - includeScrollOffsets: false,
2296 -
2297 - // must be called before calling withinIncludingScrolloffset, every time the
2298 - // page is scrolled
2299 - prepare: function() {
2300 - this.deltaX = window.pageXOffset
2301 - || document.documentElement.scrollLeft
2302 - || document.body.scrollLeft
2303 - || 0;
2304 - this.deltaY = window.pageYOffset
2305 - || document.documentElement.scrollTop
2306 - || document.body.scrollTop
2307 - || 0;
2308 - },
2309 -
2310 - realOffset: function(element) {
2311 - var valueT = 0, valueL = 0;
2312 - do {
2313 - valueT += element.scrollTop || 0;
2314 - valueL += element.scrollLeft || 0;
2315 - element = element.parentNode;
2316 - } while (element);
2317 - return [valueL, valueT];
2318 - },
2319 -
2320 - cumulativeOffset: function(element) {
2321 - var valueT = 0, valueL = 0;
2322 - do {
2323 - valueT += element.offsetTop || 0;
2324 - valueL += element.offsetLeft || 0;
2325 - element = element.offsetParent;
2326 - } while (element);
2327 - return [valueL, valueT];
2328 - },
2329 -
2330 - positionedOffset: function(element) {
2331 - var valueT = 0, valueL = 0;
2332 - do {
2333 - valueT += element.offsetTop || 0;
2334 - valueL += element.offsetLeft || 0;
2335 - element = element.offsetParent;
2336 - if (element) {
2337 - if(element.tagName=='BODY') break;
2338 - var p = Element.getStyle(element, 'position');
2339 - if (p == 'relative' || p == 'absolute') break;
2340 - }
2341 - } while (element);
2342 - return [valueL, valueT];
2343 - },
2344 -
2345 - offsetParent: function(element) {
2346 - if (element.offsetParent) return element.offsetParent;
2347 - if (element == document.body) return element;
2348 -
2349 - while ((element = element.parentNode) && element != document.body)
2350 - if (Element.getStyle(element, 'position') != 'static')
2351 - return element;
2352 -
2353 - return document.body;
2354 - },
2355 -
2356 - // caches x/y coordinate pair to use with overlap
2357 - within: function(element, x, y) {
2358 - if (this.includeScrollOffsets)
2359 - return this.withinIncludingScrolloffsets(element, x, y);
2360 - this.xcomp = x;
2361 - this.ycomp = y;
2362 - this.offset = this.cumulativeOffset(element);
2363 -
2364 - return (y >= this.offset[1] &&
2365 - y < this.offset[1] + element.offsetHeight &&
2366 - x >= this.offset[0] &&
2367 - x < this.offset[0] + element.offsetWidth);
2368 - },
2369 -
2370 - withinIncludingScrolloffsets: function(element, x, y) {
2371 - var offsetcache = this.realOffset(element);
2372 -
2373 - this.xcomp = x + offsetcache[0] - this.deltaX;
2374 - this.ycomp = y + offsetcache[1] - this.deltaY;
2375 - this.offset = this.cumulativeOffset(element);
2376 -
2377 - return (this.ycomp >= this.offset[1] &&
2378 - this.ycomp < this.offset[1] + element.offsetHeight &&
2379 - this.xcomp >= this.offset[0] &&
2380 - this.xcomp < this.offset[0] + element.offsetWidth);
2381 - },
2382 -
2383 - // within must be called directly before
2384 - overlap: function(mode, element) {
2385 - if (!mode) return 0;
2386 - if (mode == 'vertical')
2387 - return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
2388 - element.offsetHeight;
2389 - if (mode == 'horizontal')
2390 - return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
2391 - element.offsetWidth;
2392 - },
2393 -
2394 - page: function(forElement) {
2395 - var valueT = 0, valueL = 0;
2396 -
2397 - var element = forElement;
2398 - do {
2399 - valueT += element.offsetTop || 0;
2400 - valueL += element.offsetLeft || 0;
2401 -
2402 - // Safari fix
2403 - if (element.offsetParent==document.body)
2404 - if (Element.getStyle(element,'position')=='absolute') break;
2405 -
2406 - } while (element = element.offsetParent);
2407 -
2408 - element = forElement;
2409 - do {
2410 - if (!window.opera || element.tagName=='BODY') {
2411 - valueT -= element.scrollTop || 0;
2412 - valueL -= element.scrollLeft || 0;
2413 - }
2414 - } while (element = element.parentNode);
2415 -
2416 - return [valueL, valueT];
2417 - },
2418 -
2419 - clone: function(source, target) {
2420 - var options = Object.extend({
2421 - setLeft: true,
2422 - setTop: true,
2423 - setWidth: true,
2424 - setHeight: true,
2425 - offsetTop: 0,
2426 - offsetLeft: 0
2427 - }, arguments[2] || {})
2428 -
2429 - // find page position of source
2430 - source = $(source);
2431 - var p = Position.page(source);
2432 -
2433 - // find coordinate system to use
2434 - target = $(target);
2435 - var delta = [0, 0];
2436 - var parent = null;
2437 - // delta [0,0] will do fine with position: fixed elements,
2438 - // position:absolute needs offsetParent deltas
2439 - if (Element.getStyle(target,'position') == 'absolute') {
2440 - parent = Position.offsetParent(target);
2441 - delta = Position.page(parent);
2442 - }
2443 -
2444 - // correct by body offsets (fixes Safari)
2445 - if (parent == document.body) {
2446 - delta[0] -= document.body.offsetLeft;
2447 - delta[1] -= document.body.offsetTop;
2448 - }
2449 -
2450 - // set position
2451 - if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2452 - if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2453 - if(options.setWidth) target.style.width = source.offsetWidth + 'px';
2454 - if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2455 - },
2456 -
2457 - absolutize: function(element) {
2458 - element = $(element);
2459 - if (element.style.position == 'absolute') return;
2460 - Position.prepare();
2461 -
2462 - var offsets = Position.positionedOffset(element);
2463 - var top = offsets[1];
2464 - var left = offsets[0];
2465 - var width = element.clientWidth;
2466 - var height = element.clientHeight;
2467 -
2468 - element._originalLeft = left - parseFloat(element.style.left || 0);
2469 - element._originalTop = top - parseFloat(element.style.top || 0);
2470 - element._originalWidth = element.style.width;
2471 - element._originalHeight = element.style.height;
2472 -
2473 - element.style.position = 'absolute';
2474 - element.style.top = top + 'px';
2475 - element.style.left = left + 'px';
2476 - element.style.width = width + 'px';
2477 - element.style.height = height + 'px';
2478 - },
2479 -
2480 - relativize: function(element) {
2481 - element = $(element);
2482 - if (element.style.position == 'relative') return;
2483 - Position.prepare();
2484 -
2485 - element.style.position = 'relative';
2486 - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2487 - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2488 -
2489 - element.style.top = top + 'px';
2490 - element.style.left = left + 'px';
2491 - element.style.height = element._originalHeight;
2492 - element.style.width = element._originalWidth;
2493 - }
2494 - }
2495 -
2496 - // Safari returns margins on body which is incorrect if the child is absolutely
2497 - // positioned. For performance reasons, redefine Position.cumulativeOffset for
2498 - // KHTML/WebKit only.
2499 - if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2500 - Position.cumulativeOffset = function(element) {
2501 - var valueT = 0, valueL = 0;
2502 - do {
2503 - valueT += element.offsetTop || 0;
2504 - valueL += element.offsetLeft || 0;
2505 - if (element.offsetParent == document.body)
2506 - if (Element.getStyle(element, 'position') == 'absolute') break;
2507 -
2508 - element = element.offsetParent;
2509 - } while (element);
2510 -
2511 - return [valueL, valueT];
2512 - }
2513 - }
2514 -
2515 Element.addMethods(); No newline at end of file
4225 Element.addMethods();
You need to be logged in to leave comments. Login now