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

@@ -13,7 +13,8
13 13 verify :method => :post, :only => [:submit],
14 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 19 def index
19 20 redirect_to :action => 'login'
@@ -1,18 +1,18
1 1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
2 2 <td>
3 - <%= "#{problem_counter + 1}" %>
3 + <%= "#{problem_counter}" %>
4 4 </td>
5 5 <td>
6 6 <%= "#{problem.full_name} (#{problem.name})" %>
7 7 <%= link_to '[desc]', problem.url, :popup => true if (problem.url!=nil) and (problem.url!='') %>
8 8 </td>
9 9 <td align="center">
10 - <%= @prob_submissions[problem_counter][:count] %>
10 + <%= @prob_submissions[problem_counter-1][:count] %>
11 11 </td>
12 12 <td>
13 13 <%= render :partial => 'submission_short',
14 14 :locals => {
15 - :submission => @prob_submissions[problem_counter][:submission],
15 + :submission => @prob_submissions[problem_counter-1][:submission],
16 16 :problem_name => problem.name }%>
17 17 </td>
18 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)
4 - root_path = File.join(File.dirname(__FILE__), '..')
5 -
6 - unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
7 - require 'pathname'
8 - root_path = Pathname.new(root_path).cleanpath(true).to_s
6 + module Rails
7 + class << self
8 + def boot!
9 + unless booted?
10 + preinitialize
11 + pick_boot.run
12 + end
9 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 21 end
13 22
14 - unless defined?(Rails::Initializer)
15 - if File.directory?("#{RAILS_ROOT}/vendor/rails")
16 - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
17 - else
18 - require 'rubygems'
23 + def vendor_rails?
24 + File.exist?("#{RAILS_ROOT}/vendor/rails")
25 + end
19 26
20 - environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join
21 - environment_without_comments =~ /[^#]RAILS_GEM_VERSION = '([\d.]+)'/
22 - rails_gem_version = $1
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 + def preinitialize
28 + load(preinitializer_path) if File.exist?(preinitializer_path)
29 + end
27 30
28 - if rails_gem
29 - gem "rails", "=#{rails_gem.version.version}"
30 - require rails_gem.full_gem_path + '/lib/initializer'
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
31 + def preinitializer_path
32 + "#{RAILS_ROOT}/config/preinitializer.rb"
33 + end
37 34 end
38 - else
39 - gem "rails"
40 - require 'initializer'
35 +
36 + class Boot
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 47 end
42 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 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,6 +1,6
1 - // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 - // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 - // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 + // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 + // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
4 4 // Contributors:
5 5 // Richard Livsey
6 6 // Rahul Bhargava
@@ -38,16 +38,17
38 38 throw("controls.js requires including script.aculo.us' effects.js library");
39 39
40 40 var Autocompleter = {}
41 - Autocompleter.Base = function() {};
42 - Autocompleter.Base.prototype = {
41 + Autocompleter.Base = Class.create({
43 42 baseInitialize: function(element, update, options) {
44 - this.element = $(element);
43 + element = $(element)
44 + this.element = element;
45 45 this.update = $(update);
46 46 this.hasFocus = false;
47 47 this.changed = false;
48 48 this.active = false;
49 49 this.index = 0;
50 50 this.entryCount = 0;
51 + this.oldElementValue = this.element.value;
51 52
52 53 if(this.setOptions)
53 54 this.setOptions(options);
@@ -74,6 +75,9
74 75
75 76 if(typeof(this.options.tokens) == 'string')
76 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 82 this.observer = null;
79 83
@@ -81,15 +85,14
81 85
82 86 Element.hide(this.update);
83 87
84 - Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
85 - Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
88 + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
89 + Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86 90 },
87 91
88 92 show: function() {
89 93 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90 94 if(!this.iefix &&
91 - (navigator.appVersion.indexOf('MSIE')>0) &&
92 - (navigator.userAgent.indexOf('Opera')<0) &&
95 + (Prototype.Browser.IE) &&
93 96 (Element.getStyle(this.update, 'position')=='absolute')) {
94 97 new Insertion.After(this.update,
95 98 '<iframe id="' + this.update.id + '_iefix" '+
@@ -139,17 +142,17
139 142 case Event.KEY_UP:
140 143 this.markPrevious();
141 144 this.render();
142 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145 + Event.stop(event);
143 146 return;
144 147 case Event.KEY_DOWN:
145 148 this.markNext();
146 149 this.render();
147 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150 + Event.stop(event);
148 151 return;
149 152 }
150 153 else
151 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 157 this.changed = true;
155 158 this.hasFocus = true;
@@ -195,7 +198,6
195 198 this.index==i ?
196 199 Element.addClassName(this.getEntry(i),"selected") :
197 200 Element.removeClassName(this.getEntry(i),"selected");
198 -
199 201 if(this.hasFocus) {
200 202 this.show();
201 203 this.active = true;
@@ -238,21 +240,22
238 240 }
239 241 var value = '';
240 242 if (this.options.select) {
241 - var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243 + var nodes = $(selectedElement).select('.' + this.options.select) || [];
242 244 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243 245 } else
244 246 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245 247
246 - var lastTokenPos = this.findLastToken();
247 - if (lastTokenPos != -1) {
248 - var newValue = this.element.value.substr(0, lastTokenPos + 1);
249 - var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
248 + var bounds = this.getTokenBounds();
249 + if (bounds[0] != -1) {
250 + var newValue = this.element.value.substr(0, bounds[0]);
251 + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250 252 if (whitespace)
251 253 newValue += whitespace[0];
252 - this.element.value = newValue + value;
254 + this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253 255 } else {
254 256 this.element.value = value;
255 257 }
258 + this.oldElementValue = this.element.value;
256 259 this.element.focus();
257 260
258 261 if (this.options.afterUpdateElement)
@@ -296,39 +299,48
296 299
297 300 onObserverEvent: function() {
298 301 this.changed = false;
302 + this.tokenBounds = null;
299 303 if(this.getToken().length>=this.options.minChars) {
300 - this.startIndicator();
301 304 this.getUpdatedChoices();
302 305 } else {
303 306 this.active = false;
304 307 this.hide();
305 308 }
309 + this.oldElementValue = this.element.value;
306 310 },
307 311
308 312 getToken: function() {
309 - var tokenPos = this.findLastToken();
310 - if (tokenPos != -1)
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;
313 + var bounds = this.getTokenBounds();
314 + return this.element.value.substring(bounds[0], bounds[1]).strip();
316 315 },
317 316
318 - findLastToken: function() {
319 - var lastTokenPos = -1;
317 + getTokenBounds: function() {
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++) {
322 - var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
323 - if (thisTokenPos > lastTokenPos)
324 - lastTokenPos = thisTokenPos;
325 - }
326 - return lastTokenPos;
327 - }
328 - }
335 + Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
336 + var boundary = Math.min(newS.length, oldS.length);
337 + for (var index = 0; index < boundary; ++index)
338 + if (newS[index] != oldS[index])
339 + return index;
340 + return boundary;
341 + };
329 342
330 - Ajax.Autocompleter = Class.create();
331 - Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
343 + Ajax.Autocompleter = Class.create(Autocompleter.Base, {
332 344 initialize: function(element, update, url, options) {
333 345 this.baseInitialize(element, update, options);
334 346 this.options.asynchronous = true;
@@ -338,7 +350,9
338 350 },
339 351
340 352 getUpdatedChoices: function() {
341 - entry = encodeURIComponent(this.options.paramName) + '=' +
353 + this.startIndicator();
354 +
355 + var entry = encodeURIComponent(this.options.paramName) + '=' +
342 356 encodeURIComponent(this.getToken());
343 357
344 358 this.options.parameters = this.options.callback ?
@@ -353,7 +367,6
353 367 onComplete: function(request) {
354 368 this.updateChoices(request.responseText);
355 369 }
356 -
357 370 });
358 371
359 372 // The local array autocompleter. Used when you'd prefer to
@@ -391,8 +404,7
391 404 // In that case, the other options above will not apply unless
392 405 // you support them.
393 406
394 - Autocompleter.Local = Class.create();
395 - Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
407 + Autocompleter.Local = Class.create(Autocompleter.Base, {
396 408 initialize: function(element, update, array, options) {
397 409 this.baseInitialize(element, update, options);
398 410 this.options.array = array;
@@ -452,9 +464,8
452 464 }
453 465 });
454 466
455 - // AJAX in-place editor
456 - //
457 - // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
467 + // AJAX in-place editor and collection editor
468 + // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
458 469
459 470 // Use this if you notice weird scrolling problems on some browsers,
460 471 // the DOM might be a bit confused when this gets called so do this
@@ -465,353 +476,472
465 476 }, 1);
466 477 }
467 478
468 - Ajax.InPlaceEditor = Class.create();
469 - Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470 - Ajax.InPlaceEditor.prototype = {
479 + Ajax.InPlaceEditor = Class.create({
471 480 initialize: function(element, url, options) {
472 481 this.url = url;
473 - this.element = $(element);
474 -
475 - this.options = Object.extend({
476 - paramName: "value",
477 - okButton: true,
478 - okText: "ok",
479 - cancelLink: true,
480 - cancelText: "cancel",
481 - savingText: "Saving...",
482 - clickToEditText: "Click to edit",
483 - okText: "ok",
484 - rows: 1,
485 - onComplete: function(transport, element) {
486 - new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
487 - },
488 - onFailure: function(transport) {
489 - alert("Error communicating with the server: " + transport.responseText.stripTags());
490 - },
491 - callback: function(form) {
492 - return Form.serialize(form);
482 + this.element = element = $(element);
483 + this.prepareOptions();
484 + this._controls = { };
485 + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
486 + Object.extend(this.options, options || { });
487 + if (!this.options.formId && this.element.id) {
488 + this.options.formId = this.element.id + '-inplaceeditor';
489 + if ($(this.options.formId))
490 + this.options.formId = '';
491 + }
492 + if (this.options.externalControl)
493 + this.options.externalControl = $(this.options.externalControl);
494 + if (!this.options.externalControl)
495 + this.options.externalControlOnly = false;
496 + this._originalBackground = this.element.getStyle('background-color') || 'transparent';
497 + this.element.title = this.options.clickToEditText;
498 + this._boundCancelHandler = this.handleFormCancellation.bind(this);
499 + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
500 + this._boundFailureHandler = this.handleAJAXFailure.bind(this);
501 + this._boundSubmitHandler = this.handleFormSubmission.bind(this);
502 + this._boundWrapperHandler = this.wrapUp.bind(this);
503 + this.registerListeners();
493 504 },
494 - handleLineBreaks: true,
495 - loadingText: 'Loading...',
496 - savingClassName: 'inplaceeditor-saving',
497 - loadingClassName: 'inplaceeditor-loading',
498 - formClassName: 'inplaceeditor-form',
499 - highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
500 - highlightendcolor: "#FFFFFF",
501 - externalControl: null,
502 - submitOnBlur: false,
503 - ajaxOptions: {},
504 - evalScripts: false
505 - }, options || {});
506 -
507 - if(!this.options.formId && this.element.id) {
508 - this.options.formId = this.element.id + "-inplaceeditor";
509 - if ($(this.options.formId)) {
510 - // there's already a form with that name, don't specify an id
511 - this.options.formId = null;
512 - }
513 - }
514 -
515 - if (this.options.externalControl) {
516 - this.options.externalControl = $(this.options.externalControl);
517 - }
518 -
519 - this.originalBackground = Element.getStyle(this.element, 'background-color');
520 - if (!this.originalBackground) {
521 - this.originalBackground = "transparent";
522 - }
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);
505 + checkForEscapeOrReturn: function(e) {
506 + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
507 + if (Event.KEY_ESC == e.keyCode)
508 + this.handleFormCancellation(e);
509 + else if (Event.KEY_RETURN == e.keyCode)
510 + this.handleFormSubmission(e);
511 + },
512 + createControl: function(mode, handler, extraClasses) {
513 + var control = this.options[mode + 'Control'];
514 + var text = this.options[mode + 'Text'];
515 + if ('button' == control) {
516 + var btn = document.createElement('input');
517 + btn.type = 'submit';
518 + btn.value = text;
519 + btn.className = 'editor_' + mode + '_button';
520 + if ('cancel' == mode)
521 + btn.onclick = this._boundCancelHandler;
522 + this._form.appendChild(btn);
523 + this._controls[mode] = btn;
524 + } else if ('link' == control) {
525 + var link = document.createElement('a');
526 + link.href = '#';
527 + link.appendChild(document.createTextNode(text));
528 + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
529 + link.className = 'editor_' + mode + '_link';
530 + if (extraClasses)
531 + link.className += ' ' + extraClasses;
532 + this._form.appendChild(link);
533 + this._controls[mode] = link;
536 534 }
537 535 },
538 - enterEditMode: function(evt) {
539 - if (this.saving) return;
540 - if (this.editing) return;
541 - this.editing = true;
542 - this.onEnterEditMode();
543 - if (this.options.externalControl) {
544 - Element.hide(this.options.externalControl);
536 + createEditField: function() {
537 + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
538 + var fld;
539 + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
540 + fld = document.createElement('input');
541 + fld.type = 'text';
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);
547 - this.createForm();
548 - this.element.parentNode.insertBefore(this.form, this.element);
549 - if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
550 - // stop the event to avoid a page refresh in Safari
551 - if (evt) {
552 - Event.stop(evt);
553 - }
554 - return false;
549 + fld.name = this.options.paramName;
550 + fld.value = text; // No HTML breaks conversion anymore
551 + fld.className = 'editor_field';
552 + if (this.options.submitOnBlur)
553 + fld.onblur = this._boundSubmitHandler;
554 + this._controls.editor = fld;
555 + if (this.options.loadTextURL)
556 + this.loadExternalText();
557 + this._form.appendChild(this._controls.editor);
555 558 },
556 559 createForm: function() {
557 - this.form = document.createElement("form");
558 - this.form.id = this.options.formId;
559 - Element.addClassName(this.form, this.options.formClassName)
560 - this.form.onsubmit = this.onSubmit.bind(this);
561 -
560 + var ipe = this;
561 + function addText(mode, condition) {
562 + var text = ipe.options['text' + mode + 'Controls'];
563 + if (!text || condition === false) return;
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 570 this.createEditField();
563 -
564 - if (this.options.textarea) {
565 - var br = document.createElement("br");
566 - this.form.appendChild(br);
567 - }
568 -
569 - if (this.options.okButton) {
570 - okButton = document.createElement("input");
571 - okButton.type = "submit";
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, "");
571 + if ('textarea' == this._controls.editor.tagName.toLowerCase())
572 + this._form.appendChild(document.createElement('br'));
573 + if (this.options.onFormCustomization)
574 + this.options.onFormCustomization(this, this._form);
575 + addText('Before', this.options.okControl || this.options.cancelControl);
576 + this.createControl('ok', this._boundSubmitHandler);
577 + addText('Between', this.options.okControl && this.options.cancelControl);
578 + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
579 + addText('After', this.options.okControl || this.options.cancelControl);
592 580 },
593 - createEditField: function() {
594 - var text;
595 - if(this.options.loadTextURL) {
596 - text = this.options.loadingText;
597 - } else {
598 - text = this.getText();
599 - }
600 -
601 - var obj = this;
602 -
603 - if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
604 - this.options.textarea = false;
605 - var textField = document.createElement("input");
606 - textField.obj = this;
607 - textField.type = "text";
608 - textField.name = this.options.paramName;
609 - textField.value = text;
610 - textField.style.backgroundColor = this.options.highlightcolor;
611 - textField.className = 'editor_field';
612 - var size = this.options.size || this.options.cols || 0;
613 - if (size != 0) textField.size = size;
614 - if (this.options.submitOnBlur)
615 - textField.onblur = this.onSubmit.bind(this);
616 - this.editField = textField;
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);
581 + destroy: function() {
582 + if (this._oldInnerHTML)
583 + this.element.innerHTML = this._oldInnerHTML;
584 + this.leaveEditMode();
585 + this.unregisterListeners();
586 + },
587 + enterEditMode: function(e) {
588 + if (this._saving || this._editing) return;
589 + this._editing = true;
590 + this.triggerCallback('onEnterEditMode');
591 + if (this.options.externalControl)
592 + this.options.externalControl.hide();
593 + this.element.hide();
594 + this.createForm();
595 + this.element.parentNode.insertBefore(this._form, this.element);
596 + if (!this.options.loadTextURL)
597 + this.postProcessEditField();
598 + if (e) Event.stop(e);
599 + },
600 + enterHover: function(e) {
601 + if (this.options.hoverClassName)
602 + this.element.addClassName(this.options.hoverClassName);
603 + if (this._saving) return;
604 + this.triggerCallback('onEnterHover');
635 605 },
636 606 getText: function() {
637 607 return this.element.innerHTML;
638 608 },
639 - loadExternalText: function() {
640 - Element.addClassName(this.form, this.options.loadingClassName);
641 - this.editField.disabled = true;
642 - new Ajax.Request(
643 - this.options.loadTextURL,
644 - Object.extend({
645 - asynchronous: true,
646 - onComplete: this.onLoadedExternalText.bind(this)
647 - }, this.options.ajaxOptions)
648 - );
609 + handleAJAXFailure: function(transport) {
610 + this.triggerCallback('onFailure', transport);
611 + if (this._oldInnerHTML) {
612 + this.element.innerHTML = this._oldInnerHTML;
613 + this._oldInnerHTML = null;
614 + }
615 + },
616 + handleFormCancellation: function(e) {
617 + this.wrapUp();
618 + if (e) Event.stop(e);
649 619 },
650 - onLoadedExternalText: function(transport) {
651 - Element.removeClassName(this.form, this.options.loadingClassName);
652 - this.editField.disabled = false;
653 - this.editField.value = transport.responseText.stripTags();
654 - Field.scrollFreeActivate(this.editField);
655 - },
656 - onclickCancel: function() {
657 - this.onComplete();
658 - this.leaveEditMode();
659 - return false;
660 - },
661 - onFailure: function(transport) {
662 - this.options.onFailure(transport);
663 - if (this.oldInnerHTML) {
664 - this.element.innerHTML = this.oldInnerHTML;
665 - this.oldInnerHTML = null;
620 + handleFormSubmission: function(e) {
621 + var form = this._form;
622 + var value = $F(this._controls.editor);
623 + this.prepareSubmission();
624 + var params = this.options.callback(form, value) || '';
625 + if (Object.isString(params))
626 + params = params.toQueryParams();
627 + params.editorId = this.element.id;
628 + if (this.options.htmlResponse) {
629 + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
630 + Object.extend(options, {
631 + parameters: params,
632 + onComplete: this._boundWrapperHandler,
633 + onFailure: this._boundFailureHandler
634 + });
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() {
670 - // onLoading resets these so we need to save them away for the Ajax call
671 - var form = this.form;
672 - var value = this.editField.value;
673 -
674 - // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
675 - // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
676 - // to be displayed indefinitely
677 - this.onLoading();
678 -
679 - if (this.options.evalScripts) {
680 - new Ajax.Request(
681 - this.url, Object.extend({
682 - parameters: this.options.callback(form, value),
683 - onComplete: this.onComplete.bind(this),
684 - onFailure: this.onFailure.bind(this),
685 - asynchronous:true,
686 - evalScripts:true
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;
647 + leaveEditMode: function() {
648 + this.element.removeClassName(this.options.savingClassName);
649 + this.removeForm();
650 + this.leaveHover();
651 + this.element.style.backgroundColor = this._originalBackground;
652 + this.element.show();
653 + if (this.options.externalControl)
654 + this.options.externalControl.show();
655 + this._saving = false;
656 + this._editing = false;
657 + this._oldInnerHTML = null;
658 + this.triggerCallback('onLeaveEditMode');
659 + },
660 + leaveHover: function(e) {
661 + if (this.options.hoverClassName)
662 + this.element.removeClassName(this.options.hoverClassName);
663 + if (this._saving) return;
664 + this.triggerCallback('onLeaveHover');
704 665 },
705 - onLoading: function() {
706 - this.saving = true;
666 + loadExternalText: function() {
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 700 this.removeForm();
708 701 this.leaveHover();
709 702 this.showSaving();
710 703 },
711 - showSaving: function() {
712 - this.oldInnerHTML = this.element.innerHTML;
713 - this.element.innerHTML = this.options.savingText;
714 - Element.addClassName(this.element, this.options.savingClassName);
715 - this.element.style.backgroundColor = this.originalBackground;
716 - Element.show(this.element);
704 + registerListeners: function() {
705 + this._listeners = { };
706 + var listener;
707 + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
708 + listener = this[pair.value].bind(this);
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 716 removeForm: function() {
719 - if(this.form) {
720 - if (this.form.parentNode) Element.remove(this.form);
721 - this.form = null;
717 + if (!this._form) return;
718 + this._form.remove();
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() {
725 - if (this.saving) return;
726 - this.element.style.backgroundColor = this.options.highlightcolor;
727 - if (this.effect) {
728 - this.effect.cancel();
734 + unregisterListeners: function() {
735 + $H(this._listeners).each(function(pair) {
736 + if (!this.options.externalControlOnly)
737 + this.element.stopObserving(pair.key, pair.value);
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() {
733 - if (this.options.backgroundColor) {
734 - this.element.style.backgroundColor = this.oldBackground;
735 - }
736 - Element.removeClassName(this.element, this.options.hoverClassName)
737 - if (this.saving) return;
738 - this.effect = new Effect.Highlight(this.element, {
739 - startcolor: this.options.highlightcolor,
740 - endcolor: this.options.highlightendcolor,
741 - restorecolor: this.originalBackground
759 +
760 + createEditField: function() {
761 + var list = document.createElement('select');
762 + list.name = this.options.paramName;
763 + list.size = 1;
764 + this._controls.editor = list;
765 + this._collection = this.options.collection || [];
766 + if (this.options.loadCollectionURL)
767 + this.loadCollection();
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() {
745 - Element.removeClassName(this.element, this.options.savingClassName);
746 - this.removeForm();
747 - this.leaveHover();
748 - this.element.style.backgroundColor = this.originalBackground;
749 - Element.show(this.element);
750 - if (this.options.externalControl) {
751 - Element.show(this.options.externalControl);
791 +
792 + showLoadingText: function(text) {
793 + this._controls.editor.disabled = true;
794 + var tempOption = this._controls.editor.firstChild;
795 + if (!tempOption) {
796 + tempOption = document.createElement('option');
797 + tempOption.value = '';
798 + this._controls.editor.appendChild(tempOption);
799 + tempOption.selected = true;
752 800 }
753 - this.editing = false;
754 - this.saving = false;
755 - this.oldInnerHTML = null;
756 - this.onLeaveEditMode();
801 + tempOption.update((text || '').stripScripts().stripTags());
802 + },
803 +
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) {
759 - this.leaveEditMode();
760 - this.options.onComplete.bind(this)(transport, this.element);
761 - },
762 - onEnterEditMode: function() {},
763 - onLeaveEditMode: function() {},
764 - dispose: function() {
765 - if (this.oldInnerHTML) {
766 - this.element.innerHTML = this.oldInnerHTML;
826 +
827 + buildOptionList: function() {
828 + this._form.removeClassName(this.options.loadingClassName);
829 + this._collection = this._collection.map(function(entry) {
830 + return 2 === entry.length ? entry : [entry, entry].flatten();
831 + });
832 + var marker = ('value' in this.options) ? this.options.value : this._text;
833 + var textFound = this._collection.any(function(entry) {
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();
769 - Event.stopObserving(this.element, 'click', this.onclickListener);
770 - Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
771 - Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
772 - if (this.options.externalControl) {
773 - Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
774 - Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
775 - Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
776 - }
777 - }
848 + });
849 +
850 + //**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
851 + //**** This only exists for a while, in order to let ****
852 + //**** users adapt to the new API. Read up on the new ****
853 + //**** API and convert your code to it ASAP! ****
854 +
855 + Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
856 + if (!options) return;
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();
781 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
782 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
783 - createEditField: function() {
784 - if (!this.cached_selectTag) {
785 - var selectTag = document.createElement("select");
786 - var collection = this.options.collection || [];
787 - var optionTag;
788 - collection.each(function(e,i) {
789 - optionTag = document.createElement("option");
790 - optionTag.value = (e instanceof Array) ? e[0] : e;
791 - if((typeof this.options.value == 'undefined') &&
792 - ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
793 - if(this.options.value==optionTag.value) optionTag.selected = true;
794 - optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
795 - selectTag.appendChild(optionTag);
796 - }.bind(this));
797 - this.cached_selectTag = selectTag;
869 + Object.extend(Ajax.InPlaceEditor, {
870 + DefaultOptions: {
871 + ajaxOptions: { },
872 + autoRows: 3, // Use when multi-line w/ rows == 1
873 + cancelControl: 'link', // 'link'|'button'|false
874 + cancelText: 'cancel',
875 + clickToEditText: 'Click to edit',
876 + externalControl: null, // id|elt
877 + externalControlOnly: false,
878 + fieldPostCreation: 'activate', // 'activate'|'focus'|false
879 + formClassName: 'inplaceeditor-form',
880 + formId: null, // id|elt
881 + highlightColor: '#ffff99',
882 + highlightEndColor: '#ffffff',
883 + hoverClassName: '',
884 + htmlResponse: true,
885 + loadingClassName: 'inplaceeditor-loading',
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 -
800 - this.editField = this.cached_selectTag;
801 - if(this.options.loadTextURL) this.loadExternalText();
802 - this.form.appendChild(this.editField);
803 - this.options.callback = function(form, value) {
804 - return "value=" + encodeURIComponent(value);
805 - }
927 + },
928 + Listeners: {
929 + click: 'enterEditMode',
930 + keydown: 'checkForEscapeOrReturn',
931 + mouseover: 'enterHover',
932 + mouseout: 'leaveHover'
806 933 }
807 934 });
808 935
936 + Ajax.InPlaceCollectionEditor.DefaultOptions = {
937 + loadingCollectionText: 'Loading options...'
938 + };
939 +
809 940 // Delayed observer, like Form.Element.Observer,
810 941 // but waits for delay after last key input
811 942 // Ideal for live-search fields
812 943
813 - Form.Element.DelayedObserver = Class.create();
814 - Form.Element.DelayedObserver.prototype = {
944 + Form.Element.DelayedObserver = Class.create({
815 945 initialize: function(element, delay, callback) {
816 946 this.delay = delay || 0.5;
817 947 this.element = $(element);
@@ -830,4 +960,4
830 960 this.timer = null;
831 961 this.callback(this.element, $F(this.element));
832 962 }
833 - };
963 + });
@@ -1,10 +1,10
1 - // Copyright (c) 2005, 2006 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)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 + // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
3 3 //
4 4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
5 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 8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
9 9
10 10 var Droppables = {
@@ -26,8 +26,7
26 26 if(options.containment) {
27 27 options._containers = [];
28 28 var containment = options.containment;
29 - if((typeof containment == 'object') &&
30 - (containment.constructor == Array)) {
29 + if(Object.isArray(containment)) {
31 30 containment.each( function(c) { options._containers.push($(c)) });
32 31 } else {
33 32 options._containers.push($(containment));
@@ -87,21 +86,23
87 86
88 87 show: function(point, element) {
89 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 91 this.drops.each( function(drop) {
94 92 if(Droppables.isAffected(point, element, drop))
95 93 affected.push(drop);
96 94 });
97 95
98 - if(affected.length>0) {
96 + if(affected.length>0)
99 97 drop = Droppables.findDeepestChild(affected);
98 +
99 + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
100 + if (drop) {
100 101 Position.within(drop.element, point[0], point[1]);
101 102 if(drop.onHover)
102 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
@@ -110,8 +111,10
110 111 Position.prepare();
111 112
112 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 115 this.last_active.onDrop(element, this.last_active.element, event);
116 + return true;
117 + }
115 118 },
116 119
117 120 reset: function() {
@@ -219,10 +222,7
219 222
220 223 /*--------------------------------------------------------------------------*/
221 224
222 - var Draggable = Class.create();
223 - Draggable._dragging = {};
224 -
225 - Draggable.prototype = {
225 + var Draggable = Class.create({
226 226 initialize: function(element) {
227 227 var defaults = {
228 228 handle: false,
@@ -233,7 +233,7
233 233 });
234 234 },
235 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 237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238 238 queue: {scope:'_draggable', position:'end'},
239 239 afterFinish: function(){
@@ -243,6 +243,7
243 243 },
244 244 zindex: 1000,
245 245 revert: false,
246 + quiet: false,
246 247 scroll: false,
247 248 scrollSensitivity: 20,
248 249 scrollSpeed: 15,
@@ -250,7 +251,7
250 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 255 Object.extend(defaults, {
255 256 starteffect: function(element) {
256 257 element._opacity = Element.getOpacity(element);
@@ -263,7 +264,7
263 264
264 265 this.element = $(element);
265 266
266 - if(options.handle && (typeof options.handle == 'string'))
267 + if(options.handle && Object.isString(options.handle))
267 268 this.handle = this.element.down('.'+options.handle, 0);
268 269
269 270 if(!this.handle) this.handle = $(options.handle);
@@ -276,7 +277,6
276 277
277 278 Element.makePositioned(this.element); // fix IE
278 279
279 - this.delta = this.currentDelta();
280 280 this.options = options;
281 281 this.dragging = false;
282 282
@@ -298,17 +298,17
298 298 },
299 299
300 300 initDrag: function(event) {
301 - if(typeof Draggable._dragging[this.element] != 'undefined' &&
301 + if(!Object.isUndefined(Draggable._dragging[this.element]) &&
302 302 Draggable._dragging[this.element]) return;
303 303 if(Event.isLeftClick(event)) {
304 304 // abort on form elements, fixes a Firefox issue
305 305 var src = Event.element(event);
306 - if(src.tagName && (
307 - src.tagName=='INPUT' ||
308 - src.tagName=='SELECT' ||
309 - src.tagName=='OPTION' ||
310 - src.tagName=='BUTTON' ||
311 - src.tagName=='TEXTAREA')) return;
306 + if((tag_name = src.tagName.toUpperCase()) && (
307 + tag_name=='INPUT' ||
308 + tag_name=='SELECT' ||
309 + tag_name=='OPTION' ||
310 + tag_name=='BUTTON' ||
311 + tag_name=='TEXTAREA')) return;
312 312
313 313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
314 314 var pos = Position.cumulativeOffset(this.element);
@@ -321,6 +321,8
321 321
322 322 startDrag: function(event) {
323 323 this.dragging = true;
324 + if(!this.delta)
325 + this.delta = this.currentDelta();
324 326
325 327 if(this.options.zindex) {
326 328 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
@@ -329,6 +331,8
329 331
330 332 if(this.options.ghosting) {
331 333 this._clone = this.element.cloneNode(true);
334 + this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
335 + if (!this.element._originallyAbsolute)
332 336 Position.absolutize(this.element);
333 337 this.element.parentNode.insertBefore(this._clone, this.element);
334 338 }
@@ -351,8 +355,12
351 355
352 356 updateDrag: function(event, pointer) {
353 357 if(!this.dragging) this.startDrag(event);
358 +
359 + if(!this.options.quiet){
354 360 Position.prepare();
355 361 Droppables.show(pointer, this.element);
362 + }
363 +
356 364 Draggables.notify('onDrag', this, event);
357 365
358 366 this.draw(pointer);
@@ -380,7 +388,7
380 388 }
381 389
382 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 393 Event.stop(event);
386 394 },
@@ -388,20 +396,34
388 396 finishDrag: function(event, success) {
389 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 405 if(this.options.ghosting) {
406 + if (!this.element._originallyAbsolute)
392 407 Position.relativize(this.element);
408 + delete this.element._originallyAbsolute;
393 409 Element.remove(this._clone);
394 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 419 Draggables.notify('onEnd', this, event);
399 420
400 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 424 var d = this.currentDelta();
404 425 if(revert && this.options.reverteffect) {
426 + if (dropped == 0 || revert != 'failure')
405 427 this.options.reverteffect(this.element,
406 428 d[1]-this.delta[1], d[0]-this.delta[0]);
407 429 } else {
@@ -451,15 +473,15
451 473 }.bind(this));
452 474
453 475 if(this.options.snap) {
454 - if(typeof this.options.snap == 'function') {
476 + if(Object.isFunction(this.options.snap)) {
455 477 p = this.options.snap(p[0],p[1],this);
456 478 } else {
457 - if(this.options.snap instanceof Array) {
479 + if(Object.isArray(this.options.snap)) {
458 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 482 } else {
461 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
@@ -543,12 +565,13
543 565 }
544 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();
551 - SortableObserver.prototype = {
574 + var SortableObserver = Class.create({
552 575 initialize: function(element, observer) {
553 576 this.element = $(element);
554 577 this.observer = observer;
@@ -564,7 +587,7
564 587 if(this.lastValue != Sortable.serialize(this.element))
565 588 this.observer(this.element)
566 589 }
567 - }
590 + });
568 591
569 592 var Sortable = {
570 593 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
@@ -572,7 +595,7
572 595 sortables: {},
573 596
574 597 _findRootElement: function(element) {
575 - while (element.tagName != "BODY") {
598 + while (element.tagName.toUpperCase() != "BODY") {
576 599 if(element.id && Sortable.sortables[element.id]) return element;
577 600 element = element.parentNode;
578 601 }
@@ -612,10 +635,17
612 635 delay: 0,
613 636 hoverclass: null,
614 637 ghosting: false,
638 + quiet: false,
615 639 scroll: false,
616 640 scrollSensitivity: 20,
617 641 scrollSpeed: 15,
618 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 649 onChange: Prototype.emptyFunction,
620 650 onUpdate: Prototype.emptyFunction
621 651 }, arguments[1] || {});
@@ -626,6 +656,7
626 656 // build options for the draggables
627 657 var options_for_draggable = {
628 658 revert: true,
659 + quiet: options.quiet,
629 660 scroll: options.scroll,
630 661 scrollSpeed: options.scrollSpeed,
631 662 scrollSensitivity: options.scrollSensitivity,
@@ -679,10 +710,9
679 710 options.droppables.push(element);
680 711 }
681 712
682 - (this.findElements(element, options) || []).each( function(e) {
683 - // handles are per-draggable
684 - var handle = options.handle ?
685 - $(e).down('.'+options.handle,0) : e;
713 + (options.elements || this.findElements(element, options) || []).each( function(e,i) {
714 + var handle = options.handles ? $(options.handles[i]) :
715 + (options.handle ? $(e).select('.' + options.handle)[0] : e);
686 716 options.draggables.push(
687 717 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
688 718 Droppables.add(e, options_for_droppable);
This diff has been collapsed as it changes many lines, (596 lines changed) Show them Hide them
@@ -1,4 +1,4
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 2 // Contributors:
3 3 // Justin Palmer (http://encytemedia.com/)
4 4 // Mark Pilgrim (http://diveintomark.org/)
@@ -21,7 +21,7
21 21 }
22 22 }
23 23 return(color.length==7 ? color : (arguments[0] || this));
24 - }
24 + };
25 25
26 26 /*--------------------------------------------------------------------------*/
27 27
@@ -30,7 +30,7
30 30 return (node.nodeType==3 ? node.nodeValue :
31 31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
32 32 }).flatten().join('');
33 - }
33 + };
34 34
35 35 Element.collectTextNodesIgnoreClass = function(element, className) {
36 36 return $A($(element).childNodes).collect( function(node) {
@@ -38,47 +38,18
38 38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
39 39 Element.collectTextNodesIgnoreClass(node, className) : ''));
40 40 }).flatten().join('');
41 - }
41 + };
42 42
43 43 Element.setContentZoom = function(element, percent) {
44 44 element = $(element);
45 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 47 return element;
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 - }
48 + };
78 49
79 50 Element.getInlineOpacity = function(element){
80 51 return $(element).style.opacity || '';
81 - }
52 + };
82 53
83 54 Element.forceRerendering = function(element) {
84 55 try {
@@ -91,31 +62,63
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 65 var Effect = {
102 66 _elementDoesNotExistError: {
103 67 name: 'ElementDoesNotExistError',
104 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 112 tagifyText: function(element) {
107 - if(typeof Builder == 'undefined')
108 - throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109 -
110 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 116 element = $(element);
114 117 $A(element.childNodes).each( function(child) {
115 118 if(child.nodeType==3) {
116 119 child.nodeValue.toArray().each( function(character) {
117 120 element.insertBefore(
118 - Builder.node('span',{style: tagifyStyle},
121 + new Element('span', {style: tagifyStyle}).update(
119 122 character == ' ' ? String.fromCharCode(160) : character),
120 123 child);
121 124 });
@@ -126,7 +129,7
126 129 multiple: function(element, effect) {
127 130 var elements;
128 131 if(((typeof element == 'object') ||
129 - (typeof element == 'function')) &&
132 + Object.isFunction(element)) &&
130 133 (element.length))
131 134 elements = element;
132 135 else
@@ -158,44 +161,11
158 161 }
159 162 };
160 163
161 - var Effect2 = Effect; // deprecated
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 - };
164 + Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
194 165
195 166 /* ------------- core effects ------------- */
196 167
197 - Effect.ScopedQueue = Class.create();
198 - Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
168 + Effect.ScopedQueue = Class.create(Enumerable, {
199 169 initialize: function() {
200 170 this.effects = [];
201 171 this.interval = null;
@@ -206,7 +176,7
206 176 add: function(effect) {
207 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 180 effect.options.queue : effect.options.queue.position;
211 181
212 182 switch(position) {
@@ -233,7 +203,7
233 203 this.effects.push(effect);
234 204
235 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 208 remove: function(effect) {
239 209 this.effects = this.effects.reject(function(e) { return e==effect });
@@ -244,46 +214,57
244 214 },
245 215 loop: function() {
246 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 222 Effect.Queues = {
252 223 instances: $H(),
253 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])
257 - this.instances[queueName] = new Effect.ScopedQueue();
258 -
259 - return this.instances[queueName];
227 + return this.instances.get(queueName) ||
228 + this.instances.set(queueName, new Effect.ScopedQueue());
260 229 }
261 - }
230 + };
262 231 Effect.Queue = Effect.Queues.get('global');
263 232
264 - Effect.DefaultOptions = {
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 = {
233 + Effect.Base = Class.create({
277 234 position: null,
278 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 243 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
280 244 this.currentFrame = 0;
281 245 this.state = 'idle';
282 246 this.startOn = this.options.delay*1000;
283 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 265 this.event('beforeStart');
285 266 if(!this.options.sync)
286 - Effect.Queues.get(typeof this.options.queue == 'string' ?
267 + Effect.Queues.get(Object.isString(this.options.queue) ?
287 268 'global' : this.options.queue.scope).add(this);
288 269 },
289 270 loop: function(timePos) {
@@ -296,34 +277,17
296 277 this.event('afterFinish');
297 278 return;
298 279 }
299 - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
300 - var frame = Math.round(pos * this.options.fps * this.options.duration);
280 + var pos = (timePos - this.startOn) / this.totalTime,
281 + frame = (pos * this.totalFrames).round();
301 282 if(frame > this.currentFrame) {
302 283 this.render(pos);
303 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 288 cancel: function() {
325 289 if(!this.options.sync)
326 - Effect.Queues.get(typeof this.options.queue == 'string' ?
290 + Effect.Queues.get(Object.isString(this.options.queue) ?
327 291 'global' : this.options.queue.scope).remove(this);
328 292 this.state = 'finished';
329 293 },
@@ -332,12 +296,14
332 296 if(this.options[eventName]) this.options[eventName](this);
333 297 },
334 298 inspect: function() {
335 - return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
336 - }
299 + var data = $H();
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();
340 - Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
306 + Effect.Parallel = Class.create(Effect.Base, {
341 307 initialize: function(effects) {
342 308 this.effects = effects || [];
343 309 this.start(arguments[1]);
@@ -356,24 +322,34
356 322 }
357 323 });
358 324
359 - Effect.Event = Class.create();
360 - Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
325 + Effect.Tween = Class.create(Effect.Base, {
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 341 initialize: function() {
362 - var options = Object.extend({
363 - duration: 0
364 - }, arguments[0] || {});
365 - this.start(options);
342 + this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
366 343 },
367 344 update: Prototype.emptyFunction
368 345 });
369 346
370 - Effect.Opacity = Class.create();
371 - Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
347 + Effect.Opacity = Class.create(Effect.Base, {
372 348 initialize: function(element) {
373 349 this.element = $(element);
374 350 if(!this.element) throw(Effect._elementDoesNotExistError);
375 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 353 this.element.setStyle({zoom: 1});
378 354 var options = Object.extend({
379 355 from: this.element.getOpacity() || 0.0,
@@ -386,8 +362,7
386 362 }
387 363 });
388 364
389 - Effect.Move = Class.create();
390 - Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
365 + Effect.Move = Class.create(Effect.Base, {
391 366 initialize: function(element) {
392 367 this.element = $(element);
393 368 if(!this.element) throw(Effect._elementDoesNotExistError);
@@ -399,23 +374,18
399 374 this.start(options);
400 375 },
401 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 377 this.element.makePositioned();
407 378 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
408 379 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
409 380 if(this.options.mode == 'absolute') {
410 - // absolute movement, so we need to calc deltaX and deltaY
411 381 this.options.x = this.options.x - this.originalLeft;
412 382 this.options.y = this.options.y - this.originalTop;
413 383 }
414 384 },
415 385 update: function(position) {
416 386 this.element.setStyle({
417 - left: Math.round(this.options.x * position + this.originalLeft) + 'px',
418 - top: Math.round(this.options.y * position + this.originalTop) + 'px'
387 + left: (this.options.x * position + this.originalLeft).round() + 'px',
388 + top: (this.options.y * position + this.originalTop).round() + 'px'
419 389 });
420 390 }
421 391 });
@@ -426,8 +396,7
426 396 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
427 397 };
428 398
429 - Effect.Scale = Class.create();
430 - Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
399 + Effect.Scale = Class.create(Effect.Base, {
431 400 initialize: function(element, percent) {
432 401 this.element = $(element);
433 402 if(!this.element) throw(Effect._elementDoesNotExistError);
@@ -484,8 +453,8
484 453 },
485 454 setDimensions: function(height, width) {
486 455 var d = {};
487 - if(this.options.scaleX) d.width = Math.round(width) + 'px';
488 - if(this.options.scaleY) d.height = Math.round(height) + 'px';
456 + if (this.options.scaleX) d.width = width.round() + 'px';
457 + if (this.options.scaleY) d.height = height.round() + 'px';
489 458 if(this.options.scaleFromCenter) {
490 459 var topd = (height - this.dims[0])/2;
491 460 var leftd = (width - this.dims[1])/2;
@@ -501,8 +470,7
501 470 }
502 471 });
503 472
504 - Effect.Highlight = Class.create();
505 - Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
473 + Effect.Highlight = Class.create(Effect.Base, {
506 474 initialize: function(element) {
507 475 this.element = $(element);
508 476 if(!this.element) throw(Effect._elementDoesNotExistError);
@@ -513,9 +481,11
513 481 // Prevent executing on elements not in the layout flow
514 482 if(this.element.getStyle('display')=='none') { this.cancel(); return; }
515 483 // Disable background image during the effect
516 - this.oldStyle = {
517 - backgroundImage: this.element.getStyle('background-image') };
484 + this.oldStyle = { };
485 + if (!this.options.keepBackgroundImage) {
486 + this.oldStyle.backgroundImage = this.element.getStyle('background-image');
518 487 this.element.setStyle({backgroundImage: 'none'});
488 + }
519 489 if(!this.options.endcolor)
520 490 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
521 491 if(!this.options.restorecolor)
@@ -526,7 +496,7
526 496 },
527 497 update: function(position) {
528 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 501 finish: function() {
532 502 this.element.setStyle(Object.extend(this.oldStyle, {
@@ -535,30 +505,21
535 505 }
536 506 });
537 507
538 - Effect.ScrollTo = Class.create();
539 - Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
540 - initialize: function(element) {
541 - this.element = $(element);
542 - this.start(arguments[1] || {});
543 - },
544 - setup: function() {
545 - Position.prepare();
546 - var offsets = Position.cumulativeOffset(this.element);
547 - if(this.options.offset) offsets[1] += this.options.offset;
548 - var max = window.innerHeight ?
549 - window.height - window.innerHeight :
550 - document.body.scrollHeight -
551 - (document.documentElement.clientHeight ?
552 - document.documentElement.clientHeight : document.body.clientHeight);
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 - });
508 + Effect.ScrollTo = function(element) {
509 + var options = arguments[1] || { },
510 + scrollOffsets = document.viewport.getScrollOffsets(),
511 + elementOffsets = $(element).cumulativeOffset(),
512 + max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
513 +
514 + if (options.offset) elementOffsets[1] += options.offset;
515 +
516 + return new Effect.Tween(null,
517 + scrollOffsets.top,
518 + elementOffsets[1] > max ? max : elementOffsets[1],
519 + options,
520 + function(p){ scrollTo(scrollOffsets.left, p.round()) }
521 + );
522 + };
562 523
563 524 /* ------------- combination effects ------------- */
564 525
@@ -571,9 +532,10
571 532 afterFinishInternal: function(effect) {
572 533 if(effect.options.to!=0) return;
573 534 effect.element.hide().setStyle({opacity: oldOpacity});
574 - }}, arguments[1] || {});
535 + }
536 + }, arguments[1] || { });
575 537 return new Effect.Opacity(element,options);
576 - }
538 + };
577 539
578 540 Effect.Appear = function(element) {
579 541 element = $(element);
@@ -588,7 +550,7
588 550 effect.element.setOpacity(effect.options.from).show();
589 551 }}, arguments[1] || {});
590 552 return new Effect.Opacity(element,options);
591 - }
553 + };
592 554
593 555 Effect.Puff = function(element) {
594 556 element = $(element);
@@ -612,7 +574,7
612 574 effect.effects[0].element.hide().setStyle(oldStyle); }
613 575 }, arguments[1] || {})
614 576 );
615 - }
577 + };
616 578
617 579 Effect.BlindUp = function(element) {
618 580 element = $(element);
@@ -626,7 +588,7
626 588 }
627 589 }, arguments[1] || {})
628 590 );
629 - }
591 + };
630 592
631 593 Effect.BlindDown = function(element) {
632 594 element = $(element);
@@ -644,7 +606,7
644 606 effect.element.undoClipping();
645 607 }
646 608 }, arguments[1] || {}));
647 - }
609 + };
648 610
649 611 Effect.SwitchOff = function(element) {
650 612 element = $(element);
@@ -666,7 +628,7
666 628 })
667 629 }
668 630 }, arguments[1] || {}));
669 - }
631 + };
670 632
671 633 Effect.DropOut = function(element) {
672 634 element = $(element);
@@ -686,28 +648,34
686 648 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
687 649 }
688 650 }, arguments[1] || {}));
689 - }
651 + };
690 652
691 653 Effect.Shake = function(element) {
692 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 661 var oldStyle = {
694 662 top: element.getStyle('top'),
695 663 left: element.getStyle('left') };
696 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 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 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 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 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 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 676 effect.element.undoPositioned().setStyle(oldStyle);
709 677 }}) }}) }}) }}) }}) }});
710 - }
678 + };
711 679
712 680 Effect.SlideDown = function(element) {
713 681 element = $(element).cleanWhitespace();
@@ -735,18 +703,20
735 703 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
736 704 }, arguments[1] || {})
737 705 );
738 - }
706 + };
739 707
740 708 Effect.SlideUp = function(element) {
741 709 element = $(element).cleanWhitespace();
742 710 var oldInnerBottom = element.down().getStyle('bottom');
711 + var elementDimensions = element.getDimensions();
743 712 return new Effect.Scale(element, window.opera ? 0 : 1,
744 713 Object.extend({ scaleContent: false,
745 714 scaleX: false,
746 715 scaleMode: 'box',
747 716 scaleFrom: 100,
717 + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
748 718 restoreAfterFinish: true,
749 - beforeStartInternal: function(effect) {
719 + afterSetup: function(effect) {
750 720 effect.element.makePositioned();
751 721 effect.element.down().makePositioned();
752 722 if(window.opera) effect.element.setStyle({top: ''});
@@ -757,12 +727,12
757 727 (effect.dims[0] - effect.element.clientHeight) + 'px' });
758 728 },
759 729 afterFinishInternal: function(effect) {
760 - effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
761 - effect.element.down().undoPositioned();
730 + effect.element.hide().undoClipping().undoPositioned();
731 + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
762 732 }
763 733 }, arguments[1] || {})
764 734 );
765 - }
735 + };
766 736
767 737 // Bug in opera makes the TD containing this element expand for a instance after finish
768 738 Effect.Squish = function(element) {
@@ -775,7 +745,7
775 745 effect.element.hide().undoClipping();
776 746 }
777 747 });
778 - }
748 + };
779 749
780 750 Effect.Grow = function(element) {
781 751 element = $(element);
@@ -849,7 +819,7
849 819 )
850 820 }
851 821 });
852 - }
822 + };
853 823
854 824 Effect.Shrink = function(element) {
855 825 element = $(element);
@@ -903,7 +873,7
903 873 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
904 874 }, options)
905 875 );
906 - }
876 + };
907 877
908 878 Effect.Pulsate = function(element) {
909 879 element = $(element);
@@ -916,7 +886,7
916 886 Object.extend(Object.extend({ duration: 2.0, from: 0,
917 887 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
918 888 }, options), {transition: reverser}));
919 - }
889 + };
920 890
921 891 Effect.Fold = function(element) {
922 892 element = $(element);
@@ -939,16 +909,37
939 909 }}, arguments[1] || {}));
940 910 };
941 911
942 - Effect.Morph = Class.create();
943 - Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
912 + Effect.Morph = Class.create(Effect.Base, {
944 913 initialize: function(element) {
945 914 this.element = $(element);
946 915 if(!this.element) throw(Effect._elementDoesNotExistError);
947 916 var options = Object.extend({
948 - style: ''
917 + style: { }
949 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 940 this.start(options);
951 941 },
942 +
952 943 setup: function(){
953 944 function parseColor(color){
954 945 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
@@ -957,16 +948,29
957 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){
961 - var originalValue = this.element.getStyle(property[0]);
962 - return $H({
963 - style: property[0],
964 - originalValue: property[1].unit=='color' ?
965 - parseColor(originalValue) : parseFloat(originalValue || 0),
966 - targetValue: property[1].unit=='color' ?
967 - parseColor(property[1].value) : property[1].value,
968 - unit: property[1].unit
969 - });
951 + this.transforms = this.style.map(function(pair){
952 + var property = pair[0], value = pair[1], unit = null;
953 +
954 + if (value.parseColor('#zzzzzz') != '#zzzzzz') {
955 + value = value.parseColor();
956 + unit = 'color';
957 + } else if (property == 'opacity') {
958 + value = parseFloat(value);
959 + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
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 974 }.bind(this)).reject(function(transform){
971 975 return (
972 976 (transform.originalValue == transform.targetValue) ||
@@ -978,22 +982,24
978 982 });
979 983 },
980 984 update: function(position) {
981 - var style = $H(), value = null;
982 - this.transforms.each(function(transform){
983 - value = transform.unit=='color' ?
984 - $R(0,2).inject('#',function(m,v,i){
985 - return m+(Math.round(transform.originalValue[i]+
986 - (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
987 - transform.originalValue + Math.round(
988 - ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
989 - style[transform.style] = value;
990 - });
991 - this.element.setStyle(style);
985 + var style = { }, transform, i = this.transforms.length;
986 + while(i--)
987 + style[(transform = this.transforms[i]).style] =
988 + transform.unit=='color' ? '#'+
989 + (Math.round(transform.originalValue[0]+
990 + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
991 + (Math.round(transform.originalValue[1]+
992 + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
993 + (Math.round(transform.originalValue[2]+
994 + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
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();
996 - Object.extend(Effect.Transform.prototype, {
1002 + Effect.Transform = Class.create({
997 1003 initialize: function(tracks){
998 1004 this.tracks = [];
999 1005 this.options = arguments[1] || {};
@@ -1001,9 +1007,10
1001 1007 },
1002 1008 addTracks: function(tracks){
1003 1009 tracks.each(function(track){
1004 - var data = $H(track).values().first();
1010 + track = $H(track);
1011 + var data = track.values().first();
1005 1012 this.tracks.push($H({
1006 - ids: $H(track).keys().first(),
1013 + ids: track.keys().first(),
1007 1014 effect: Effect.Morph,
1008 1015 options: { style: data }
1009 1016 }));
@@ -1013,76 +1020,101
1013 1020 play: function(){
1014 1021 return new Effect.Parallel(
1015 1022 this.tracks.map(function(track){
1016 - var elements = [$(track.ids) || $$(track.ids)].flatten();
1017 - return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1023 + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1024 + var elements = [$(ids) || $$(ids)].flatten();
1025 + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1018 1026 }).flatten(),
1019 1027 this.options
1020 1028 );
1021 1029 }
1022 1030 });
1023 1031
1024 - Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1025 - 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1026 - 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1027 - 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1028 - 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1029 - 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1030 - 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1031 - 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1032 - 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1033 - 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
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'];
1032 + Element.CSS_PROPERTIES = $w(
1033 + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1034 + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1035 + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1036 + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1037 + 'fontSize fontWeight height left letterSpacing lineHeight ' +
1038 + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1039 + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1040 + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1041 + 'right textIndent top width wordSpacing zIndex');
1041 1042
1042 1043 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043 1044
1045 + String.__parseStyleElement = document.createElement('div');
1044 1046 String.prototype.parseStyle = function(){
1045 - var element = Element.extend(document.createElement('div'));
1046 - element.innerHTML = '<div style="' + this + '"></div>';
1047 - var style = element.down().style, styleRules = $H();
1047 + var style, styleRules = $H();
1048 + if (Prototype.Browser.WebKit)
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 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){
1056 - var property = pair[0], value = pair[1], unit = null;
1062 + return styleRules;
1063 + };
1057 1064
1058 - if(value.parseColor('#zzzzzz') != '#zzzzzz') {
1059 - value = value.parseColor();
1060 - unit = 'color';
1061 - } else if(Element.CSS_LENGTH.test(value))
1062 - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
1063 - value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
1064 -
1065 - result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1066 - }.bind(this));
1067 -
1068 - return result;
1065 + if (document.defaultView && document.defaultView.getComputedStyle) {
1066 + Element.getStyles = function(element) {
1067 + var css = document.defaultView.getComputedStyle($(element), null);
1068 + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1069 + styles[property] = css[property];
1070 + return styles;
1071 + });
1072 + };
1073 + } else {
1074 + Element.getStyles = function(element) {
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 1089 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1073 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',
1077 - 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1078 - function(f) { Element.Methods[f] = Element[f]; }
1105 + $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1106 + 'pulsate shake puff squish switchOff dropOut').each(
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) {
1082 - s = effect.gsub(/_/, '-').camelize();
1083 - effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1084 - new Effect[effect_class](element, options);
1085 - return $(element);
1086 - };
1116 + $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1117 + function(f) { Effect.Methods[f] = Element[f]; }
1118 + );
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,43 +1,114
1 - /* Prototype JavaScript framework, version 1.5.0
1 + /* Prototype JavaScript framework, version 1.6.0.1
2 2 * (c) 2005-2007 Sam Stephenson
3 3 *
4 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 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 20 BrowserFeatures: {
12 - XPath: !!document.evaluate
13 - },
14 -
15 - ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
21 + XPath: !!document.evaluate,
22 + ElementExtensions: !!window.HTMLElement,
23 + SpecificElementExtensions:
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 32 emptyFunction: function() {},
17 33 K: function(x) { return x }
18 - }
19 -
34 + };
35 +
36 + if (Prototype.Browser.MobileSafari)
37 + Prototype.BrowserFeatures.SpecificElementExtensions = false;
38 +
39 +
40 + /* Based on Alex Arnell's inheritance implementation. */
20 41 var Class = {
21 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 48 this.initialize.apply(this, arguments);
24 49 }
25 - }
26 - }
27 -
28 - var Abstract = new Object();
50 +
51 + Object.extend(klass, Class.Methods);
52 + klass.superclass = parent;
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 102 Object.extend = function(destination, source) {
31 - for (var property in source) {
103 + for (var property in source)
32 104 destination[property] = source[property];
33 - }
34 105 return destination;
35 - }
106 + };
36 107
37 108 Object.extend(Object, {
38 109 inspect: function(object) {
39 110 try {
40 - if (object === undefined) return 'undefined';
111 + if (Object.isUndefined(object)) return 'undefined';
41 112 if (object === null) return 'null';
42 113 return object.inspect ? object.inspect() : object.toString();
43 114 } catch (e) {
@@ -46,6 +117,37
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 151 keys: function(object) {
50 152 var keys = [];
51 153 for (var property in object)
@@ -62,40 +164,100
62 164
63 165 clone: function(object) {
64 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 206 var __method = this, args = $A(arguments), object = args.shift();
70 207 return function() {
71 208 return __method.apply(object, args.concat($A(arguments)));
72 209 }
73 - }
74 -
75 - Function.prototype.bindAsEventListener = function(object) {
210 + },
211 +
212 + bindAsEventListener: function() {
76 213 var __method = this, args = $A(arguments), object = args.shift();
77 214 return function(event) {
78 - return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
79 - }
80 - }
81 -
82 - Object.extend(Number.prototype, {
83 - toColorPart: function() {
84 - var digits = this.toString(16);
85 - if (this < 16) return '0' + digits;
86 - return digits;
87 - },
88 -
89 - succ: function() {
90 - return this + 1;
91 - },
92 -
93 - times: function(iterator) {
94 - $R(0, this, true).each(iterator);
95 - return this;
215 + return __method.apply(object, [event || window.event].concat(args));
216 + }
217 + },
218 +
219 + curry: function() {
220 + if (!arguments.length) return this;
221 + var __method = this, args = $A(arguments);
222 + return function() {
223 + return __method.apply(this, args.concat($A(arguments)));
224 + }
225 + },
226 +
227 + delay: function() {
228 + var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
229 + return window.setTimeout(function() {
230 + return __method.apply(__method, args);
231 + }, timeout);
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 261 var Try = {
100 262 these: function() {
101 263 var returnValue;
@@ -110,12 +272,17
110 272
111 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();
118 - PeriodicalExecuter.prototype = {
285 + var PeriodicalExecuter = Class.create({
119 286 initialize: function(callback, frequency) {
120 287 this.callback = callback;
121 288 this.frequency = frequency;
@@ -128,6 +295,10
128 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 302 stop: function() {
132 303 if (!this.timer) return;
133 304 clearInterval(this.timer);
@@ -138,16 +309,26
138 309 if (!this.currentlyExecuting) {
139 310 try {
140 311 this.currentlyExecuting = true;
141 - this.callback(this);
312 + this.execute();
142 313 } finally {
143 314 this.currentlyExecuting = false;
144 315 }
145 316 }
146 317 }
147 - }
148 - String.interpret = function(value){
318 + });
319 + Object.extend(String, {
320 + interpret: function(value) {
149 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 333 Object.extend(String.prototype, {
153 334 gsub: function(pattern, replacement) {
@@ -168,7 +349,7
168 349
169 350 sub: function(pattern, replacement, count) {
170 351 replacement = this.gsub.prepareReplacement(replacement);
171 - count = count === undefined ? 1 : count;
352 + count = Object.isUndefined(count) ? 1 : count;
172 353
173 354 return this.gsub(pattern, function(match) {
174 355 if (--count < 0) return match[0];
@@ -178,14 +359,14
178 359
179 360 scan: function(pattern, iterator) {
180 361 this.gsub(pattern, iterator);
181 - return this;
362 + return String(this);
182 363 },
183 364
184 365 truncate: function(length, truncation) {
185 366 length = length || 30;
186 - truncation = truncation === undefined ? '...' : truncation;
367 + truncation = Object.isUndefined(truncation) ? '...' : truncation;
187 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 372 strip: function() {
@@ -213,14 +394,13
213 394 },
214 395
215 396 escapeHTML: function() {
216 - var div = document.createElement('div');
217 - var text = document.createTextNode(this);
218 - div.appendChild(text);
219 - return div.innerHTML;
397 + var self = arguments.callee;
398 + self.text.data = this;
399 + return self.div.innerHTML;
220 400 },
221 401
222 402 unescapeHTML: function() {
223 - var div = document.createElement('div');
403 + var div = new Element('div');
224 404 div.innerHTML = this.stripTags();
225 405 return div.childNodes[0] ? (div.childNodes.length > 1 ?
226 406 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
@@ -233,15 +413,15
233 413
234 414 return match[1].split(separator || '&').inject({}, function(hash, pair) {
235 415 if ((pair = pair.split('='))[0]) {
236 - var name = decodeURIComponent(pair[0]);
237 - var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
238 -
239 - if (hash[name] !== undefined) {
240 - if (hash[name].constructor != Array)
241 - hash[name] = [hash[name]];
242 - if (value) hash[name].push(value);
243 - }
244 - else hash[name] = value;
416 + var key = decodeURIComponent(pair.shift());
417 + var value = pair.length > 1 ? pair.join('=') : pair[0];
418 + if (value != undefined) value = decodeURIComponent(value);
419 +
420 + if (key in hash) {
421 + if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
422 + hash[key].push(value);
423 + }
424 + else hash[key] = value;
245 425 }
246 426 return hash;
247 427 });
@@ -256,6 +436,10
256 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 443 camelize: function() {
260 444 var parts = this.split('-'), len = parts.length;
261 445 if (len == 1) return parts[0];
@@ -283,52 +467,131
283 467 },
284 468
285 469 inspect: function(useDoubleQuotes) {
286 - var escapedString = this.replace(/\\/g, '\\\\');
287 - if (useDoubleQuotes)
288 - return '"' + escapedString.replace(/"/g, '\\"') + '"';
289 - else
470 + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
471 + var character = String.specialChar[match[0]];
472 + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
473 + });
474 + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
290 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 536 String.prototype.gsub.prepareReplacement = function(replacement) {
295 - if (typeof replacement == 'function') return replacement;
537 + if (Object.isFunction(replacement)) return replacement;
296 538 var template = new Template(replacement);
297 539 return function(match) { return template.evaluate(match) };
298 - }
540 + };
299 541
300 542 String.prototype.parseQuery = String.prototype.toQueryParams;
301 543
302 - var Template = Class.create();
303 - Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
304 - Template.prototype = {
544 + Object.extend(String.prototype.escapeHTML, {
545 + div: document.createElement('div'),
546 + text: document.createTextNode('')
547 + });
548 +
549 + with (String.prototype.escapeHTML) div.appendChild(text);
550 +
551 + var Template = Class.create({
305 552 initialize: function(template, pattern) {
306 553 this.template = template.toString();
307 554 this.pattern = pattern || Template.Pattern;
308 555 },
309 556
310 557 evaluate: function(object) {
558 + if (Object.isFunction(object.toTemplateReplacements))
559 + object = object.toTemplateReplacements();
560 +
311 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 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 - }
317 - }
318 -
319 - var $break = new Object();
320 - var $continue = new Object();
584 + Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
585 +
586 + var $break = { };
321 587
322 588 var Enumerable = {
323 - each: function(iterator) {
589 + each: function(iterator, context) {
324 590 var index = 0;
591 + iterator = iterator.bind(context);
325 592 try {
326 593 this._each(function(value) {
327 - try {
328 594 iterator(value, index++);
329 - } catch (e) {
330 - if (e != $continue) throw e;
331 - }
332 595 });
333 596 } catch (e) {
334 597 if (e != $break) throw e;
@@ -336,40 +599,45
336 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 604 var index = -number, slices = [], array = this.toArray();
341 605 while ((index += number) < array.length)
342 606 slices.push(array.slice(index, index+number));
343 - return slices.map(iterator);
344 - },
345 -
346 - all: function(iterator) {
607 + return slices.collect(iterator, context);
608 + },
609 +
610 + all: function(iterator, context) {
611 + iterator = iterator ? iterator.bind(context) : Prototype.K;
347 612 var result = true;
348 613 this.each(function(value, index) {
349 - result = result && !!(iterator || Prototype.K)(value, index);
614 + result = result && !!iterator(value, index);
350 615 if (!result) throw $break;
351 616 });
352 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 622 var result = false;
357 623 this.each(function(value, index) {
358 - if (result = !!(iterator || Prototype.K)(value, index))
624 + if (result = !!iterator(value, index))
359 625 throw $break;
360 626 });
361 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 632 var results = [];
366 633 this.each(function(value, index) {
367 - results.push((iterator || Prototype.K)(value, index));
634 + results.push(iterator(value, index));
368 635 });
369 636 return results;
370 637 },
371 638
372 - detect: function(iterator) {
639 + detect: function(iterator, context) {
640 + iterator = iterator.bind(context);
373 641 var result;
374 642 this.each(function(value, index) {
375 643 if (iterator(value, index)) {
@@ -380,7 +648,8
380 648 return result;
381 649 },
382 650
383 - findAll: function(iterator) {
651 + findAll: function(iterator, context) {
652 + iterator = iterator.bind(context);
384 653 var results = [];
385 654 this.each(function(value, index) {
386 655 if (iterator(value, index))
@@ -389,17 +658,24
389 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 663 var results = [];
664 +
665 + if (Object.isString(filter))
666 + filter = new RegExp(filter);
667 +
394 668 this.each(function(value, index) {
395 - var stringValue = value.toString();
396 - if (stringValue.match(pattern))
397 - results.push((iterator || Prototype.K)(value, index));
398 - })
669 + if (filter.match(value))
670 + results.push(iterator(value, index));
671 + });
399 672 return results;
400 673 },
401 674
402 675 include: function(object) {
676 + if (Object.isFunction(this.indexOf))
677 + if (this.indexOf(object) != -1) return true;
678 +
403 679 var found = false;
404 680 this.each(function(value) {
405 681 if (value == object) {
@@ -411,14 +687,15
411 687 },
412 688
413 689 inGroupsOf: function(number, fillWith) {
414 - fillWith = fillWith === undefined ? null : fillWith;
690 + fillWith = Object.isUndefined(fillWith) ? null : fillWith;
415 691 return this.eachSlice(number, function(slice) {
416 692 while(slice.length < number) slice.push(fillWith);
417 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 699 this.each(function(value, index) {
423 700 memo = iterator(memo, value, index);
424 701 });
@@ -432,30 +709,33
432 709 });
433 710 },
434 711
435 - max: function(iterator) {
712 + max: function(iterator, context) {
713 + iterator = iterator ? iterator.bind(context) : Prototype.K;
436 714 var result;
437 715 this.each(function(value, index) {
438 - value = (iterator || Prototype.K)(value, index);
439 - if (result == undefined || value >= result)
716 + value = iterator(value, index);
717 + if (result == null || value >= result)
440 718 result = value;
441 719 });
442 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 725 var result;
447 726 this.each(function(value, index) {
448 - value = (iterator || Prototype.K)(value, index);
449 - if (result == undefined || value < result)
727 + value = iterator(value, index);
728 + if (result == null || value < result)
450 729 result = value;
451 730 });
452 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 736 var trues = [], falses = [];
457 737 this.each(function(value, index) {
458 - ((iterator || Prototype.K)(value, index) ?
738 + (iterator(value, index) ?
459 739 trues : falses).push(value);
460 740 });
461 741 return [trues, falses];
@@ -463,13 +743,14
463 743
464 744 pluck: function(property) {
465 745 var results = [];
466 - this.each(function(value, index) {
746 + this.each(function(value) {
467 747 results.push(value[property]);
468 748 });
469 749 return results;
470 750 },
471 751
472 - reject: function(iterator) {
752 + reject: function(iterator, context) {
753 + iterator = iterator.bind(context);
473 754 var results = [];
474 755 this.each(function(value, index) {
475 756 if (!iterator(value, index))
@@ -478,7 +759,8
478 759 return results;
479 760 },
480 761
481 - sortBy: function(iterator) {
762 + sortBy: function(iterator, context) {
763 + iterator = iterator.bind(context);
482 764 return this.map(function(value, index) {
483 765 return {value: value, criteria: iterator(value, index)};
484 766 }).sort(function(left, right) {
@@ -493,7 +775,7
493 775
494 776 zip: function() {
495 777 var iterator = Prototype.K, args = $A(arguments);
496 - if (typeof args.last() == 'function')
778 + if (Object.isFunction(args.last()))
497 779 iterator = args.pop();
498 780
499 781 var collections = [this].concat(args).map($A);
@@ -509,31 +791,42
509 791 inspect: function() {
510 792 return '#<Enumerable:' + this.toArray().inspect() + '>';
511 793 }
512 - }
794 + };
513 795
514 796 Object.extend(Enumerable, {
515 797 map: Enumerable.collect,
516 798 find: Enumerable.detect,
517 799 select: Enumerable.findAll,
800 + filter: Enumerable.findAll,
518 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 807 if (!iterable) return [];
523 - if (iterable.toArray) {
524 - return iterable.toArray();
525 - } else {
526 - var results = [];
527 - for (var i = 0, length = iterable.length; i < length; i++)
528 - results.push(iterable[i]);
808 + if (iterable.toArray) return iterable.toArray();
809 + var length = iterable.length, results = new Array(length);
810 + while (length--) results[length] = iterable[length];
529 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 827 Object.extend(Array.prototype, Enumerable);
534 828
535 - if (!Array.prototype._reverse)
536 - Array.prototype._reverse = Array.prototype.reverse;
829 + if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
537 830
538 831 Object.extend(Array.prototype, {
539 832 _each: function(iterator) {
@@ -562,7 +855,7
562 855
563 856 flatten: function() {
564 857 return this.inject([], function(array, value) {
565 - return array.concat(value && value.constructor == Array ?
858 + return array.concat(Object.isArray(value) ?
566 859 value.flatten() : [value]);
567 860 });
568 861 },
@@ -574,12 +867,6
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 870 reverse: function(inline) {
584 871 return (inline !== false ? this : this.toArray())._reverse();
585 872 },
@@ -588,9 +875,17
588 875 return this.length > 1 ? this : this[0];
589 876 },
590 877
591 - uniq: function() {
592 - return this.inject([], function(array, value) {
593 - return array.include(value) ? array : array.concat([value]);
878 + uniq: function(sorted) {
879 + return this.inject([], function(array, value, index) {
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
@@ -604,22 +899,51
604 899
605 900 inspect: function() {
606 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 933 Array.prototype.toArray = Array.prototype.clone;
611 934
612 935 function $w(string){
936 + if (!Object.isString(string)) return [];
613 937 string = string.strip();
614 938 return string ? string.split(/\s+/) : [];
615 939 }
616 940
617 - if(window.opera){
941 + if (Prototype.Browser.Opera){
618 942 Array.prototype.concat = function(){
619 943 var array = [];
620 944 for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621 945 for(var i = 0, length = arguments.length; i < length; i++) {
622 - if(arguments[i].constructor == Array) {
946 + if (Object.isArray(arguments[i])) {
623 947 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624 948 array.push(arguments[i][j]);
625 949 } else {
@@ -627,53 +951,78
627 951 }
628 952 }
629 953 return array;
630 - }
631 - }
632 - var Hash = function(obj) {
633 - Object.extend(this, obj || {});
634 954 };
635 -
636 - Object.extend(Hash, {
637 - toQueryString: function(obj) {
638 - var parts = [];
639 -
640 - this.prototype._each.call(obj, function(pair) {
641 - if (!pair.key) return;
642 -
643 - if (pair.value && pair.value.constructor == Array) {
644 - var values = pair.value.compact();
645 - if (values.length < 2) pair.value = values.reduce();
646 - else {
647 - key = encodeURIComponent(pair.key);
648 - values.each(function(value) {
649 - value = value != undefined ? encodeURIComponent(value) : '';
650 - parts.push(key + '=' + encodeURIComponent(value));
651 - });
652 - return;
653 - }
654 - }
655 - if (pair.value == undefined) pair[1] = '';
656 - parts.push(pair.map(encodeURIComponent).join('='));
657 - });
658 -
659 - return parts.join('&');
955 + }
956 + Object.extend(Number.prototype, {
957 + toColorPart: function() {
958 + return this.toPaddedString(2, 16);
959 + },
960 +
961 + succ: function() {
962 + return this + 1;
963 + },
964 +
965 + times: function(iterator) {
966 + $R(0, this, true).each(iterator);
967 + return this;
968 + },
969 +
970 + toPaddedString: function(length, radix) {
971 + var string = this.toString(radix || 10);
972 + return '0'.times(length - string.length) + string;
973 + },
974 +
975 + toJSON: function() {
976 + return isFinite(this) ? this.toString() : 'null';
660 977 }
661 978 });
662 979
663 - Object.extend(Hash.prototype, Enumerable);
664 - Object.extend(Hash.prototype, {
980 + $w('abs round ceil floor').each(function(method){
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 999 _each: function(iterator) {
666 - for (var key in this) {
667 - var value = this[key];
668 - if (value && value == Hash.prototype[key]) continue;
669 -
670 - var pair = [key, value];
1000 + for (var key in this._object) {
1001 + var value = this._object[key], pair = [key, value];
671 1002 pair.key = key;
672 1003 pair.value = value;
673 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 1026 keys: function() {
678 1027 return this.pluck('key');
679 1028 },
@@ -682,47 +1031,55
682 1031 return this.pluck('value');
683 1032 },
684 1033
685 - merge: function(hash) {
686 - return $H(hash).inject(this, function(mergedHash, pair) {
687 - mergedHash[pair.key] = pair.value;
688 - return mergedHash;
1034 + index: function(value) {
1035 + var match = this.detect(function(pair) {
1036 + return pair.value === value;
689 1037 });
690 - },
691 -
692 - remove: function() {
693 - var result;
694 - for(var i = 0, length = arguments.length; i < length; i++) {
695 - var value = this[arguments[i]];
696 - if (value !== undefined){
697 - if (result === undefined) result = value;
698 - else {
699 - if (result.constructor != Array) result = [result];
700 - result.push(value)
701 - }
702 - }
703 - delete this[arguments[i]];
704 - }
1038 + return match && match.key;
1039 + },
1040 +
1041 + merge: function(object) {
1042 + return this.clone().update(object);
1043 + },
1044 +
1045 + update: function(object) {
1046 + return new Hash(object).inject(this, function(result, pair) {
1047 + result.set(pair.key, pair.value);
705 1048 return result;
1049 + });
706 1050 },
707 1051
708 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 1064 inspect: function() {
713 1065 return '#<Hash:{' + this.map(function(pair) {
714 1066 return pair.map(Object.inspect).join(': ');
715 1067 }).join(', ') + '}>';
716 - }
717 - });
718 -
719 - function $H(object) {
720 - if (object && object.constructor == Hash) return object;
721 - return new Hash(object);
722 - };
723 - ObjectRange = Class.create();
724 - Object.extend(ObjectRange.prototype, Enumerable);
725 - Object.extend(ObjectRange.prototype, {
1068 + },
1069 +
1070 + toJSON: function() {
1071 + return Object.toJSON(this.toObject());
1072 + },
1073 +
1074 + clone: function() {
1075 + return new Hash(this);
1076 + }
1077 + }
1078 + })());
1079 +
1080 + Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1081 + Hash.from = $H;
1082 + var ObjectRange = Class.create(Enumerable, {
726 1083 initialize: function(start, end, exclusive) {
727 1084 this.start = start;
728 1085 this.end = end;
@@ -748,7 +1105,7
748 1105
749 1106 var $R = function(start, end, exclusive) {
750 1107 return new ObjectRange(start, end, exclusive);
751 - }
1108 + };
752 1109
753 1110 var Ajax = {
754 1111 getTransport: function() {
@@ -760,7 +1117,7
760 1117 },
761 1118
762 1119 activeRequestCount: 0
763 - }
1120 + };
764 1121
765 1122 Ajax.Responders = {
766 1123 responders: [],
@@ -780,7 +1137,7
780 1137
781 1138 dispatch: function(callback, request, transport, json) {
782 1139 this.each(function(responder) {
783 - if (typeof responder[callback] == 'function') {
1140 + if (Object.isFunction(responder[callback])) {
784 1141 try {
785 1142 responder[callback].apply(responder, [request, transport, json]);
786 1143 } catch (e) {}
@@ -792,49 +1149,45
792 1149 Object.extend(Ajax.Responders, Enumerable);
793 1150
794 1151 Ajax.Responders.register({
795 - onCreate: function() {
796 - Ajax.activeRequestCount++;
797 - },
798 - onComplete: function() {
799 - Ajax.activeRequestCount--;
800 - }
1152 + onCreate: function() { Ajax.activeRequestCount++ },
1153 + onComplete: function() { Ajax.activeRequestCount-- }
801 1154 });
802 1155
803 - Ajax.Base = function() {};
804 - Ajax.Base.prototype = {
805 - setOptions: function(options) {
1156 + Ajax.Base = Class.create({
1157 + initialize: function(options) {
806 1158 this.options = {
807 1159 method: 'post',
808 1160 asynchronous: true,
809 1161 contentType: 'application/x-www-form-urlencoded',
810 1162 encoding: 'UTF-8',
811 - parameters: ''
812 - }
1163 + parameters: '',
1164 + evalJSON: true,
1165 + evalJS: true
1166 + };
813 1167 Object.extend(this.options, options || {});
814 1168
815 1169 this.options.method = this.options.method.toLowerCase();
816 - if (typeof this.options.parameters == 'string')
1170 +
1171 + if (Object.isString(this.options.parameters))
817 1172 this.options.parameters = this.options.parameters.toQueryParams();
818 - }
819 - }
820 -
821 - Ajax.Request = Class.create();
822 - Ajax.Request.Events =
823 - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
824 -
825 - Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1173 + else if (Object.isHash(this.options.parameters))
1174 + this.options.parameters = this.options.parameters.toObject();
1175 + }
1176 + });
1177 +
1178 + Ajax.Request = Class.create(Ajax.Base, {
826 1179 _complete: false,
827 1180
828 - initialize: function(url, options) {
1181 + initialize: function($super, url, options) {
1182 + $super(options);
829 1183 this.transport = Ajax.getTransport();
830 - this.setOptions(options);
831 1184 this.request(url);
832 1185 },
833 1186
834 1187 request: function(url) {
835 1188 this.url = url;
836 1189 this.method = this.options.method;
837 - var params = this.options.parameters;
1190 + var params = Object.clone(this.options.parameters);
838 1191
839 1192 if (!['get', 'post'].include(this.method)) {
840 1193 // simulate other verbs over post
@@ -842,28 +1195,31
842 1195 this.method = 'post';
843 1196 }
844 1197
845 - params = Hash.toQueryString(params);
846 - if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
847 -
1198 + this.parameters = params;
1199 +
1200 + if (params = Object.toQueryString(params)) {
848 1201 // when GET, append parameters to URL
849 - if (this.method == 'get' && params)
850 - this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
1202 + if (this.method == 'get')
1203 + this.url += (this.url.include('?') ? '&' : '?') + params;
1204 + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1205 + params += '&_=';
1206 + }
851 1207
852 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 1213 this.transport.open(this.method.toUpperCase(), this.url,
856 1214 this.options.asynchronous);
857 1215
858 - if (this.options.asynchronous)
859 - setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1216 + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
860 1217
861 1218 this.transport.onreadystatechange = this.onStateChange.bind(this);
862 1219 this.setRequestHeaders();
863 1220
864 - var body = this.method == 'post' ? (this.options.postBody || params) : null;
865 -
866 - this.transport.send(body);
1221 + this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1222 + this.transport.send(this.body);
867 1223
868 1224 /* Force Firefox to handle ready state 4 for synchronous requests */
869 1225 if (!this.options.asynchronous && this.transport.overrideMimeType)
@@ -905,7 +1261,7
905 1261 if (typeof this.options.requestHeaders == 'object') {
906 1262 var extras = this.options.requestHeaders;
907 1263
908 - if (typeof extras.push == 'function')
1264 + if (Object.isFunction(extras.push))
909 1265 for (var i = 0, length = extras.length; i < length; i += 2)
910 1266 headers[extras[i]] = extras[i+1];
911 1267 else
@@ -917,32 +1273,39
917 1273 },
918 1274
919 1275 success: function() {
920 - return !this.transport.status
921 - || (this.transport.status >= 200 && this.transport.status < 300);
1276 + var status = this.getStatus();
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 1286 respondToReadyState: function(readyState) {
925 - var state = Ajax.Request.Events[readyState];
926 - var transport = this.transport, json = this.evalJSON();
1287 + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
927 1288
928 1289 if (state == 'Complete') {
929 1290 try {
930 1291 this._complete = true;
931 - (this.options['on' + this.transport.status]
1292 + (this.options['on' + response.status]
932 1293 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
933 - || Prototype.emptyFunction)(transport, json);
1294 + || Prototype.emptyFunction)(response, response.headerJSON);
934 1295 } catch (e) {
935 1296 this.dispatchException(e);
936 1297 }
937 1298
938 - if ((this.getHeader('Content-type') || 'text/javascript').strip().
939 - match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1299 + var contentType = response.getHeader('Content-type');
1300 + if (this.options.evalJS == 'force'
1301 + || (this.options.evalJS && contentType
1302 + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
940 1303 this.evalResponse();
941 1304 }
942 1305
943 1306 try {
944 - (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
945 - Ajax.Responders.dispatch('on' + state, this, transport, json);
1307 + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1308 + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
946 1309 } catch (e) {
947 1310 this.dispatchException(e);
948 1311 }
@@ -959,16 +1322,9
959 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 1325 evalResponse: function() {
970 1326 try {
971 - return eval(this.transport.responseText);
1327 + return eval((this.transport.responseText || '').unfilterJSON());
972 1328 } catch (e) {
973 1329 this.dispatchException(e);
974 1330 }
@@ -980,51 +1336,120
980 1336 }
981 1337 });
982 1338
983 - Ajax.Updater = Class.create();
984 -
985 - Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
986 - initialize: function(container, url, options) {
1339 + Ajax.Request.Events =
1340 + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1341 +
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 1416 this.container = {
988 1417 success: (container.success || container),
989 1418 failure: (container.failure || (container.success ? null : container))
990 - }
991 -
992 - this.transport = Ajax.getTransport();
993 - this.setOptions(options);
994 -
995 - var onComplete = this.options.onComplete || Prototype.emptyFunction;
996 - this.options.onComplete = (function(transport, param) {
997 - this.updateContent();
998 - onComplete(transport, param);
1419 + };
1420 +
1421 + options = Object.clone(options);
1422 + var onComplete = options.onComplete;
1423 + options.onComplete = (function(response, json) {
1424 + this.updateContent(response.responseText);
1425 + if (Object.isFunction(onComplete)) onComplete(response, json);
999 1426 }).bind(this);
1000 1427
1001 - this.request(url);
1002 - },
1003 -
1004 - updateContent: function() {
1005 - var receiver = this.container[this.success() ? 'success' : 'failure'];
1006 - var response = this.transport.responseText;
1007 -
1008 - if (!this.options.evalScripts) response = response.stripScripts();
1428 + $super(url, options);
1429 + },
1430 +
1431 + updateContent: function(responseText) {
1432 + var receiver = this.container[this.success() ? 'success' : 'failure'],
1433 + options = this.options;
1434 +
1435 + if (!options.evalScripts) responseText = responseText.stripScripts();
1009 1436
1010 1437 if (receiver = $(receiver)) {
1011 - if (this.options.insertion)
1012 - new this.options.insertion(receiver, response);
1013 - else
1014 - receiver.update(response);
1015 - }
1016 -
1017 - if (this.success()) {
1018 - if (this.onComplete)
1019 - setTimeout(this.onComplete.bind(this), 10);
1438 + if (options.insertion) {
1439 + if (Object.isString(options.insertion)) {
1440 + var insertion = { }; insertion[options.insertion] = responseText;
1441 + receiver.insert(insertion);
1442 + }
1443 + else options.insertion(receiver, responseText);
1444 + }
1445 + else receiver.update(responseText);
1020 1446 }
1021 1447 }
1022 1448 });
1023 1449
1024 - Ajax.PeriodicalUpdater = Class.create();
1025 - Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1026 - initialize: function(container, url, options) {
1027 - this.setOptions(options);
1450 + Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1451 + initialize: function($super, container, url, options) {
1452 + $super(options);
1028 1453 this.onComplete = this.options.onComplete;
1029 1454
1030 1455 this.frequency = (this.options.frequency || 2);
@@ -1048,15 +1473,14
1048 1473 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049 1474 },
1050 1475
1051 - updateComplete: function(request) {
1476 + updateComplete: function(response) {
1052 1477 if (this.options.decay) {
1053 - this.decay = (request.responseText == this.lastText ?
1478 + this.decay = (response.responseText == this.lastText ?
1054 1479 this.decay * this.options.decay : 1);
1055 1480
1056 - this.lastText = request.responseText;
1057 - }
1058 - this.timer = setTimeout(this.onTimerEvent.bind(this),
1059 - this.decay * this.frequency * 1000);
1481 + this.lastText = response.responseText;
1482 + }
1483 + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1060 1484 },
1061 1485
1062 1486 onTimerEvent: function() {
@@ -1069,7 +1493,7
1069 1493 elements.push($(arguments[i]));
1070 1494 return elements;
1071 1495 }
1072 - if (typeof element == 'string')
1496 + if (Object.isString(element))
1073 1497 element = document.getElementById(element);
1074 1498 return Element.extend(element);
1075 1499 }
@@ -1080,63 +1504,51
1080 1504 var query = document.evaluate(expression, $(parentElement) || document,
1081 1505 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082 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 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)
1107 - var Element = new Object();
1108 -
1109 - Element.extend = function(element) {
1110 - if (!element || _nativeExtensions || element.nodeType == 3) return element;
1111 -
1112 - if (!element._extended && element.tagName && element != window) {
1113 - var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1114 -
1115 - if (element.tagName == 'FORM')
1116 - Object.extend(methods, Form.Methods);
1117 - if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1118 - Object.extend(methods, Form.Element.Methods);
1119 -
1120 - Object.extend(methods, Element.Methods.Simulated);
1121 -
1122 - for (var property in methods) {
1123 - var value = methods[property];
1124 - if (typeof value == 'function' && !(property in element))
1125 - element[property] = cache.findOrStore(value);
1126 - }
1127 - }
1128 -
1129 - element._extended = true;
1130 - return element;
1514 + if (!window.Node) var Node = { };
1515 +
1516 + if (!Node.ELEMENT_NODE) {
1517 + // DOM level 2 ECMAScript Language Binding
1518 + Object.extend(Node, {
1519 + ELEMENT_NODE: 1,
1520 + ATTRIBUTE_NODE: 2,
1521 + TEXT_NODE: 3,
1522 + CDATA_SECTION_NODE: 4,
1523 + ENTITY_REFERENCE_NODE: 5,
1524 + ENTITY_NODE: 6,
1525 + PROCESSING_INSTRUCTION_NODE: 7,
1526 + COMMENT_NODE: 8,
1527 + DOCUMENT_NODE: 9,
1528 + DOCUMENT_TYPE_NODE: 10,
1529 + DOCUMENT_FRAGMENT_NODE: 11,
1530 + NOTATION_NODE: 12
1531 + });
1532 + }
1533 +
1534 + (function() {
1535 + var element = this.Element;
1536 + this.Element = function(tagName, attributes) {
1537 + attributes = attributes || { };
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 -
1133 - Element.extend.cache = {
1134 - findOrStore: function(value) {
1135 - return this[value] = this[value] || function() {
1136 - return value.apply(null, [this].concat($A(arguments)));
1137 - }
1138 - }
1139 - };
1548 + Object.extend(this.Element, element || { });
1549 + }).call(window);
1550 +
1551 + Element.cache = { };
1140 1552
1141 1553 Element.Methods = {
1142 1554 visible: function(element) {
@@ -1165,28 +1577,74
1165 1577 return element;
1166 1578 },
1167 1579
1168 - update: function(element, html) {
1169 - html = typeof html == 'undefined' ? '' : html.toString();
1170 - $(element).innerHTML = html.stripScripts();
1171 - setTimeout(function() {html.evalScripts()}, 10);
1580 + update: function(element, content) {
1581 + element = $(element);
1582 + if (content && content.toElement) content = content.toElement();
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 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 1605 element = $(element);
1177 - html = typeof html == 'undefined' ? '' : html.toString();
1178 - if (element.outerHTML) {
1179 - element.outerHTML = html.stripScripts();
1180 - } else {
1181 - var range = element.ownerDocument.createRange();
1182 - range.selectNodeContents(element);
1183 - element.parentNode.replaceChild(
1184 - range.createContextualFragment(html.stripScripts()), element);
1185 - }
1186 - setTimeout(function() {html.evalScripts()}, 10);
1606 +
1607 + if (Object.isString(insertions) || Object.isNumber(insertions) ||
1608 + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1609 + insertions = {bottom:insertions};
1610 +
1611 + var content, t, range;
1612 +
1613 + for (position in insertions) {
1614 + content = insertions[position];
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 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 1648 inspect: function(element) {
1191 1649 element = $(element);
1192 1650 var result = '<' + element.tagName.toLowerCase();
@@ -1212,7 +1670,13
1212 1670 },
1213 1671
1214 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 1682 immediateDescendants: function(element) {
@@ -1236,48 +1700,96
1236 1700 },
1237 1701
1238 1702 match: function(element, selector) {
1239 - if (typeof selector == 'string')
1703 + if (Object.isString(selector))
1240 1704 selector = new Selector(selector);
1241 1705 return selector.match($(element));
1242 1706 },
1243 1707
1244 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 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 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 1732 next: function(element, expression, index) {
1257 - return Selector.findElement($(element).nextSiblings(), expression, index);
1258 - },
1259 -
1260 - getElementsBySelector: function() {
1733 + element = $(element);
1734 + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1735 + var nextSiblings = element.nextSiblings();
1736 + return expression ? Selector.findElement(nextSiblings, expression, index) :
1737 + nextSiblings[index || 0];
1738 + },
1739 +
1740 + select: function() {
1261 1741 var args = $A(arguments), element = $(args.shift());
1262 1742 return Selector.findChildElements(element, args);
1263 1743 },
1264 1744
1265 - getElementsByClassName: function(element, className) {
1266 - return document.getElementsByClassName(className, element);
1745 + adjacent: function() {
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 1759 readAttribute: function(element, name) {
1270 1760 element = $(element);
1271 - if (document.all && !window.opera) {
1272 - var t = Element._attributeTranslations;
1761 + if (Prototype.Browser.IE) {
1762 + var t = Element._attributeTranslations.read;
1273 1763 if (t.values[name]) return t.values[name](element, name);
1274 1764 if (t.names[name]) name = t.names[name];
1275 - var attribute = element.attributes[name];
1276 - if(attribute) return attribute.nodeValue;
1765 + if (name.include(':')) {
1766 + return (!element.attributes || !element.attributes[name]) ? null :
1767 + element.attributes[name].value;
1768 + }
1277 1769 }
1278 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 1793 getHeight: function(element) {
1282 1794 return $(element).getDimensions().height;
1283 1795 },
@@ -1293,39 +1805,28
1293 1805 hasClassName: function(element, className) {
1294 1806 if (!(element = $(element))) return;
1295 1807 var elementClassName = element.className;
1296 - if (elementClassName.length == 0) return false;
1297 - if (elementClassName == className ||
1298 - elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299 - return true;
1300 - return false;
1808 + return (elementClassName.length > 0 && (elementClassName == className ||
1809 + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1301 1810 },
1302 1811
1303 1812 addClassName: function(element, className) {
1304 1813 if (!(element = $(element))) return;
1305 - Element.classNames(element).add(className);
1814 + if (!element.hasClassName(className))
1815 + element.className += (element.className ? ' ' : '') + className;
1306 1816 return element;
1307 1817 },
1308 1818
1309 1819 removeClassName: function(element, className) {
1310 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 1823 return element;
1313 1824 },
1314 1825
1315 1826 toggleClassName: function(element, className) {
1316 1827 if (!(element = $(element))) return;
1317 - Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318 - return element;
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();
1828 + return element[element.hasClassName(className) ?
1829 + 'removeClassName' : 'addClassName'](className);
1329 1830 },
1330 1831
1331 1832 // removes whitespace-only text node children
@@ -1342,74 +1843,76
1342 1843 },
1343 1844
1344 1845 empty: function(element) {
1345 - return $(element).innerHTML.match(/^\s*$/);
1846 + return $(element).innerHTML.blank();
1346 1847 },
1347 1848
1348 1849 descendantOf: function(element, ancestor) {
1349 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 1866 while (element = element.parentNode)
1351 - if (element == ancestor) return true;
1867 + if (element == originalAncestor) return true;
1352 1868 return false;
1353 1869 },
1354 1870
1355 1871 scrollTo: function(element) {
1356 1872 element = $(element);
1357 - var pos = Position.cumulativeOffset(element);
1873 + var pos = element.cumulativeOffset();
1358 1874 window.scrollTo(pos[0], pos[1]);
1359 1875 return element;
1360 1876 },
1361 1877
1362 1878 getStyle: function(element, style) {
1363 1879 element = $(element);
1364 - if (['float','cssFloat'].include(style))
1365 - style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366 - style = style.camelize();
1880 + style = style == 'float' ? 'cssFloat' : style.camelize();
1367 1881 var value = element.style[style];
1368 1882 if (!value) {
1369 - if (document.defaultView && document.defaultView.getComputedStyle) {
1370 1883 var css = document.defaultView.getComputedStyle(element, null);
1371 1884 value = css ? css[style] : null;
1372 - } else if (element.currentStyle) {
1373 - value = element.currentStyle[style];
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 - }
1885 + }
1886 + if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1388 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 1895 element = $(element);
1393 - for (var name in style) {
1394 - var value = style[name];
1395 - if(name == 'opacity') {
1396 - if (value == 1) {
1397 - value = (/Gecko/.test(navigator.userAgent) &&
1398 - !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401 - } else if(value == '') {
1402 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404 - } else {
1405 - if(value < 0.00001) value = 0;
1406 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407 - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408 - 'alpha(opacity='+value*100+')';
1409 - }
1410 - } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411 - element.style[name.camelize()] = value;
1412 - }
1896 + var elementStyle = element.style, match;
1897 + if (Object.isString(styles)) {
1898 + element.style.cssText += ';' + styles;
1899 + return styles.include('opacity') ?
1900 + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1901 + }
1902 + for (var property in styles)
1903 + if (property == 'opacity') element.setOpacity(styles[property]);
1904 + else
1905 + elementStyle[(property == 'float' || property == 'cssFloat') ?
1906 + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
1907 + property] = styles[property];
1908 +
1909 + return element;
1910 + },
1911 +
1912 + setOpacity: function(element, value) {
1913 + element = $(element);
1914 + element.style.opacity = (value == 1 || value === '') ? '' :
1915 + (value < 0.00001) ? 0 : value;
1413 1916 return element;
1414 1917 },
1415 1918
@@ -1468,8 +1971,8
1468 1971 makeClipping: function(element) {
1469 1972 element = $(element);
1470 1973 if (element._overflow) return element;
1471 - element._overflow = element.style.overflow || 'auto';
1472 - if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1974 + element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1975 + if (element._overflow !== 'hidden')
1473 1976 element.style.overflow = 'hidden';
1474 1977 return element;
1475 1978 },
@@ -1480,233 +1983,2208
1480 1983 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481 1984 element._overflow = null;
1482 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});
1487 -
1488 - Element._attributeTranslations = {};
1489 -
1490 - Element._attributeTranslations.names = {
1491 - colspan: "colSpan",
1492 - rowspan: "rowSpan",
1493 - valign: "vAlign",
1494 - datetime: "dateTime",
1495 - accesskey: "accessKey",
1496 - tabindex: "tabIndex",
1497 - enctype: "encType",
1498 - maxlength: "maxLength",
1499 - readonly: "readOnly",
1500 - longdesc: "longDesc"
2139 + Element.Methods.identify.counter = 1;
2140 +
2141 + Object.extend(Element.Methods, {
2142 + getElementsBySelector: Element.Methods.select,
2143 + childElements: Element.Methods.immediateDescendants
2144 + });
2145 +
2146 + Element._attributeTranslations = {
2147 + write: {
2148 + names: {
2149 + className: 'class',
2150 + htmlFor: 'for'
2151 + },
2152 + values: { }
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 -
1503 - Element._attributeTranslations.values = {
2194 + }
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 2302 _getAttr: function(element, attribute) {
1505 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 2313 _flag: function(element, attribute) {
1509 2314 return $(element).hasAttribute(attribute) ? attribute : null;
1510 2315 },
1511 -
1512 2316 style: function(element) {
1513 2317 return element.style.cssText.toLowerCase();
1514 2318 },
1515 -
1516 2319 title: function(element) {
1517 - var node = element.getAttributeNode('title');
1518 - return node.specified ? node.nodeValue : null;
2320 + return element.title;
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, {
1523 - href: Element._attributeTranslations.values._getAttr,
1524 - src: Element._attributeTranslations.values._getAttr,
1525 - disabled: Element._attributeTranslations.values._flag,
1526 - checked: Element._attributeTranslations.values._flag,
1527 - readonly: Element._attributeTranslations.values._flag,
1528 - multiple: Element._attributeTranslations.values._flag
2339 + Element._attributeTranslations.has = {};
2340 +
2341 + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2342 + 'encType maxLength readOnly longDesc').each(function(attr) {
2343 + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2344 + Element._attributeTranslations.has[attr.toLowerCase()] = attr;
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 2543 Element.Methods.Simulated = {
1532 2544 hasAttribute: function(element, attribute) {
1533 - var t = Element._attributeTranslations;
1534 - attribute = t.names[attribute] || attribute;
1535 - return $(element).getAttributeNode(attribute).specified;
2545 + attribute = Element._attributeTranslations.has[attribute] || attribute;
2546 + var node = $(element).getAttributeNode(attribute);
2547 + return node && node.specified;
1536 2548 }
1537 2549 };
1538 2550
1539 - // IE is missing .innerHTML support for TABLE-related elements
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 - };
2551 + Element.Methods.ByTag = { };
1575 2552
1576 2553 Object.extend(Element, Element.Methods);
1577 2554
1578 - var _nativeExtensions = false;
1579 -
1580 - if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1581 - ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1582 - var className = 'HTML' + tag + 'Element';
1583 - if(window[className]) return;
1584 - var klass = window[className] = {};
1585 - klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
2555 + if (!Prototype.BrowserFeatures.ElementExtensions &&
2556 + document.createElement('div').__proto__) {
2557 + window.HTMLElement = { };
2558 + window.HTMLElement.prototype = document.createElement('div').__proto__;
2559 + Prototype.BrowserFeatures.ElementExtensions = true;
2560 + }
2561 +
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 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 2638 function copy(methods, destination, onlyIfAbsent) {
1592 2639 onlyIfAbsent = onlyIfAbsent || false;
1593 - var cache = Element.extend.cache;
1594 2640 for (var property in methods) {
1595 2641 var value = methods[property];
2642 + if (!Object.isFunction(value)) continue;
1596 2643 if (!onlyIfAbsent || !(property in destination))
1597 - destination[property] = cache.findOrStore(value);
1598 - }
1599 - }
1600 -
1601 - if (typeof HTMLElement != 'undefined') {
2644 + destination[property] = value.methodize();
2645 + }
2646 + }
2647 +
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 2674 copy(Element.Methods, HTMLElement.prototype);
1603 2675 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1604 - copy(Form.Methods, HTMLFormElement.prototype);
1605 - [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1606 - copy(Form.Element.Methods, klass.prototype);
2676 + }
2677 +
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;
1609 - }
1610 - }
1611 -
1612 - var Toggle = new Object();
1613 - Toggle.display = Element.toggle;
1614 -
1615 - /*--------------------------------------------------------------------------*/
1616 -
1617 - Abstract.Insertion = function(adjacency) {
1618 - this.adjacency = adjacency;
1619 - }
1620 -
1621 - Abstract.Insertion.prototype = {
1622 - initialize: function(element, content) {
1623 - this.element = $(element);
1624 - this.content = content.stripScripts();
1625 -
1626 - if (this.adjacency && this.element.insertAdjacentHTML) {
1627 - try {
1628 - this.element.insertAdjacentHTML(this.adjacency, this.content);
1629 - } catch (e) {
1630 - var tagName = this.element.tagName.toUpperCase();
1631 - if (['TBODY', 'TR'].include(tagName)) {
1632 - this.insertContent(this.contentFromAnonymousTable());
1633 - } else {
1634 - throw e;
1635 - }
2855 +
2856 + Object.extend(Selector, {
2857 + _cache: { },
2858 +
2859 + xpath: {
2860 + descendant: "//*",
2861 + child: "/*",
2862 + adjacent: "/following-sibling::*[1]",
2863 + laterSibling: '/following-sibling::*',
2864 + tagName: function(m) {
2865 + if (m[1] == '*') return '';
2866 + return "[local-name()='" + m[1].toLowerCase() +
2867 + "' or local-name()='" + m[1].toUpperCase() + "']";
2868 + },
2869 + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2870 + id: "[@id='#{1}']",
2871 + attrPresence: function(m) {
2872 + m[1] = m[1].toLowerCase();
2873 + return new Template("[@#{1}]").evaluate(m);
2874 + },
2875 + attr: function(m) {
2876 + m[1] = m[1].toLowerCase();
2877 + m[3] = m[5] || m[6];
2878 + return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2879 + },
2880 + pseudo: function(m) {
2881 + var h = Selector.xpath.pseudos[m[1]];
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 3053 } else {
1638 - this.range = this.element.ownerDocument.createRange();
1639 - if (this.initializeRange) this.initializeRange();
1640 - this.insertContent([this.range.createContextualFragment(this.content)]);
1641 - }
1642 -
1643 - setTimeout(function() {content.evalScripts()}, 10);
1644 - },
1645 -
1646 - contentFromAnonymousTable: function() {
1647 - var div = document.createElement('div');
1648 - div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1649 - return $A(div.childNodes[0].childNodes[0].childNodes);
1650 - }
1651 - }
1652 -
1653 - var Insertion = new Object();
1654 -
1655 - Insertion.Before = Class.create();
1656 - Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1657 - initializeRange: function() {
1658 - this.range.setStartBefore(this.element);
1659 - },
1660 -
1661 - insertContent: function(fragments) {
1662 - fragments.each((function(fragment) {
1663 - this.element.parentNode.insertBefore(fragment, this.element);
1664 - }).bind(this));
3054 + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3055 + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3056 + }
3057 + },
3058 +
3059 + // filters out duplicates and extends all nodes
3060 + unique: function(nodes) {
3061 + if (nodes.length == 0) return nodes;
3062 + var results = [], n;
3063 + for (var i = 0, l = nodes.length; i < l; i++)
3064 + if (!(n = nodes[i])._counted) {
3065 + n._counted = true;
3066 + results.push(Element.extend(n));
3067 + }
3068 + return Selector.handlers.unmark(results);
3069 + },
3070 +
3071 + // COMBINATOR FUNCTIONS
3072 + descendant: function(nodes) {
3073 + var h = Selector.handlers;
3074 + for (var i = 0, results = [], node; node = nodes[i]; i++)
3075 + h.concat(results, node.getElementsByTagName('*'));
3076 + return results;
3077 + },
3078 +
3079 + child: function(nodes) {
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();
1669 - Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1670 - initializeRange: function() {
1671 - this.range.selectNodeContents(this.element);
1672 - this.range.collapse(true);
1673 - },
1674 -
1675 - insertContent: function(fragments) {
1676 - fragments.reverse(false).each((function(fragment) {
1677 - this.element.insertBefore(fragment, this.element.firstChild);
1678 - }).bind(this));
3368 + if (Prototype.Browser.IE) {
3369 + // IE returns comment nodes on getElementsByTagName("*").
3370 + // Filter them out.
3371 + Selector.handlers.concat = function(a, b) {
3372 + for (var i = 0, node; node = b[i]; i++)
3373 + if (node.tagName !== "!") a.push(node);
3374 + return a;
3375 + };
3376 + }
3377 +
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();
1683 - Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1684 - initializeRange: function() {
1685 - this.range.selectNodeContents(this.element);
1686 - this.range.collapse(this.element);
1687 - },
1688 -
1689 - insertContent: function(fragments) {
1690 - fragments.each((function(fragment) {
1691 - this.element.appendChild(fragment);
1692 - }).bind(this));
1693 - }
3752 + Event.Methods = (function() {
3753 + var isButton;
3754 +
3755 + if (Prototype.Browser.IE) {
3756 + var buttonMap = { 0: 1, 1: 4, 2: 2 };
3757 + isButton = function(event, code) {
3758 + return event.button == buttonMap[code];
3759 + };
3760 +
3761 + } else if (Prototype.Browser.WebKit) {
3762 + isButton = function(event, code) {
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 -
1696 - Insertion.After = Class.create();
1697 - Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1698 - initializeRange: function() {
1699 - this.range.setStartAfter(this.element);
1700 - },
1701 -
1702 - insertContent: function(fragments) {
1703 - fragments.each((function(fragment) {
1704 - this.element.parentNode.insertBefore(fragment,
1705 - this.element.nextSibling);
1706 - }).bind(this));
1707 - }
3943 + return element;
3944 + }
3945 +
3946 + var wrapper = findWrapper(id, eventName, handler);
3947 + if (!wrapper) return element;
3948 +
3949 + if (element.removeEventListener) {
3950 + element.removeEventListener(name, wrapper, false);
3951 + } else {
3952 + element.detachEvent("on" + name, wrapper);
3953 + }
3954 +
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 4190 Element.ClassNames = Class.create();
@@ -1741,775 +4219,7
1741 4219 };
1742 4220
1743 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 4225 Element.addMethods(); No newline at end of file
You need to be logged in to leave comments. Login now