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 - - The requested commit is too big and content was truncated. 7 files changed. Show full diff

@@ -13,7 +13,8
13 verify :method => :post, :only => [:submit],
13 verify :method => :post, :only => [:submit],
14 :redirect_to => { :action => :index }
14 :redirect_to => { :action => :index }
15
15
16 - caches_action :index, :login
16 + # COMMENT OUT, only need when having high load
17 + # caches_action :index, :login
17
18
18 def index
19 def index
19 redirect_to :action => 'login'
20 redirect_to :action => 'login'
@@ -1,18 +1,18
1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
2 <td>
2 <td>
3 - <%= "#{problem_counter + 1}" %>
3 + <%= "#{problem_counter}" %>
4 </td>
4 </td>
5 <td>
5 <td>
6 <%= "#{problem.full_name} (#{problem.name})" %>
6 <%= "#{problem.full_name} (#{problem.name})" %>
7 <%= link_to '[desc]', problem.url, :popup => true if (problem.url!=nil) and (problem.url!='') %>
7 <%= link_to '[desc]', problem.url, :popup => true if (problem.url!=nil) and (problem.url!='') %>
8 </td>
8 </td>
9 <td align="center">
9 <td align="center">
10 - <%= @prob_submissions[problem_counter][:count] %>
10 + <%= @prob_submissions[problem_counter-1][:count] %>
11 </td>
11 </td>
12 <td>
12 <td>
13 <%= render :partial => 'submission_short',
13 <%= render :partial => 'submission_short',
14 :locals => {
14 :locals => {
15 - :submission => @prob_submissions[problem_counter][:submission],
15 + :submission => @prob_submissions[problem_counter-1][:submission],
16 :problem_name => problem.name }%>
16 :problem_name => problem.name }%>
17 </td>
17 </td>
18 </tr>
18 </tr>
@@ -1,45 +1,109
1 - # Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
1 + # Don't change this file!
2 + # Configure your app in config/environment.rb and config/environments/*.rb
3 +
4 + RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5 +
6 + module Rails
7 + class << self
8 + def boot!
9 + unless booted?
10 + preinitialize
11 + pick_boot.run
12 + end
13 + end
2
14
3 - unless defined?(RAILS_ROOT)
15 + def booted?
4 - root_path = File.join(File.dirname(__FILE__), '..')
16 + defined? Rails::Initializer
17 + end
18 +
19 + def pick_boot
20 + (vendor_rails? ? VendorBoot : GemBoot).new
21 + end
5
22
6 - unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
23 + def vendor_rails?
7 - require 'pathname'
24 + File.exist?("#{RAILS_ROOT}/vendor/rails")
8 - root_path = Pathname.new(root_path).cleanpath(true).to_s
25 + end
26 +
27 + def preinitialize
28 + load(preinitializer_path) if File.exist?(preinitializer_path)
29 + end
30 +
31 + def preinitializer_path
32 + "#{RAILS_ROOT}/config/preinitializer.rb"
33 + end
9 end
34 end
10
35
11 - RAILS_ROOT = root_path
36 + class Boot
12 - end
37 + def run
13 -
38 + load_initializer
14 - unless defined?(Rails::Initializer)
39 + Rails::Initializer.run(:set_load_path)
15 - if File.directory?("#{RAILS_ROOT}/vendor/rails")
40 + end
16 - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
41 + end
17 - else
18 - require 'rubygems'
19 -
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
42
24 - if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version
43 + class VendorBoot < Boot
25 - # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems
44 + def load_initializer
26 - rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last
45 + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
27 -
46 + Rails::Initializer.run(:install_gem_spec_stubs)
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
37 - end
38 - else
39 - gem "rails"
40 - require 'initializer'
41 end
47 end
42 end
48 end
43
49
44 - Rails::Initializer.run(:set_load_path)
50 + class GemBoot < Boot
51 + def load_initializer
52 + self.class.load_rubygems
53 + load_rails_gem
54 + require 'initializer'
55 + end
56 +
57 + def load_rails_gem
58 + if version = self.class.gem_version
59 + gem 'rails', version
60 + else
61 + gem 'rails'
62 + end
63 + rescue Gem::LoadError => load_error
64 + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
65 + exit 1
66 + end
67 +
68 + class << self
69 + def rubygems_version
70 + Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
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
45 end
106 end
107 +
108 + # All that for this:
109 + Rails.boot!
This diff has been collapsed as it changes many lines, (840 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)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 - // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
2 + // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 - // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
3 + // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
4 // Contributors:
4 // Contributors:
5 // Richard Livsey
5 // Richard Livsey
6 // Rahul Bhargava
6 // Rahul Bhargava
@@ -37,22 +37,23
37 if(typeof Effect == 'undefined')
37 if(typeof Effect == 'undefined')
38 throw("controls.js requires including script.aculo.us' effects.js library");
38 throw("controls.js requires including script.aculo.us' effects.js library");
39
39
40 - var Autocompleter = {}
40 + var Autocompleter = { }
41 - Autocompleter.Base = function() {};
41 + Autocompleter.Base = Class.create({
42 - Autocompleter.Base.prototype = {
43 baseInitialize: function(element, update, options) {
42 baseInitialize: function(element, update, options) {
44 - this.element = $(element);
43 + element = $(element)
44 + this.element = element;
45 this.update = $(update);
45 this.update = $(update);
46 this.hasFocus = false;
46 this.hasFocus = false;
47 this.changed = false;
47 this.changed = false;
48 this.active = false;
48 this.active = false;
49 this.index = 0;
49 this.index = 0;
50 this.entryCount = 0;
50 this.entryCount = 0;
51 + this.oldElementValue = this.element.value;
51
52
52 if(this.setOptions)
53 if(this.setOptions)
53 this.setOptions(options);
54 this.setOptions(options);
54 else
55 else
55 - this.options = options || {};
56 + this.options = options || { };
56
57
57 this.options.paramName = this.options.paramName || this.element.name;
58 this.options.paramName = this.options.paramName || this.element.name;
58 this.options.tokens = this.options.tokens || [];
59 this.options.tokens = this.options.tokens || [];
@@ -74,6 +75,9
74
75
75 if(typeof(this.options.tokens) == 'string')
76 if(typeof(this.options.tokens) == 'string')
76 this.options.tokens = new Array(this.options.tokens);
77 this.options.tokens = new Array(this.options.tokens);
78 + // Force carriage returns as token delimiters anyway
79 + if (!this.options.tokens.include('\n'))
80 + this.options.tokens.push('\n');
77
81
78 this.observer = null;
82 this.observer = null;
79
83
@@ -81,15 +85,14
81
85
82 Element.hide(this.update);
86 Element.hide(this.update);
83
87
84 - Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
88 + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
85 - Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
89 + Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86 },
90 },
87
91
88 show: function() {
92 show: function() {
89 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
93 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90 if(!this.iefix &&
94 if(!this.iefix &&
91 - (navigator.appVersion.indexOf('MSIE')>0) &&
95 + (Prototype.Browser.IE) &&
92 - (navigator.userAgent.indexOf('Opera')<0) &&
93 (Element.getStyle(this.update, 'position')=='absolute')) {
96 (Element.getStyle(this.update, 'position')=='absolute')) {
94 new Insertion.After(this.update,
97 new Insertion.After(this.update,
95 '<iframe id="' + this.update.id + '_iefix" '+
98 '<iframe id="' + this.update.id + '_iefix" '+
@@ -139,17 +142,17
139 case Event.KEY_UP:
142 case Event.KEY_UP:
140 this.markPrevious();
143 this.markPrevious();
141 this.render();
144 this.render();
142 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145 + Event.stop(event);
143 return;
146 return;
144 case Event.KEY_DOWN:
147 case Event.KEY_DOWN:
145 this.markNext();
148 this.markNext();
146 this.render();
149 this.render();
147 - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150 + Event.stop(event);
148 return;
151 return;
149 }
152 }
150 else
153 else
151 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
154 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152 - (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
155 + (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
153
156
154 this.changed = true;
157 this.changed = true;
155 this.hasFocus = true;
158 this.hasFocus = true;
@@ -195,7 +198,6
195 this.index==i ?
198 this.index==i ?
196 Element.addClassName(this.getEntry(i),"selected") :
199 Element.addClassName(this.getEntry(i),"selected") :
197 Element.removeClassName(this.getEntry(i),"selected");
200 Element.removeClassName(this.getEntry(i),"selected");
198 -
199 if(this.hasFocus) {
201 if(this.hasFocus) {
200 this.show();
202 this.show();
201 this.active = true;
203 this.active = true;
@@ -238,21 +240,22
238 }
240 }
239 var value = '';
241 var value = '';
240 if (this.options.select) {
242 if (this.options.select) {
241 - var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243 + var nodes = $(selectedElement).select('.' + this.options.select) || [];
242 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
244 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243 } else
245 } else
244 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
246 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
247
246 - var lastTokenPos = this.findLastToken();
248 + var bounds = this.getTokenBounds();
247 - if (lastTokenPos != -1) {
249 + if (bounds[0] != -1) {
248 - var newValue = this.element.value.substr(0, lastTokenPos + 1);
250 + var newValue = this.element.value.substr(0, bounds[0]);
249 - var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
251 + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250 if (whitespace)
252 if (whitespace)
251 newValue += whitespace[0];
253 newValue += whitespace[0];
252 - this.element.value = newValue + value;
254 + this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253 } else {
255 } else {
254 this.element.value = value;
256 this.element.value = value;
255 }
257 }
258 + this.oldElementValue = this.element.value;
256 this.element.focus();
259 this.element.focus();
257
260
258 if (this.options.afterUpdateElement)
261 if (this.options.afterUpdateElement)
@@ -296,39 +299,48
296
299
297 onObserverEvent: function() {
300 onObserverEvent: function() {
298 this.changed = false;
301 this.changed = false;
302 + this.tokenBounds = null;
299 if(this.getToken().length>=this.options.minChars) {
303 if(this.getToken().length>=this.options.minChars) {
300 - this.startIndicator();
301 this.getUpdatedChoices();
304 this.getUpdatedChoices();
302 } else {
305 } else {
303 this.active = false;
306 this.active = false;
304 this.hide();
307 this.hide();
305 }
308 }
309 + this.oldElementValue = this.element.value;
306 },
310 },
307
311
308 getToken: function() {
312 getToken: function() {
309 - var tokenPos = this.findLastToken();
313 + var bounds = this.getTokenBounds();
310 - if (tokenPos != -1)
314 + return this.element.value.substring(bounds[0], bounds[1]).strip();
311 - var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
312 - else
313 - var ret = this.element.value;
314 -
315 - return /\n/.test(ret) ? '' : ret;
316 },
315 },
317
316
318 - findLastToken: function() {
317 + getTokenBounds: function() {
319 - var lastTokenPos = -1;
318 + if (null != this.tokenBounds) return this.tokenBounds;
319 + var value = this.element.value;
320 + if (value.strip().empty()) return [-1, 0];
321 + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
322 + var offset = (diff == this.oldElementValue.length ? 1 : 0);
323 + var prevTokenPos = -1, nextTokenPos = value.length;
324 + var tp;
325 + for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
326 + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
327 + if (tp > prevTokenPos) prevTokenPos = tp;
328 + tp = value.indexOf(this.options.tokens[index], diff + offset);
329 + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
330 + }
331 + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
332 + }
333 + });
320
334
321 - for (var i=0; i<this.options.tokens.length; i++) {
335 + Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
322 - var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
336 + var boundary = Math.min(newS.length, oldS.length);
323 - if (thisTokenPos > lastTokenPos)
337 + for (var index = 0; index < boundary; ++index)
324 - lastTokenPos = thisTokenPos;
338 + if (newS[index] != oldS[index])
325 - }
339 + return index;
326 - return lastTokenPos;
340 + return boundary;
327 - }
341 + };
328 - }
329
342
330 - Ajax.Autocompleter = Class.create();
343 + Ajax.Autocompleter = Class.create(Autocompleter.Base, {
331 - Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
332 initialize: function(element, update, url, options) {
344 initialize: function(element, update, url, options) {
333 this.baseInitialize(element, update, options);
345 this.baseInitialize(element, update, options);
334 this.options.asynchronous = true;
346 this.options.asynchronous = true;
@@ -338,7 +350,9
338 },
350 },
339
351
340 getUpdatedChoices: function() {
352 getUpdatedChoices: function() {
341 - entry = encodeURIComponent(this.options.paramName) + '=' +
353 + this.startIndicator();
354 +
355 + var entry = encodeURIComponent(this.options.paramName) + '=' +
342 encodeURIComponent(this.getToken());
356 encodeURIComponent(this.getToken());
343
357
344 this.options.parameters = this.options.callback ?
358 this.options.parameters = this.options.callback ?
@@ -346,14 +360,13
346
360
347 if(this.options.defaultParams)
361 if(this.options.defaultParams)
348 this.options.parameters += '&' + this.options.defaultParams;
362 this.options.parameters += '&' + this.options.defaultParams;
349 -
363 +
350 new Ajax.Request(this.url, this.options);
364 new Ajax.Request(this.url, this.options);
351 },
365 },
352
366
353 onComplete: function(request) {
367 onComplete: function(request) {
354 this.updateChoices(request.responseText);
368 this.updateChoices(request.responseText);
355 }
369 }
356 -
357 });
370 });
358
371
359 // The local array autocompleter. Used when you'd prefer to
372 // The local array autocompleter. Used when you'd prefer to
@@ -391,8 +404,7
391 // In that case, the other options above will not apply unless
404 // In that case, the other options above will not apply unless
392 // you support them.
405 // you support them.
393
406
394 - Autocompleter.Local = Class.create();
407 + Autocompleter.Local = Class.create(Autocompleter.Base, {
395 - Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
396 initialize: function(element, update, array, options) {
408 initialize: function(element, update, array, options) {
397 this.baseInitialize(element, update, options);
409 this.baseInitialize(element, update, options);
398 this.options.array = array;
410 this.options.array = array;
@@ -448,13 +460,12
448 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
460 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
449 return "<ul>" + ret.join('') + "</ul>";
461 return "<ul>" + ret.join('') + "</ul>";
450 }
462 }
451 - }, options || {});
463 + }, options || { });
452 }
464 }
453 });
465 });
454
466
455 - // AJAX in-place editor
467 + // AJAX in-place editor and collection editor
456 - //
468 + // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
457 - // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
458
469
459 // Use this if you notice weird scrolling problems on some browsers,
470 // Use this if you notice weird scrolling problems on some browsers,
460 // the DOM might be a bit confused when this gets called so do this
471 // the DOM might be a bit confused when this gets called so do this
@@ -465,353 +476,472
465 }, 1);
476 }, 1);
466 }
477 }
467
478
468 - Ajax.InPlaceEditor = Class.create();
479 + Ajax.InPlaceEditor = Class.create({
469 - Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470 - Ajax.InPlaceEditor.prototype = {
471 initialize: function(element, url, options) {
480 initialize: function(element, url, options) {
472 this.url = url;
481 this.url = url;
473 - this.element = $(element);
482 + this.element = element = $(element);
474 -
483 + this.prepareOptions();
475 - this.options = Object.extend({
484 + this._controls = { };
476 - paramName: "value",
485 + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
477 - okButton: true,
486 + Object.extend(this.options, options || { });
478 - okText: "ok",
487 + if (!this.options.formId && this.element.id) {
479 - cancelLink: true,
488 + this.options.formId = this.element.id + '-inplaceeditor';
480 - cancelText: "cancel",
489 + if ($(this.options.formId))
481 - savingText: "Saving...",
490 + this.options.formId = '';
482 - clickToEditText: "Click to edit",
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);
493 - },
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 }
491 }
514 -
492 + if (this.options.externalControl)
515 - if (this.options.externalControl) {
516 this.options.externalControl = $(this.options.externalControl);
493 this.options.externalControl = $(this.options.externalControl);
517 - }
494 + if (!this.options.externalControl)
518 -
495 + this.options.externalControlOnly = false;
519 - this.originalBackground = Element.getStyle(this.element, 'background-color');
496 + this._originalBackground = this.element.getStyle('background-color') || 'transparent';
520 - if (!this.originalBackground) {
521 - this.originalBackground = "transparent";
522 - }
523 -
524 this.element.title = this.options.clickToEditText;
497 this.element.title = this.options.clickToEditText;
525 -
498 + this._boundCancelHandler = this.handleFormCancellation.bind(this);
526 - this.onclickListener = this.enterEditMode.bindAsEventListener(this);
499 + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
527 - this.mouseoverListener = this.enterHover.bindAsEventListener(this);
500 + this._boundFailureHandler = this.handleAJAXFailure.bind(this);
528 - this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
501 + this._boundSubmitHandler = this.handleFormSubmission.bind(this);
529 - Event.observe(this.element, 'click', this.onclickListener);
502 + this._boundWrapperHandler = this.wrapUp.bind(this);
530 - Event.observe(this.element, 'mouseover', this.mouseoverListener);
503 + this.registerListeners();
531 - Event.observe(this.element, 'mouseout', this.mouseoutListener);
504 + },
532 - if (this.options.externalControl) {
505 + checkForEscapeOrReturn: function(e) {
533 - Event.observe(this.options.externalControl, 'click', this.onclickListener);
506 + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
534 - Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
507 + if (Event.KEY_ESC == e.keyCode)
535 - Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
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) {
536 + createEditField: function() {
539 - if (this.saving) return;
537 + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
540 - if (this.editing) return;
538 + var fld;
541 - this.editing = true;
539 + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
542 - this.onEnterEditMode();
540 + fld = document.createElement('input');
543 - if (this.options.externalControl) {
541 + fld.type = 'text';
544 - Element.hide(this.options.externalControl);
542 + var size = this.options.size || this.options.cols || 0;
543 + if (0 < size) fld.size = size;
544 + } else {
545 + fld = document.createElement('textarea');
546 + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
547 + fld.cols = this.options.cols || 40;
545 }
548 }
546 - Element.hide(this.element);
549 + fld.name = this.options.paramName;
547 - this.createForm();
550 + fld.value = text; // No HTML breaks conversion anymore
548 - this.element.parentNode.insertBefore(this.form, this.element);
551 + fld.className = 'editor_field';
549 - if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
552 + if (this.options.submitOnBlur)
550 - // stop the event to avoid a page refresh in Safari
553 + fld.onblur = this._boundSubmitHandler;
551 - if (evt) {
554 + this._controls.editor = fld;
552 - Event.stop(evt);
555 + if (this.options.loadTextURL)
553 - }
556 + this.loadExternalText();
554 - return false;
557 + this._form.appendChild(this._controls.editor);
555 },
558 },
556 createForm: function() {
559 createForm: function() {
557 - this.form = document.createElement("form");
560 + var ipe = this;
558 - this.form.id = this.options.formId;
561 + function addText(mode, condition) {
559 - Element.addClassName(this.form, this.options.formClassName)
562 + var text = ipe.options['text' + mode + 'Controls'];
560 - this.form.onsubmit = this.onSubmit.bind(this);
563 + if (!text || condition === false) return;
561 -
564 + ipe._form.appendChild(document.createTextNode(text));
565 + };
566 + this._form = $(document.createElement('form'));
567 + this._form.id = this.options.formId;
568 + this._form.addClassName(this.options.formClassName);
569 + this._form.onsubmit = this._boundSubmitHandler;
562 this.createEditField();
570 this.createEditField();
563 -
571 + if ('textarea' == this._controls.editor.tagName.toLowerCase())
564 - if (this.options.textarea) {
572 + this._form.appendChild(document.createElement('br'));
565 - var br = document.createElement("br");
573 + if (this.options.onFormCustomization)
566 - this.form.appendChild(br);
574 + this.options.onFormCustomization(this, this._form);
567 - }
575 + addText('Before', this.options.okControl || this.options.cancelControl);
568 -
576 + this.createControl('ok', this._boundSubmitHandler);
569 - if (this.options.okButton) {
577 + addText('Between', this.options.okControl && this.options.cancelControl);
570 - okButton = document.createElement("input");
578 + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
571 - okButton.type = "submit";
579 + addText('After', this.options.okControl || this.options.cancelControl);
572 - okButton.value = this.options.okText;
573 - okButton.className = 'editor_ok_button';
574 - this.form.appendChild(okButton);
575 - }
576 -
577 - if (this.options.cancelLink) {
578 - cancelLink = document.createElement("a");
579 - cancelLink.href = "#";
580 - cancelLink.appendChild(document.createTextNode(this.options.cancelText));
581 - cancelLink.onclick = this.onclickCancel.bind(this);
582 - cancelLink.className = 'editor_cancel';
583 - this.form.appendChild(cancelLink);
584 - }
585 - },
586 - hasHTMLLineBreaks: function(string) {
587 - if (!this.options.handleLineBreaks) return false;
588 - return string.match(/<br/i) || string.match(/<p>/i);
589 - },
590 - convertHTMLLineBreaks: function(string) {
591 - return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
592 },
580 },
593 - createEditField: function() {
581 + destroy: function() {
594 - var text;
582 + if (this._oldInnerHTML)
595 - if(this.options.loadTextURL) {
583 + this.element.innerHTML = this._oldInnerHTML;
596 - text = this.options.loadingText;
584 + this.leaveEditMode();
597 - } else {
585 + this.unregisterListeners();
598 - text = this.getText();
586 + },
599 - }
587 + enterEditMode: function(e) {
600 -
588 + if (this._saving || this._editing) return;
601 - var obj = this;
589 + this._editing = true;
602 -
590 + this.triggerCallback('onEnterEditMode');
603 - if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
591 + if (this.options.externalControl)
604 - this.options.textarea = false;
592 + this.options.externalControl.hide();
605 - var textField = document.createElement("input");
593 + this.element.hide();
606 - textField.obj = this;
594 + this.createForm();
607 - textField.type = "text";
595 + this.element.parentNode.insertBefore(this._form, this.element);
608 - textField.name = this.options.paramName;
596 + if (!this.options.loadTextURL)
609 - textField.value = text;
597 + this.postProcessEditField();
610 - textField.style.backgroundColor = this.options.highlightcolor;
598 + if (e) Event.stop(e);
611 - textField.className = 'editor_field';
599 + },
612 - var size = this.options.size || this.options.cols || 0;
600 + enterHover: function(e) {
613 - if (size != 0) textField.size = size;
601 + if (this.options.hoverClassName)
614 - if (this.options.submitOnBlur)
602 + this.element.addClassName(this.options.hoverClassName);
615 - textField.onblur = this.onSubmit.bind(this);
603 + if (this._saving) return;
616 - this.editField = textField;
604 + this.triggerCallback('onEnterHover');
617 - } else {
618 - this.options.textarea = true;
619 - var textArea = document.createElement("textarea");
620 - textArea.obj = this;
621 - textArea.name = this.options.paramName;
622 - textArea.value = this.convertHTMLLineBreaks(text);
623 - textArea.rows = this.options.rows;
624 - textArea.cols = this.options.cols || 40;
625 - textArea.className = 'editor_field';
626 - if (this.options.submitOnBlur)
627 - textArea.onblur = this.onSubmit.bind(this);
628 - this.editField = textArea;
629 - }
630 -
631 - if(this.options.loadTextURL) {
632 - this.loadExternalText();
633 - }
634 - this.form.appendChild(this.editField);
635 },
605 },
636 getText: function() {
606 getText: function() {
637 return this.element.innerHTML;
607 return this.element.innerHTML;
638 },
608 },
639 - loadExternalText: function() {
609 + handleAJAXFailure: function(transport) {
640 - Element.addClassName(this.form, this.options.loadingClassName);
610 + this.triggerCallback('onFailure', transport);
641 - this.editField.disabled = true;
611 + if (this._oldInnerHTML) {
642 - new Ajax.Request(
612 + this.element.innerHTML = this._oldInnerHTML;
643 - this.options.loadTextURL,
613 + this._oldInnerHTML = null;
644 - Object.extend({
614 + }
645 - asynchronous: true,
615 + },
646 - onComplete: this.onLoadedExternalText.bind(this)
616 + handleFormCancellation: function(e) {
647 - }, this.options.ajaxOptions)
617 + this.wrapUp();
648 - );
618 + if (e) Event.stop(e);
649 },
619 },
650 - onLoadedExternalText: function(transport) {
620 + handleFormSubmission: function(e) {
651 - Element.removeClassName(this.form, this.options.loadingClassName);
621 + var form = this._form;
652 - this.editField.disabled = false;
622 + var value = $F(this._controls.editor);
653 - this.editField.value = transport.responseText.stripTags();
623 + this.prepareSubmission();
654 - Field.scrollFreeActivate(this.editField);
624 + var params = this.options.callback(form, value) || '';
655 - },
625 + if (Object.isString(params))
656 - onclickCancel: function() {
626 + params = params.toQueryParams();
657 - this.onComplete();
627 + params.editorId = this.element.id;
658 - this.leaveEditMode();
628 + if (this.options.htmlResponse) {
659 - return false;
629 + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
660 - },
630 + Object.extend(options, {
661 - onFailure: function(transport) {
631 + parameters: params,
662 - this.options.onFailure(transport);
632 + onComplete: this._boundWrapperHandler,
663 - if (this.oldInnerHTML) {
633 + onFailure: this._boundFailureHandler
664 - this.element.innerHTML = this.oldInnerHTML;
634 + });
665 - this.oldInnerHTML = null;
635 + new Ajax.Updater({ success: this.element }, this.url, options);
636 + } else {
637 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
638 + Object.extend(options, {
639 + parameters: params,
640 + onComplete: this._boundWrapperHandler,
641 + onFailure: this._boundFailureHandler
642 + });
643 + new Ajax.Request(this.url, options);
666 }
644 }
667 - return false;
645 + if (e) Event.stop(e);
668 },
646 },
669 - onSubmit: function() {
647 + leaveEditMode: function() {
670 - // onLoading resets these so we need to save them away for the Ajax call
648 + this.element.removeClassName(this.options.savingClassName);
671 - var form = this.form;
649 + this.removeForm();
672 - var value = this.editField.value;
650 + this.leaveHover();
673 -
651 + this.element.style.backgroundColor = this._originalBackground;
674 - // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
652 + this.element.show();
675 - // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
653 + if (this.options.externalControl)
676 - // to be displayed indefinitely
654 + this.options.externalControl.show();
677 - this.onLoading();
655 + this._saving = false;
678 -
656 + this._editing = false;
679 - if (this.options.evalScripts) {
657 + this._oldInnerHTML = null;
680 - new Ajax.Request(
658 + this.triggerCallback('onLeaveEditMode');
681 - this.url, Object.extend({
659 + },
682 - parameters: this.options.callback(form, value),
660 + leaveHover: function(e) {
683 - onComplete: this.onComplete.bind(this),
661 + if (this.options.hoverClassName)
684 - onFailure: this.onFailure.bind(this),
662 + this.element.removeClassName(this.options.hoverClassName);
685 - asynchronous:true,
663 + if (this._saving) return;
686 - evalScripts:true
664 + this.triggerCallback('onLeaveHover');
687 - }, this.options.ajaxOptions));
688 - } else {
689 - new Ajax.Updater(
690 - { success: this.element,
691 - // don't update on failure (this could be an option)
692 - failure: null },
693 - this.url, Object.extend({
694 - parameters: this.options.callback(form, value),
695 - onComplete: this.onComplete.bind(this),
696 - onFailure: this.onFailure.bind(this)
697 - }, this.options.ajaxOptions));
698 - }
699 - // stop the event to avoid a page refresh in Safari
700 - if (arguments.length > 1) {
701 - Event.stop(arguments[0]);
702 - }
703 - return false;
704 },
665 },
705 - onLoading: function() {
666 + loadExternalText: function() {
706 - this.saving = true;
667 + this._form.addClassName(this.options.loadingClassName);
668 + this._controls.editor.disabled = true;
669 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
670 + Object.extend(options, {
671 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
672 + onComplete: Prototype.emptyFunction,
673 + onSuccess: function(transport) {
674 + this._form.removeClassName(this.options.loadingClassName);
675 + var text = transport.responseText;
676 + if (this.options.stripLoadedTextTags)
677 + text = text.stripTags();
678 + this._controls.editor.value = text;
679 + this._controls.editor.disabled = false;
680 + this.postProcessEditField();
681 + }.bind(this),
682 + onFailure: this._boundFailureHandler
683 + });
684 + new Ajax.Request(this.options.loadTextURL, options);
685 + },
686 + postProcessEditField: function() {
687 + var fpc = this.options.fieldPostCreation;
688 + if (fpc)
689 + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
690 + },
691 + prepareOptions: function() {
692 + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
693 + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
694 + [this._extraDefaultOptions].flatten().compact().each(function(defs) {
695 + Object.extend(this.options, defs);
696 + }.bind(this));
697 + },
698 + prepareSubmission: function() {
699 + this._saving = true;
707 this.removeForm();
700 this.removeForm();
708 this.leaveHover();
701 this.leaveHover();
709 this.showSaving();
702 this.showSaving();
710 },
703 },
711 - showSaving: function() {
704 + registerListeners: function() {
712 - this.oldInnerHTML = this.element.innerHTML;
705 + this._listeners = { };
713 - this.element.innerHTML = this.options.savingText;
706 + var listener;
714 - Element.addClassName(this.element, this.options.savingClassName);
707 + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
715 - this.element.style.backgroundColor = this.originalBackground;
708 + listener = this[pair.value].bind(this);
716 - Element.show(this.element);
709 + this._listeners[pair.key] = listener;
710 + if (!this.options.externalControlOnly)
711 + this.element.observe(pair.key, listener);
712 + if (this.options.externalControl)
713 + this.options.externalControl.observe(pair.key, listener);
714 + }.bind(this));
717 },
715 },
718 removeForm: function() {
716 removeForm: function() {
719 - if(this.form) {
717 + if (!this._form) return;
720 - if (this.form.parentNode) Element.remove(this.form);
718 + this._form.remove();
721 - this.form = null;
719 + this._form = null;
720 + this._controls = { };
721 + },
722 + showSaving: function() {
723 + this._oldInnerHTML = this.element.innerHTML;
724 + this.element.innerHTML = this.options.savingText;
725 + this.element.addClassName(this.options.savingClassName);
726 + this.element.style.backgroundColor = this._originalBackground;
727 + this.element.show();
728 + },
729 + triggerCallback: function(cbName, arg) {
730 + if ('function' == typeof this.options[cbName]) {
731 + this.options[cbName](this, arg);
722 }
732 }
723 },
733 },
724 - enterHover: function() {
734 + unregisterListeners: function() {
725 - if (this.saving) return;
735 + $H(this._listeners).each(function(pair) {
726 - this.element.style.backgroundColor = this.options.highlightcolor;
736 + if (!this.options.externalControlOnly)
727 - if (this.effect) {
737 + this.element.stopObserving(pair.key, pair.value);
728 - this.effect.cancel();
738 + if (this.options.externalControl)
729 - }
739 + this.options.externalControl.stopObserving(pair.key, pair.value);
730 - Element.addClassName(this.element, this.options.hoverClassName)
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);
747 + }
748 + });
749 +
750 + Object.extend(Ajax.InPlaceEditor.prototype, {
751 + dispose: Ajax.InPlaceEditor.prototype.destroy
752 + });
753 +
754 + Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
755 + initialize: function($super, element, url, options) {
756 + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
757 + $super(element, url, options);
731 },
758 },
732 - leaveHover: function() {
759 +
733 - if (this.options.backgroundColor) {
760 + createEditField: function() {
734 - this.element.style.backgroundColor = this.oldBackground;
761 + var list = document.createElement('select');
735 - }
762 + list.name = this.options.paramName;
736 - Element.removeClassName(this.element, this.options.hoverClassName)
763 + list.size = 1;
737 - if (this.saving) return;
764 + this._controls.editor = list;
738 - this.effect = new Effect.Highlight(this.element, {
765 + this._collection = this.options.collection || [];
739 - startcolor: this.options.highlightcolor,
766 + if (this.options.loadCollectionURL)
740 - endcolor: this.options.highlightendcolor,
767 + this.loadCollection();
741 - restorecolor: this.originalBackground
768 + else
769 + this.checkForExternalText();
770 + this._form.appendChild(this._controls.editor);
771 + },
772 +
773 + loadCollection: function() {
774 + this._form.addClassName(this.options.loadingClassName);
775 + this.showLoadingText(this.options.loadingCollectionText);
776 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
777 + Object.extend(options, {
778 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
779 + onComplete: Prototype.emptyFunction,
780 + onSuccess: function(transport) {
781 + var js = transport.responseText.strip();
782 + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
783 + throw 'Server returned an invalid collection representation.';
784 + this._collection = eval(js);
785 + this.checkForExternalText();
786 + }.bind(this),
787 + onFailure: this.onFailure
742 });
788 });
789 + new Ajax.Request(this.options.loadCollectionURL, options);
743 },
790 },
744 - leaveEditMode: function() {
791 +
745 - Element.removeClassName(this.element, this.options.savingClassName);
792 + showLoadingText: function(text) {
746 - this.removeForm();
793 + this._controls.editor.disabled = true;
747 - this.leaveHover();
794 + var tempOption = this._controls.editor.firstChild;
748 - this.element.style.backgroundColor = this.originalBackground;
795 + if (!tempOption) {
749 - Element.show(this.element);
796 + tempOption = document.createElement('option');
750 - if (this.options.externalControl) {
797 + tempOption.value = '';
751 - Element.show(this.options.externalControl);
798 + this._controls.editor.appendChild(tempOption);
799 + tempOption.selected = true;
752 }
800 }
753 - this.editing = false;
801 + tempOption.update((text || '').stripScripts().stripTags());
754 - this.saving = false;
802 + },
755 - this.oldInnerHTML = null;
803 +
756 - this.onLeaveEditMode();
804 + checkForExternalText: function() {
805 + this._text = this.getText();
806 + if (this.options.loadTextURL)
807 + this.loadExternalText();
808 + else
809 + this.buildOptionList();
810 + },
811 +
812 + loadExternalText: function() {
813 + this.showLoadingText(this.options.loadingText);
814 + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
815 + Object.extend(options, {
816 + parameters: 'editorId=' + encodeURIComponent(this.element.id),
817 + onComplete: Prototype.emptyFunction,
818 + onSuccess: function(transport) {
819 + this._text = transport.responseText.strip();
820 + this.buildOptionList();
821 + }.bind(this),
822 + onFailure: this.onFailure
823 + });
824 + new Ajax.Request(this.options.loadTextURL, options);
757 },
825 },
758 - onComplete: function(transport) {
826 +
759 - this.leaveEditMode();
827 + buildOptionList: function() {
760 - this.options.onComplete.bind(this)(transport, this.element);
828 + this._form.removeClassName(this.options.loadingClassName);
761 - },
829 + this._collection = this._collection.map(function(entry) {
762 - onEnterEditMode: function() {},
830 + return 2 === entry.length ? entry : [entry, entry].flatten();
763 - onLeaveEditMode: function() {},
831 + });
764 - dispose: function() {
832 + var marker = ('value' in this.options) ? this.options.value : this._text;
765 - if (this.oldInnerHTML) {
833 + var textFound = this._collection.any(function(entry) {
766 - this.element.innerHTML = this.oldInnerHTML;
834 + return entry[0] == marker;
767 - }
835 + }.bind(this));
768 - this.leaveEditMode();
836 + this._controls.editor.update('');
769 - Event.stopObserving(this.element, 'click', this.onclickListener);
837 + var option;
770 - Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
838 + this._collection.each(function(entry, index) {
771 - Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
839 + option = document.createElement('option');
772 - if (this.options.externalControl) {
840 + option.value = entry[0];
773 - Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
841 + option.selected = textFound ? entry[0] == marker : 0 == index;
774 - Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
842 + option.appendChild(document.createTextNode(entry[1]));
775 - Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
843 + this._controls.editor.appendChild(option);
776 - }
844 + }.bind(this));
845 + this._controls.editor.disabled = false;
846 + Field.scrollFreeActivate(this._controls.editor);
777 }
847 }
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();
869 + Object.extend(Ajax.InPlaceEditor, {
781 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
870 + DefaultOptions: {
782 - Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
871 + ajaxOptions: { },
783 - createEditField: function() {
872 + autoRows: 3, // Use when multi-line w/ rows == 1
784 - if (!this.cached_selectTag) {
873 + cancelControl: 'link', // 'link'|'button'|false
785 - var selectTag = document.createElement("select");
874 + cancelText: 'cancel',
786 - var collection = this.options.collection || [];
875 + clickToEditText: 'Click to edit',
787 - var optionTag;
876 + externalControl: null, // id|elt
788 - collection.each(function(e,i) {
877 + externalControlOnly: false,
789 - optionTag = document.createElement("option");
878 + fieldPostCreation: 'activate', // 'activate'|'focus'|false
790 - optionTag.value = (e instanceof Array) ? e[0] : e;
879 + formClassName: 'inplaceeditor-form',
791 - if((typeof this.options.value == 'undefined') &&
880 + formId: null, // id|elt
792 - ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
881 + highlightColor: '#ffff99',
793 - if(this.options.value==optionTag.value) optionTag.selected = true;
882 + highlightEndColor: '#ffffff',
794 - optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
883 + hoverClassName: '',
795 - selectTag.appendChild(optionTag);
884 + htmlResponse: true,
796 - }.bind(this));
885 + loadingClassName: 'inplaceeditor-loading',
797 - this.cached_selectTag = selectTag;
886 + loadingText: 'Loading...',
887 + okControl: 'button', // 'link'|'button'|false
888 + okText: 'ok',
889 + paramName: 'value',
890 + rows: 1, // If 1 and multi-line, uses autoRows
891 + savingClassName: 'inplaceeditor-saving',
892 + savingText: 'Saving...',
893 + size: 0,
894 + stripLoadedTextTags: false,
895 + submitOnBlur: false,
896 + textAfterControls: '',
897 + textBeforeControls: '',
898 + textBetweenControls: ''
899 + },
900 + DefaultCallbacks: {
901 + callback: function(form) {
902 + return Form.serialize(form);
903 + },
904 + onComplete: function(transport, element) {
905 + // For backward compatibility, this one is bound to the IPE, and passes
906 + // the element directly. It was too often customized, so we don't break it.
907 + new Effect.Highlight(element, {
908 + startcolor: this.options.highlightColor, keepBackgroundImage: true });
909 + },
910 + onEnterEditMode: null,
911 + onEnterHover: function(ipe) {
912 + ipe.element.style.backgroundColor = ipe.options.highlightColor;
913 + if (ipe._effect)
914 + ipe._effect.cancel();
915 + },
916 + onFailure: function(transport, ipe) {
917 + alert('Error communication with the server: ' + transport.responseText.stripTags());
918 + },
919 + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
920 + onLeaveEditMode: null,
921 + onLeaveHover: function(ipe) {
922 + ipe._effect = new Effect.Highlight(ipe.element, {
923 + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
924 + restorecolor: ipe._originalBackground, keepBackgroundImage: true
925 + });
798 }
926 }
799 -
927 + },
800 - this.editField = this.cached_selectTag;
928 + Listeners: {
801 - if(this.options.loadTextURL) this.loadExternalText();
929 + click: 'enterEditMode',
802 - this.form.appendChild(this.editField);
930 + keydown: 'checkForEscapeOrReturn',
803 - this.options.callback = function(form, value) {
931 + mouseover: 'enterHover',
804 - return "value=" + encodeURIComponent(value);
932 + mouseout: 'leaveHover'
805 - }
806 }
933 }
807 });
934 });
808
935
936 + Ajax.InPlaceCollectionEditor.DefaultOptions = {
937 + loadingCollectionText: 'Loading options...'
938 + };
939 +
809 // Delayed observer, like Form.Element.Observer,
940 // Delayed observer, like Form.Element.Observer,
810 // but waits for delay after last key input
941 // but waits for delay after last key input
811 // Ideal for live-search fields
942 // Ideal for live-search fields
812
943
813 - Form.Element.DelayedObserver = Class.create();
944 + Form.Element.DelayedObserver = Class.create({
814 - Form.Element.DelayedObserver.prototype = {
815 initialize: function(element, delay, callback) {
945 initialize: function(element, delay, callback) {
816 this.delay = delay || 0.5;
946 this.delay = delay || 0.5;
817 this.element = $(element);
947 this.element = $(element);
@@ -830,4 +960,4
830 this.timer = null;
960 this.timer = null;
831 this.callback(this.element, $F(this.element));
961 this.callback(this.element, $F(this.element));
832 }
962 }
833 - };
963 + });
@@ -1,10 +1,10
1 - // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 + // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 - // (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
2 + // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
3 //
3 //
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
6
6
7 - if(typeof Effect == 'undefined')
7 + if(Object.isUndefined(Effect))
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
9
9
10 var Droppables = {
10 var Droppables = {
@@ -20,14 +20,13
20 greedy: true,
20 greedy: true,
21 hoverclass: null,
21 hoverclass: null,
22 tree: false
22 tree: false
23 - }, arguments[1] || {});
23 + }, arguments[1] || { });
24
24
25 // cache containers
25 // cache containers
26 if(options.containment) {
26 if(options.containment) {
27 options._containers = [];
27 options._containers = [];
28 var containment = options.containment;
28 var containment = options.containment;
29 - if((typeof containment == 'object') &&
29 + if(Object.isArray(containment)) {
30 - (containment.constructor == Array)) {
31 containment.each( function(c) { options._containers.push($(c)) });
30 containment.each( function(c) { options._containers.push($(c)) });
32 } else {
31 } else {
33 options._containers.push($(containment));
32 options._containers.push($(containment));
@@ -87,21 +86,23
87
86
88 show: function(point, element) {
87 show: function(point, element) {
89 if(!this.drops.length) return;
88 if(!this.drops.length) return;
90 - var affected = [];
89 + var drop, affected = [];
91
90
92 - if(this.last_active) this.deactivate(this.last_active);
93 this.drops.each( function(drop) {
91 this.drops.each( function(drop) {
94 if(Droppables.isAffected(point, element, drop))
92 if(Droppables.isAffected(point, element, drop))
95 affected.push(drop);
93 affected.push(drop);
96 });
94 });
97
95
98 - if(affected.length>0) {
96 + if(affected.length>0)
99 drop = Droppables.findDeepestChild(affected);
97 drop = Droppables.findDeepestChild(affected);
98 +
99 + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
100 + if (drop) {
100 Position.within(drop.element, point[0], point[1]);
101 Position.within(drop.element, point[0], point[1]);
101 if(drop.onHover)
102 if(drop.onHover)
102 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103
104
104 - Droppables.activate(drop);
105 + if (drop != this.last_active) Droppables.activate(drop);
105 }
106 }
106 },
107 },
107
108
@@ -110,8 +111,10
110 Position.prepare();
111 Position.prepare();
111
112
112 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 - if (this.last_active.onDrop)
114 + if (this.last_active.onDrop) {
114 - this.last_active.onDrop(element, this.last_active.element, event);
115 + this.last_active.onDrop(element, this.last_active.element, event);
116 + return true;
117 + }
115 },
118 },
116
119
117 reset: function() {
120 reset: function() {
@@ -219,10 +222,7
219
222
220 /*--------------------------------------------------------------------------*/
223 /*--------------------------------------------------------------------------*/
221
224
222 - var Draggable = Class.create();
225 + var Draggable = Class.create({
223 - Draggable._dragging = {};
224 -
225 - Draggable.prototype = {
226 initialize: function(element) {
226 initialize: function(element) {
227 var defaults = {
227 var defaults = {
228 handle: false,
228 handle: false,
@@ -233,7 +233,7
233 });
233 });
234 },
234 },
235 endeffect: function(element) {
235 endeffect: function(element) {
236 - var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
236 + var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238 queue: {scope:'_draggable', position:'end'},
238 queue: {scope:'_draggable', position:'end'},
239 afterFinish: function(){
239 afterFinish: function(){
@@ -243,6 +243,7
243 },
243 },
244 zindex: 1000,
244 zindex: 1000,
245 revert: false,
245 revert: false,
246 + quiet: false,
246 scroll: false,
247 scroll: false,
247 scrollSensitivity: 20,
248 scrollSensitivity: 20,
248 scrollSpeed: 15,
249 scrollSpeed: 15,
@@ -250,7 +251,7
250 delay: 0
251 delay: 0
251 };
252 };
252
253
253 - if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
254 + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
254 Object.extend(defaults, {
255 Object.extend(defaults, {
255 starteffect: function(element) {
256 starteffect: function(element) {
256 element._opacity = Element.getOpacity(element);
257 element._opacity = Element.getOpacity(element);
@@ -259,11 +260,11
259 }
260 }
260 });
261 });
261
262
262 - var options = Object.extend(defaults, arguments[1] || {});
263 + var options = Object.extend(defaults, arguments[1] || { });
263
264
264 this.element = $(element);
265 this.element = $(element);
265
266
266 - if(options.handle && (typeof options.handle == 'string'))
267 + if(options.handle && Object.isString(options.handle))
267 this.handle = this.element.down('.'+options.handle, 0);
268 this.handle = this.element.down('.'+options.handle, 0);
268
269
269 if(!this.handle) this.handle = $(options.handle);
270 if(!this.handle) this.handle = $(options.handle);
@@ -276,7 +277,6
276
277
277 Element.makePositioned(this.element); // fix IE
278 Element.makePositioned(this.element); // fix IE
278
279
279 - this.delta = this.currentDelta();
280 this.options = options;
280 this.options = options;
281 this.dragging = false;
281 this.dragging = false;
282
282
@@ -298,17 +298,17
298 },
298 },
299
299
300 initDrag: function(event) {
300 initDrag: function(event) {
301 - if(typeof Draggable._dragging[this.element] != 'undefined' &&
301 + if(!Object.isUndefined(Draggable._dragging[this.element]) &&
302 Draggable._dragging[this.element]) return;
302 Draggable._dragging[this.element]) return;
303 if(Event.isLeftClick(event)) {
303 if(Event.isLeftClick(event)) {
304 // abort on form elements, fixes a Firefox issue
304 // abort on form elements, fixes a Firefox issue
305 var src = Event.element(event);
305 var src = Event.element(event);
306 - if(src.tagName && (
306 + if((tag_name = src.tagName.toUpperCase()) && (
307 - src.tagName=='INPUT' ||
307 + tag_name=='INPUT' ||
308 - src.tagName=='SELECT' ||
308 + tag_name=='SELECT' ||
309 - src.tagName=='OPTION' ||
309 + tag_name=='OPTION' ||
310 - src.tagName=='BUTTON' ||
310 + tag_name=='BUTTON' ||
311 - src.tagName=='TEXTAREA')) return;
311 + tag_name=='TEXTAREA')) return;
312
312
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
314 var pos = Position.cumulativeOffset(this.element);
314 var pos = Position.cumulativeOffset(this.element);
@@ -321,6 +321,8
321
321
322 startDrag: function(event) {
322 startDrag: function(event) {
323 this.dragging = true;
323 this.dragging = true;
324 + if(!this.delta)
325 + this.delta = this.currentDelta();
324
326
325 if(this.options.zindex) {
327 if(this.options.zindex) {
326 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
328 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
@@ -329,7 +331,9
329
331
330 if(this.options.ghosting) {
332 if(this.options.ghosting) {
331 this._clone = this.element.cloneNode(true);
333 this._clone = this.element.cloneNode(true);
332 - Position.absolutize(this.element);
334 + this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
335 + if (!this.element._originallyAbsolute)
336 + Position.absolutize(this.element);
333 this.element.parentNode.insertBefore(this._clone, this.element);
337 this.element.parentNode.insertBefore(this._clone, this.element);
334 }
338 }
335
339
@@ -351,8 +355,12
351
355
352 updateDrag: function(event, pointer) {
356 updateDrag: function(event, pointer) {
353 if(!this.dragging) this.startDrag(event);
357 if(!this.dragging) this.startDrag(event);
354 - Position.prepare();
358 +
355 - Droppables.show(pointer, this.element);
359 + if(!this.options.quiet){
360 + Position.prepare();
361 + Droppables.show(pointer, this.element);
362 + }
363 +
356 Draggables.notify('onDrag', this, event);
364 Draggables.notify('onDrag', this, event);
357
365
358 this.draw(pointer);
366 this.draw(pointer);
@@ -380,30 +388,44
380 }
388 }
381
389
382 // fix AppleWebKit rendering
390 // fix AppleWebKit rendering
383 - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
391 + if(Prototype.Browser.WebKit) window.scrollBy(0,0);
384
392
385 Event.stop(event);
393 Event.stop(event);
386 },
394 },
387
395
388 finishDrag: function(event, success) {
396 finishDrag: function(event, success) {
389 this.dragging = false;
397 this.dragging = false;
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 + }
390
404
391 if(this.options.ghosting) {
405 if(this.options.ghosting) {
392 - Position.relativize(this.element);
406 + if (!this.element._originallyAbsolute)
407 + Position.relativize(this.element);
408 + delete this.element._originallyAbsolute;
393 Element.remove(this._clone);
409 Element.remove(this._clone);
394 this._clone = null;
410 this._clone = null;
395 }
411 }
396
412
397 - if(success) Droppables.fire(event, this.element);
413 + var dropped = false;
414 + if(success) {
415 + dropped = Droppables.fire(event, this.element);
416 + if (!dropped) dropped = false;
417 + }
418 + if(dropped && this.options.onDropped) this.options.onDropped(this.element);
398 Draggables.notify('onEnd', this, event);
419 Draggables.notify('onEnd', this, event);
399
420
400 var revert = this.options.revert;
421 var revert = this.options.revert;
401 - if(revert && typeof revert == 'function') revert = revert(this.element);
422 + if(revert && Object.isFunction(revert)) revert = revert(this.element);
402
423
403 var d = this.currentDelta();
424 var d = this.currentDelta();
404 if(revert && this.options.reverteffect) {
425 if(revert && this.options.reverteffect) {
405 - this.options.reverteffect(this.element,
426 + if (dropped == 0 || revert != 'failure')
406 - d[1]-this.delta[1], d[0]-this.delta[0]);
427 + this.options.reverteffect(this.element,
428 + d[1]-this.delta[1], d[0]-this.delta[0]);
407 } else {
429 } else {
408 this.delta = d;
430 this.delta = d;
409 }
431 }
@@ -451,15 +473,15
451 }.bind(this));
473 }.bind(this));
452
474
453 if(this.options.snap) {
475 if(this.options.snap) {
454 - if(typeof this.options.snap == 'function') {
476 + if(Object.isFunction(this.options.snap)) {
455 p = this.options.snap(p[0],p[1],this);
477 p = this.options.snap(p[0],p[1],this);
456 } else {
478 } else {
457 - if(this.options.snap instanceof Array) {
479 + if(Object.isArray(this.options.snap)) {
458 p = p.map( function(v, i) {
480 p = p.map( function(v, i) {
459 - return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
481 + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
460 } else {
482 } else {
461 p = p.map( function(v) {
483 p = p.map( function(v) {
462 - return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
484 + return (v/this.options.snap).round()*this.options.snap }.bind(this))
463 }
485 }
464 }}
486 }}
465
487
@@ -543,12 +565,13
543 }
565 }
544 return { top: T, left: L, width: W, height: H };
566 return { top: T, left: L, width: W, height: H };
545 }
567 }
546 - }
568 + });
569 +
570 + Draggable._dragging = { };
547
571
548 /*--------------------------------------------------------------------------*/
572 /*--------------------------------------------------------------------------*/
549
573
550 - var SortableObserver = Class.create();
574 + var SortableObserver = Class.create({
551 - SortableObserver.prototype = {
552 initialize: function(element, observer) {
575 initialize: function(element, observer) {
553 this.element = $(element);
576 this.element = $(element);
554 this.observer = observer;
577 this.observer = observer;
@@ -564,15 +587,15
564 if(this.lastValue != Sortable.serialize(this.element))
587 if(this.lastValue != Sortable.serialize(this.element))
565 this.observer(this.element)
588 this.observer(this.element)
566 }
589 }
567 - }
590 + });
568
591
569 var Sortable = {
592 var Sortable = {
570 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
593 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
571
594
572 - sortables: {},
595 + sortables: { },
573
596
574 _findRootElement: function(element) {
597 _findRootElement: function(element) {
575 - while (element.tagName != "BODY") {
598 + while (element.tagName.toUpperCase() != "BODY") {
576 if(element.id && Sortable.sortables[element.id]) return element;
599 if(element.id && Sortable.sortables[element.id]) return element;
577 element = element.parentNode;
600 element = element.parentNode;
578 }
601 }
@@ -612,13 +635,20
612 delay: 0,
635 delay: 0,
613 hoverclass: null,
636 hoverclass: null,
614 ghosting: false,
637 ghosting: false,
638 + quiet: false,
615 scroll: false,
639 scroll: false,
616 scrollSensitivity: 20,
640 scrollSensitivity: 20,
617 scrollSpeed: 15,
641 scrollSpeed: 15,
618 format: this.SERIALIZE_RULE,
642 format: this.SERIALIZE_RULE,
643 +
644 + // these take arrays of elements or ids and can be
645 + // used for better initialization performance
646 + elements: false,
647 + handles: false,
648 +
619 onChange: Prototype.emptyFunction,
649 onChange: Prototype.emptyFunction,
620 onUpdate: Prototype.emptyFunction
650 onUpdate: Prototype.emptyFunction
621 - }, arguments[1] || {});
651 + }, arguments[1] || { });
622
652
623 // clear any old sortable with same element
653 // clear any old sortable with same element
624 this.destroy(element);
654 this.destroy(element);
@@ -626,6 +656,7
626 // build options for the draggables
656 // build options for the draggables
627 var options_for_draggable = {
657 var options_for_draggable = {
628 revert: true,
658 revert: true,
659 + quiet: options.quiet,
629 scroll: options.scroll,
660 scroll: options.scroll,
630 scrollSpeed: options.scrollSpeed,
661 scrollSpeed: options.scrollSpeed,
631 scrollSensitivity: options.scrollSensitivity,
662 scrollSensitivity: options.scrollSensitivity,
@@ -679,10 +710,9
679 options.droppables.push(element);
710 options.droppables.push(element);
680 }
711 }
681
712
682 - (this.findElements(element, options) || []).each( function(e) {
713 + (options.elements || this.findElements(element, options) || []).each( function(e,i) {
683 - // handles are per-draggable
714 + var handle = options.handles ? $(options.handles[i]) :
684 - var handle = options.handle ?
715 + (options.handle ? $(e).select('.' + options.handle)[0] : e);
685 - $(e).down('.'+options.handle,0) : e;
686 options.draggables.push(
716 options.draggables.push(
687 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
717 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
688 Droppables.add(e, options_for_droppable);
718 Droppables.add(e, options_for_droppable);
@@ -842,7 +872,7
842 only: sortableOptions.only,
872 only: sortableOptions.only,
843 name: element.id,
873 name: element.id,
844 format: sortableOptions.format
874 format: sortableOptions.format
845 - }, arguments[1] || {});
875 + }, arguments[1] || { });
846
876
847 var root = {
877 var root = {
848 id: null,
878 id: null,
@@ -866,7 +896,7
866
896
867 sequence: function(element) {
897 sequence: function(element) {
868 element = $(element);
898 element = $(element);
869 - var options = Object.extend(this.options(element), arguments[1] || {});
899 + var options = Object.extend(this.options(element), arguments[1] || { });
870
900
871 return $(this.findElements(element, options) || []).map( function(item) {
901 return $(this.findElements(element, options) || []).map( function(item) {
872 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
902 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
@@ -875,9 +905,9
875
905
876 setSequence: function(element, new_sequence) {
906 setSequence: function(element, new_sequence) {
877 element = $(element);
907 element = $(element);
878 - var options = Object.extend(this.options(element), arguments[2] || {});
908 + var options = Object.extend(this.options(element), arguments[2] || { });
879
909
880 - var nodeMap = {};
910 + var nodeMap = { };
881 this.findElements(element, options).each( function(n) {
911 this.findElements(element, options).each( function(n) {
882 if (n.id.match(options.format))
912 if (n.id.match(options.format))
883 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
913 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
@@ -895,7 +925,7
895
925
896 serialize: function(element) {
926 serialize: function(element) {
897 element = $(element);
927 element = $(element);
898 - var options = Object.extend(Sortable.options(element), arguments[1] || {});
928 + var options = Object.extend(Sortable.options(element), arguments[1] || { });
899 var name = encodeURIComponent(
929 var name = encodeURIComponent(
900 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
930 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
901
931
@@ -919,7 +949,7
919 return Element.isParent(child.parentNode, element);
949 return Element.isParent(child.parentNode, element);
920 }
950 }
921
951
922 - Element.findChildren = function(element, only, recursive, tagName) {
952 + Element.findChildren = function(element, only, recursive, tagName) {
923 if(!element.hasChildNodes()) return null;
953 if(!element.hasChildNodes()) return null;
924 tagName = tagName.toUpperCase();
954 tagName = tagName.toUpperCase();
925 if(only) only = [only].flatten();
955 if(only) only = [only].flatten();
This diff has been collapsed as it changes many lines, (760 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 // Contributors:
2 // Contributors:
3 // Justin Palmer (http://encytemedia.com/)
3 // Justin Palmer (http://encytemedia.com/)
4 // Mark Pilgrim (http://diveintomark.org/)
4 // Mark Pilgrim (http://diveintomark.org/)
@@ -11,17 +11,17
11 // returns self (or first argument) if not convertable
11 // returns self (or first argument) if not convertable
12 String.prototype.parseColor = function() {
12 String.prototype.parseColor = function() {
13 var color = '#';
13 var color = '#';
14 - if(this.slice(0,4) == 'rgb(') {
14 + if (this.slice(0,4) == 'rgb(') {
15 var cols = this.slice(4,this.length-1).split(',');
15 var cols = this.slice(4,this.length-1).split(',');
16 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
16 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
17 } else {
17 } else {
18 - if(this.slice(0,1) == '#') {
18 + if (this.slice(0,1) == '#') {
19 - if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
19 + if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
20 - if(this.length==7) color = this.toLowerCase();
20 + if (this.length==7) color = this.toLowerCase();
21 }
21 }
22 }
22 }
23 - return(color.length==7 ? color : (arguments[0] || this));
23 + return (color.length==7 ? color : (arguments[0] || this));
24 - }
24 + };
25
25
26 /*--------------------------------------------------------------------------*/
26 /*--------------------------------------------------------------------------*/
27
27
@@ -30,7 +30,7
30 return (node.nodeType==3 ? node.nodeValue :
30 return (node.nodeType==3 ? node.nodeValue :
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
32 }).flatten().join('');
32 }).flatten().join('');
33 - }
33 + };
34
34
35 Element.collectTextNodesIgnoreClass = function(element, className) {
35 Element.collectTextNodesIgnoreClass = function(element, className) {
36 return $A($(element).childNodes).collect( function(node) {
36 return $A($(element).childNodes).collect( function(node) {
@@ -38,47 +38,18
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
40 }).flatten().join('');
40 }).flatten().join('');
41 - }
41 + };
42
42
43 Element.setContentZoom = function(element, percent) {
43 Element.setContentZoom = function(element, percent) {
44 element = $(element);
44 element = $(element);
45 element.setStyle({fontSize: (percent/100) + 'em'});
45 element.setStyle({fontSize: (percent/100) + 'em'});
46 - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
46 + if (Prototype.Browser.WebKit) window.scrollBy(0,0);
47 return element;
47 return element;
48 - }
48 + };
49 -
50 - Element.getOpacity = function(element){
51 - element = $(element);
52 - var opacity;
53 - if (opacity = element.getStyle('opacity'))
54 - return parseFloat(opacity);
55 - if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
56 - if(opacity[1]) return parseFloat(opacity[1]) / 100;
57 - return 1.0;
58 - }
59
49
60 - Element.setOpacity = function(element, value){
50 + Element.getInlineOpacity = function(element){
61 - element= $(element);
62 - if (value == 1){
63 - element.setStyle({ opacity:
64 - (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
65 - 0.999999 : 1.0 });
66 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
67 - element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
68 - } else {
69 - if(value < 0.00001) value = 0;
70 - element.setStyle({opacity: value});
71 - if(/MSIE/.test(navigator.userAgent) && !window.opera)
72 - element.setStyle(
73 - { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
74 - 'alpha(opacity='+value*100+')' });
75 - }
76 - return element;
77 - }
78 -
79 - Element.getInlineOpacity = function(element){
80 return $(element).style.opacity || '';
51 return $(element).style.opacity || '';
81 - }
52 + };
82
53
83 Element.forceRerendering = function(element) {
54 Element.forceRerendering = function(element) {
84 try {
55 try {
@@ -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 var Effect = {
65 var Effect = {
102 _elementDoesNotExistError: {
66 _elementDoesNotExistError: {
103 name: 'ElementDoesNotExistError',
67 name: 'ElementDoesNotExistError',
104 message: 'The specified DOM element does not exist, but is required for this effect to operate'
68 message: 'The specified DOM element does not exist, but is required for this effect to operate'
105 },
69 },
70 + Transitions: {
71 + linear: Prototype.K,
72 + sinoidal: function(pos) {
73 + return (-Math.cos(pos*Math.PI)/2) + 0.5;
74 + },
75 + reverse: function(pos) {
76 + return 1-pos;
77 + },
78 + flicker: function(pos) {
79 + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
80 + return pos > 1 ? 1 : pos;
81 + },
82 + wobble: function(pos) {
83 + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
84 + },
85 + pulse: function(pos, pulses) {
86 + pulses = pulses || 5;
87 + return (
88 + ((pos % (1/pulses)) * pulses).round() == 0 ?
89 + ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
90 + 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
91 + );
92 + },
93 + spring: function(pos) {
94 + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
95 + },
96 + none: function(pos) {
97 + return 0;
98 + },
99 + full: function(pos) {
100 + return 1;
101 + }
102 + },
103 + DefaultOptions: {
104 + duration: 1.0, // seconds
105 + fps: 100, // 100= assume 66fps max.
106 + sync: false, // true for combining
107 + from: 0.0,
108 + to: 1.0,
109 + delay: 0.0,
110 + queue: 'parallel'
111 + },
106 tagifyText: function(element) {
112 tagifyText: function(element) {
107 - if(typeof Builder == 'undefined')
108 - throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109 -
110 var tagifyStyle = 'position:relative';
113 var tagifyStyle = 'position:relative';
111 - if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
114 + if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
112
115
113 element = $(element);
116 element = $(element);
114 $A(element.childNodes).each( function(child) {
117 $A(element.childNodes).each( function(child) {
115 - if(child.nodeType==3) {
118 + if (child.nodeType==3) {
116 child.nodeValue.toArray().each( function(character) {
119 child.nodeValue.toArray().each( function(character) {
117 element.insertBefore(
120 element.insertBefore(
118 - Builder.node('span',{style: tagifyStyle},
121 + new Element('span', {style: tagifyStyle}).update(
119 character == ' ' ? String.fromCharCode(160) : character),
122 character == ' ' ? String.fromCharCode(160) : character),
120 child);
123 child);
121 });
124 });
@@ -125,8 +128,8
125 },
128 },
126 multiple: function(element, effect) {
129 multiple: function(element, effect) {
127 var elements;
130 var elements;
128 - if(((typeof element == 'object') ||
131 + if (((typeof element == 'object') ||
129 - (typeof element == 'function')) &&
132 + Object.isFunction(element)) &&
130 (element.length))
133 (element.length))
131 elements = element;
134 elements = element;
132 else
135 else
@@ -135,7 +138,7
135 var options = Object.extend({
138 var options = Object.extend({
136 speed: 0.1,
139 speed: 0.1,
137 delay: 0.0
140 delay: 0.0
138 - }, arguments[2] || {});
141 + }, arguments[2] || { });
139 var masterDelay = options.delay;
142 var masterDelay = options.delay;
140
143
141 $A(elements).each( function(element, index) {
144 $A(elements).each( function(element, index) {
@@ -152,53 +155,20
152 effect = (effect || 'appear').toLowerCase();
155 effect = (effect || 'appear').toLowerCase();
153 var options = Object.extend({
156 var options = Object.extend({
154 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
157 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
155 - }, arguments[2] || {});
158 + }, arguments[2] || { });
156 Effect[element.visible() ?
159 Effect[element.visible() ?
157 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
160 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
158 }
161 }
159 };
162 };
160
163
161 - var Effect2 = Effect; // deprecated
164 + Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
162 -
163 - /* ------------- transitions ------------- */
164 -
165 - Effect.Transitions = {
166 - linear: Prototype.K,
167 - sinoidal: function(pos) {
168 - return (-Math.cos(pos*Math.PI)/2) + 0.5;
169 - },
170 - reverse: function(pos) {
171 - return 1-pos;
172 - },
173 - flicker: function(pos) {
174 - return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
175 - },
176 - wobble: function(pos) {
177 - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
178 - },
179 - pulse: function(pos, pulses) {
180 - pulses = pulses || 5;
181 - return (
182 - Math.round((pos % (1/pulses)) * pulses) == 0 ?
183 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
184 - 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
185 - );
186 - },
187 - none: function(pos) {
188 - return 0;
189 - },
190 - full: function(pos) {
191 - return 1;
192 - }
193 - };
194
165
195 /* ------------- core effects ------------- */
166 /* ------------- core effects ------------- */
196
167
197 - Effect.ScopedQueue = Class.create();
168 + Effect.ScopedQueue = Class.create(Enumerable, {
198 - Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
199 initialize: function() {
169 initialize: function() {
200 this.effects = [];
170 this.effects = [];
201 - this.interval = null;
171 + this.interval = null;
202 },
172 },
203 _each: function(iterator) {
173 _each: function(iterator) {
204 this.effects._each(iterator);
174 this.effects._each(iterator);
@@ -206,7 +176,7
206 add: function(effect) {
176 add: function(effect) {
207 var timestamp = new Date().getTime();
177 var timestamp = new Date().getTime();
208
178
209 - var position = (typeof effect.options.queue == 'string') ?
179 + var position = Object.isString(effect.options.queue) ?
210 effect.options.queue : effect.options.queue.position;
180 effect.options.queue : effect.options.queue.position;
211
181
212 switch(position) {
182 switch(position) {
@@ -229,115 +199,111
229 effect.startOn += timestamp;
199 effect.startOn += timestamp;
230 effect.finishOn += timestamp;
200 effect.finishOn += timestamp;
231
201
232 - if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
202 + if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
233 this.effects.push(effect);
203 this.effects.push(effect);
234
204
235 - if(!this.interval)
205 + if (!this.interval)
236 - this.interval = setInterval(this.loop.bind(this), 40);
206 + this.interval = setInterval(this.loop.bind(this), 15);
237 },
207 },
238 remove: function(effect) {
208 remove: function(effect) {
239 this.effects = this.effects.reject(function(e) { return e==effect });
209 this.effects = this.effects.reject(function(e) { return e==effect });
240 - if(this.effects.length == 0) {
210 + if (this.effects.length == 0) {
241 clearInterval(this.interval);
211 clearInterval(this.interval);
242 this.interval = null;
212 this.interval = null;
243 }
213 }
244 },
214 },
245 loop: function() {
215 loop: function() {
246 var timePos = new Date().getTime();
216 var timePos = new Date().getTime();
247 - this.effects.invoke('loop', timePos);
217 + for(var i=0, len=this.effects.length;i<len;i++)
218 + this.effects[i] && this.effects[i].loop(timePos);
248 }
219 }
249 });
220 });
250
221
251 Effect.Queues = {
222 Effect.Queues = {
252 instances: $H(),
223 instances: $H(),
253 get: function(queueName) {
224 get: function(queueName) {
254 - if(typeof queueName != 'string') return queueName;
225 + if (!Object.isString(queueName)) return queueName;
255
226
256 - if(!this.instances[queueName])
227 + return this.instances.get(queueName) ||
257 - this.instances[queueName] = new Effect.ScopedQueue();
228 + this.instances.set(queueName, new Effect.ScopedQueue());
258 -
259 - return this.instances[queueName];
260 }
229 }
261 - }
230 + };
262 Effect.Queue = Effect.Queues.get('global');
231 Effect.Queue = Effect.Queues.get('global');
263
232
264 - Effect.DefaultOptions = {
233 + Effect.Base = Class.create({
265 - transition: Effect.Transitions.sinoidal,
266 - duration: 1.0, // seconds
267 - fps: 25.0, // max. 25fps due to Effect.Queue implementation
268 - sync: false, // true for combining
269 - from: 0.0,
270 - to: 1.0,
271 - delay: 0.0,
272 - queue: 'parallel'
273 - }
274 -
275 - Effect.Base = function() {};
276 - Effect.Base.prototype = {
277 position: null,
234 position: null,
278 start: function(options) {
235 start: function(options) {
279 - this.options = Object.extend(Object.extend({},Effect.DefaultOptions), 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;
243 + this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
280 this.currentFrame = 0;
244 this.currentFrame = 0;
281 this.state = 'idle';
245 this.state = 'idle';
282 this.startOn = this.options.delay*1000;
246 this.startOn = this.options.delay*1000;
283 - this.finishOn = this.startOn + (this.options.duration*1000);
247 + this.finishOn = this.startOn+(this.options.duration*1000);
248 + this.fromToDelta = this.options.to-this.options.from;
249 + this.totalTime = this.finishOn-this.startOn;
250 + this.totalFrames = this.options.fps*this.options.duration;
251 +
252 + eval('this.render = function(pos){ '+
253 + 'if (this.state=="idle"){this.state="running";'+
254 + codeForEvent(this.options,'beforeSetup')+
255 + (this.setup ? 'this.setup();':'')+
256 + codeForEvent(this.options,'afterSetup')+
257 + '};if (this.state=="running"){'+
258 + 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
259 + 'this.position=pos;'+
260 + codeForEvent(this.options,'beforeUpdate')+
261 + (this.update ? 'this.update(pos);':'')+
262 + codeForEvent(this.options,'afterUpdate')+
263 + '}}');
264 +
284 this.event('beforeStart');
265 this.event('beforeStart');
285 - if(!this.options.sync)
266 + if (!this.options.sync)
286 - Effect.Queues.get(typeof this.options.queue == 'string' ?
267 + Effect.Queues.get(Object.isString(this.options.queue) ?
287 'global' : this.options.queue.scope).add(this);
268 'global' : this.options.queue.scope).add(this);
288 },
269 },
289 loop: function(timePos) {
270 loop: function(timePos) {
290 - if(timePos >= this.startOn) {
271 + if (timePos >= this.startOn) {
291 - if(timePos >= this.finishOn) {
272 + if (timePos >= this.finishOn) {
292 this.render(1.0);
273 this.render(1.0);
293 this.cancel();
274 this.cancel();
294 this.event('beforeFinish');
275 this.event('beforeFinish');
295 - if(this.finish) this.finish();
276 + if (this.finish) this.finish();
296 this.event('afterFinish');
277 this.event('afterFinish');
297 return;
278 return;
298 }
279 }
299 - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
280 + var pos = (timePos - this.startOn) / this.totalTime,
300 - var frame = Math.round(pos * this.options.fps * this.options.duration);
281 + frame = (pos * this.totalFrames).round();
301 - if(frame > this.currentFrame) {
282 + if (frame > this.currentFrame) {
302 this.render(pos);
283 this.render(pos);
303 this.currentFrame = frame;
284 this.currentFrame = frame;
304 }
285 }
305 }
286 }
306 },
287 },
307 - render: function(pos) {
308 - if(this.state == 'idle') {
309 - this.state = 'running';
310 - this.event('beforeSetup');
311 - if(this.setup) this.setup();
312 - this.event('afterSetup');
313 - }
314 - if(this.state == 'running') {
315 - if(this.options.transition) pos = this.options.transition(pos);
316 - pos *= (this.options.to-this.options.from);
317 - pos += this.options.from;
318 - this.position = pos;
319 - this.event('beforeUpdate');
320 - if(this.update) this.update(pos);
321 - this.event('afterUpdate');
322 - }
323 - },
324 cancel: function() {
288 cancel: function() {
325 - if(!this.options.sync)
289 + if (!this.options.sync)
326 - Effect.Queues.get(typeof this.options.queue == 'string' ?
290 + Effect.Queues.get(Object.isString(this.options.queue) ?
327 'global' : this.options.queue.scope).remove(this);
291 'global' : this.options.queue.scope).remove(this);
328 this.state = 'finished';
292 this.state = 'finished';
329 },
293 },
330 event: function(eventName) {
294 event: function(eventName) {
331 - if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
295 + if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
332 - if(this.options[eventName]) this.options[eventName](this);
296 + if (this.options[eventName]) this.options[eventName](this);
333 },
297 },
334 inspect: function() {
298 inspect: function() {
335 - return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
299 + var data = $H();
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() + '>';
336 }
303 }
337 - }
304 + });
338
305
339 - Effect.Parallel = Class.create();
306 + Effect.Parallel = Class.create(Effect.Base, {
340 - Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
341 initialize: function(effects) {
307 initialize: function(effects) {
342 this.effects = effects || [];
308 this.effects = effects || [];
343 this.start(arguments[1]);
309 this.start(arguments[1]);
@@ -350,35 +316,45
350 effect.render(1.0);
316 effect.render(1.0);
351 effect.cancel();
317 effect.cancel();
352 effect.event('beforeFinish');
318 effect.event('beforeFinish');
353 - if(effect.finish) effect.finish(position);
319 + if (effect.finish) effect.finish(position);
354 effect.event('afterFinish');
320 effect.event('afterFinish');
355 });
321 });
356 }
322 }
357 });
323 });
358
324
359 - Effect.Event = Class.create();
325 + Effect.Tween = Class.create(Effect.Base, {
360 - Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
326 + initialize: function(object, from, to) {
327 + object = Object.isString(object) ? $(object) : object;
328 + var args = $A(arguments), method = args.last(),
329 + options = args.length == 5 ? args[3] : null;
330 + this.method = Object.isFunction(method) ? method.bind(object) :
331 + Object.isFunction(object[method]) ? object[method].bind(object) :
332 + function(value) { object[method] = value };
333 + this.start(Object.extend({ from: from, to: to }, options || { }));
334 + },
335 + update: function(position) {
336 + this.method(position);
337 + }
338 + });
339 +
340 + Effect.Event = Class.create(Effect.Base, {
361 initialize: function() {
341 initialize: function() {
362 - var options = Object.extend({
342 + this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
363 - duration: 0
364 - }, arguments[0] || {});
365 - this.start(options);
366 },
343 },
367 update: Prototype.emptyFunction
344 update: Prototype.emptyFunction
368 });
345 });
369
346
370 - Effect.Opacity = Class.create();
347 + Effect.Opacity = Class.create(Effect.Base, {
371 - Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
372 initialize: function(element) {
348 initialize: function(element) {
373 this.element = $(element);
349 this.element = $(element);
374 - if(!this.element) throw(Effect._elementDoesNotExistError);
350 + if (!this.element) throw(Effect._elementDoesNotExistError);
375 // make this work on IE on elements without 'layout'
351 // make this work on IE on elements without 'layout'
376 - if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
352 + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
377 this.element.setStyle({zoom: 1});
353 this.element.setStyle({zoom: 1});
378 var options = Object.extend({
354 var options = Object.extend({
379 from: this.element.getOpacity() || 0.0,
355 from: this.element.getOpacity() || 0.0,
380 to: 1.0
356 to: 1.0
381 - }, arguments[1] || {});
357 + }, arguments[1] || { });
382 this.start(options);
358 this.start(options);
383 },
359 },
384 update: function(position) {
360 update: function(position) {
@@ -386,36 +362,30
386 }
362 }
387 });
363 });
388
364
389 - Effect.Move = Class.create();
365 + Effect.Move = Class.create(Effect.Base, {
390 - Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
391 initialize: function(element) {
366 initialize: function(element) {
392 this.element = $(element);
367 this.element = $(element);
393 - if(!this.element) throw(Effect._elementDoesNotExistError);
368 + if (!this.element) throw(Effect._elementDoesNotExistError);
394 var options = Object.extend({
369 var options = Object.extend({
395 x: 0,
370 x: 0,
396 y: 0,
371 y: 0,
397 mode: 'relative'
372 mode: 'relative'
398 - }, arguments[1] || {});
373 + }, arguments[1] || { });
399 this.start(options);
374 this.start(options);
400 },
375 },
401 setup: function() {
376 setup: function() {
402 - // Bug in Opera: Opera returns the "real" position of a static element or
403 - // relative element that does not have top/left explicitly set.
404 - // ==> Always set top and left for position relative elements in your stylesheets
405 - // (to 0 if you do not need them)
406 this.element.makePositioned();
377 this.element.makePositioned();
407 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
378 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
408 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
379 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
409 - if(this.options.mode == 'absolute') {
380 + if (this.options.mode == 'absolute') {
410 - // absolute movement, so we need to calc deltaX and deltaY
411 this.options.x = this.options.x - this.originalLeft;
381 this.options.x = this.options.x - this.originalLeft;
412 this.options.y = this.options.y - this.originalTop;
382 this.options.y = this.options.y - this.originalTop;
413 }
383 }
414 },
384 },
415 update: function(position) {
385 update: function(position) {
416 this.element.setStyle({
386 this.element.setStyle({
417 - left: Math.round(this.options.x * position + this.originalLeft) + 'px',
387 + left: (this.options.x * position + this.originalLeft).round() + 'px',
418 - top: Math.round(this.options.y * position + this.originalTop) + 'px'
388 + top: (this.options.y * position + this.originalTop).round() + 'px'
419 });
389 });
420 }
390 }
421 });
391 });
@@ -423,30 +393,29
423 // for backwards compatibility
393 // for backwards compatibility
424 Effect.MoveBy = function(element, toTop, toLeft) {
394 Effect.MoveBy = function(element, toTop, toLeft) {
425 return new Effect.Move(element,
395 return new Effect.Move(element,
426 - Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
396 + Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
427 };
397 };
428
398
429 - Effect.Scale = Class.create();
399 + Effect.Scale = Class.create(Effect.Base, {
430 - Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
431 initialize: function(element, percent) {
400 initialize: function(element, percent) {
432 this.element = $(element);
401 this.element = $(element);
433 - if(!this.element) throw(Effect._elementDoesNotExistError);
402 + if (!this.element) throw(Effect._elementDoesNotExistError);
434 var options = Object.extend({
403 var options = Object.extend({
435 scaleX: true,
404 scaleX: true,
436 scaleY: true,
405 scaleY: true,
437 scaleContent: true,
406 scaleContent: true,
438 scaleFromCenter: false,
407 scaleFromCenter: false,
439 - scaleMode: 'box', // 'box' or 'contents' or {} with provided values
408 + scaleMode: 'box', // 'box' or 'contents' or { } with provided values
440 scaleFrom: 100.0,
409 scaleFrom: 100.0,
441 scaleTo: percent
410 scaleTo: percent
442 - }, arguments[2] || {});
411 + }, arguments[2] || { });
443 this.start(options);
412 this.start(options);
444 },
413 },
445 setup: function() {
414 setup: function() {
446 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
415 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
447 this.elementPositioning = this.element.getStyle('position');
416 this.elementPositioning = this.element.getStyle('position');
448
417
449 - this.originalStyle = {};
418 + this.originalStyle = { };
450 ['top','left','width','height','fontSize'].each( function(k) {
419 ['top','left','width','height','fontSize'].each( function(k) {
451 this.originalStyle[k] = this.element.style[k];
420 this.originalStyle[k] = this.element.style[k];
452 }.bind(this));
421 }.bind(this));
@@ -456,7 +425,7
456
425
457 var fontSize = this.element.getStyle('font-size') || '100%';
426 var fontSize = this.element.getStyle('font-size') || '100%';
458 ['em','px','%','pt'].each( function(fontSizeType) {
427 ['em','px','%','pt'].each( function(fontSizeType) {
459 - if(fontSize.indexOf(fontSizeType)>0) {
428 + if (fontSize.indexOf(fontSizeType)>0) {
460 this.fontSize = parseFloat(fontSize);
429 this.fontSize = parseFloat(fontSize);
461 this.fontSizeType = fontSizeType;
430 this.fontSizeType = fontSizeType;
462 }
431 }
@@ -465,60 +434,61
465 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
434 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
466
435
467 this.dims = null;
436 this.dims = null;
468 - if(this.options.scaleMode=='box')
437 + if (this.options.scaleMode=='box')
469 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
438 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
470 - if(/^content/.test(this.options.scaleMode))
439 + if (/^content/.test(this.options.scaleMode))
471 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
440 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
472 - if(!this.dims)
441 + if (!this.dims)
473 this.dims = [this.options.scaleMode.originalHeight,
442 this.dims = [this.options.scaleMode.originalHeight,
474 this.options.scaleMode.originalWidth];
443 this.options.scaleMode.originalWidth];
475 },
444 },
476 update: function(position) {
445 update: function(position) {
477 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
446 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
478 - if(this.options.scaleContent && this.fontSize)
447 + if (this.options.scaleContent && this.fontSize)
479 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
448 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
480 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
449 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
481 },
450 },
482 finish: function(position) {
451 finish: function(position) {
483 - if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
452 + if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
484 },
453 },
485 setDimensions: function(height, width) {
454 setDimensions: function(height, width) {
486 - var d = {};
455 + var d = { };
487 - if(this.options.scaleX) d.width = Math.round(width) + 'px';
456 + if (this.options.scaleX) d.width = width.round() + 'px';
488 - if(this.options.scaleY) d.height = Math.round(height) + 'px';
457 + if (this.options.scaleY) d.height = height.round() + 'px';
489 - if(this.options.scaleFromCenter) {
458 + if (this.options.scaleFromCenter) {
490 var topd = (height - this.dims[0])/2;
459 var topd = (height - this.dims[0])/2;
491 var leftd = (width - this.dims[1])/2;
460 var leftd = (width - this.dims[1])/2;
492 - if(this.elementPositioning == 'absolute') {
461 + if (this.elementPositioning == 'absolute') {
493 - if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
462 + if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
494 - if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
463 + if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
495 } else {
464 } else {
496 - if(this.options.scaleY) d.top = -topd + 'px';
465 + if (this.options.scaleY) d.top = -topd + 'px';
497 - if(this.options.scaleX) d.left = -leftd + 'px';
466 + if (this.options.scaleX) d.left = -leftd + 'px';
498 }
467 }
499 }
468 }
500 this.element.setStyle(d);
469 this.element.setStyle(d);
501 }
470 }
502 });
471 });
503
472
504 - Effect.Highlight = Class.create();
473 + Effect.Highlight = Class.create(Effect.Base, {
505 - Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
506 initialize: function(element) {
474 initialize: function(element) {
507 this.element = $(element);
475 this.element = $(element);
508 - if(!this.element) throw(Effect._elementDoesNotExistError);
476 + if (!this.element) throw(Effect._elementDoesNotExistError);
509 - var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
477 + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
510 this.start(options);
478 this.start(options);
511 },
479 },
512 setup: function() {
480 setup: function() {
513 // Prevent executing on elements not in the layout flow
481 // Prevent executing on elements not in the layout flow
514 - if(this.element.getStyle('display')=='none') { this.cancel(); return; }
482 + if (this.element.getStyle('display')=='none') { this.cancel(); return; }
515 // Disable background image during the effect
483 // Disable background image during the effect
516 - this.oldStyle = {
484 + this.oldStyle = { };
517 - backgroundImage: this.element.getStyle('background-image') };
485 + if (!this.options.keepBackgroundImage) {
518 - this.element.setStyle({backgroundImage: 'none'});
486 + this.oldStyle.backgroundImage = this.element.getStyle('background-image');
519 - if(!this.options.endcolor)
487 + this.element.setStyle({backgroundImage: 'none'});
488 + }
489 + if (!this.options.endcolor)
520 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
490 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
521 - if(!this.options.restorecolor)
491 + if (!this.options.restorecolor)
522 this.options.restorecolor = this.element.getStyle('background-color');
492 this.options.restorecolor = this.element.getStyle('background-color');
523 // init color calculations
493 // init color calculations
524 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
494 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
@@ -526,7 +496,7
526 },
496 },
527 update: function(position) {
497 update: function(position) {
528 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
498 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
529 - return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
499 + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
530 },
500 },
531 finish: function() {
501 finish: function() {
532 this.element.setStyle(Object.extend(this.oldStyle, {
502 this.element.setStyle(Object.extend(this.oldStyle, {
@@ -535,30 +505,21
535 }
505 }
536 });
506 });
537
507
538 - Effect.ScrollTo = Class.create();
508 + Effect.ScrollTo = function(element) {
539 - Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
509 + var options = arguments[1] || { },
540 - initialize: function(element) {
510 + scrollOffsets = document.viewport.getScrollOffsets(),
541 - this.element = $(element);
511 + elementOffsets = $(element).cumulativeOffset(),
542 - this.start(arguments[1] || {});
512 + max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
543 - },
513 +
544 - setup: function() {
514 + if (options.offset) elementOffsets[1] += options.offset;
545 - Position.prepare();
515 +
546 - var offsets = Position.cumulativeOffset(this.element);
516 + return new Effect.Tween(null,
547 - if(this.options.offset) offsets[1] += this.options.offset;
517 + scrollOffsets.top,
548 - var max = window.innerHeight ?
518 + elementOffsets[1] > max ? max : elementOffsets[1],
549 - window.height - window.innerHeight :
519 + options,
550 - document.body.scrollHeight -
520 + function(p){ scrollTo(scrollOffsets.left, p.round()) }
551 - (document.documentElement.clientHeight ?
521 + );
552 - document.documentElement.clientHeight : document.body.clientHeight);
522 + };
553 - this.scrollStart = Position.deltaY;
554 - this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
555 - },
556 - update: function(position) {
557 - Position.prepare();
558 - window.scrollTo(Position.deltaX,
559 - this.scrollStart + (position*this.delta));
560 - }
561 - });
562
523
563 /* ------------- combination effects ------------- */
524 /* ------------- combination effects ------------- */
564
525
@@ -566,14 +527,15
566 element = $(element);
527 element = $(element);
567 var oldOpacity = element.getInlineOpacity();
528 var oldOpacity = element.getInlineOpacity();
568 var options = Object.extend({
529 var options = Object.extend({
569 - from: element.getOpacity() || 1.0,
530 + from: element.getOpacity() || 1.0,
570 - to: 0.0,
531 + to: 0.0,
571 - afterFinishInternal: function(effect) {
532 + afterFinishInternal: function(effect) {
572 - if(effect.options.to!=0) return;
533 + if (effect.options.to!=0) return;
573 - effect.element.hide().setStyle({opacity: oldOpacity});
534 + effect.element.hide().setStyle({opacity: oldOpacity});
574 - }}, arguments[1] || {});
535 + }
536 + }, arguments[1] || { });
575 return new Effect.Opacity(element,options);
537 return new Effect.Opacity(element,options);
576 - }
538 + };
577
539
578 Effect.Appear = function(element) {
540 Effect.Appear = function(element) {
579 element = $(element);
541 element = $(element);
@@ -586,9 +548,9
586 },
548 },
587 beforeSetup: function(effect) {
549 beforeSetup: function(effect) {
588 effect.element.setOpacity(effect.options.from).show();
550 effect.element.setOpacity(effect.options.from).show();
589 - }}, arguments[1] || {});
551 + }}, arguments[1] || { });
590 return new Effect.Opacity(element,options);
552 return new Effect.Opacity(element,options);
591 - }
553 + };
592
554
593 Effect.Puff = function(element) {
555 Effect.Puff = function(element) {
594 element = $(element);
556 element = $(element);
@@ -610,9 +572,9
610 },
572 },
611 afterFinishInternal: function(effect) {
573 afterFinishInternal: function(effect) {
612 effect.effects[0].element.hide().setStyle(oldStyle); }
574 effect.effects[0].element.hide().setStyle(oldStyle); }
613 - }, arguments[1] || {})
575 + }, arguments[1] || { })
614 );
576 );
615 - }
577 + };
616
578
617 Effect.BlindUp = function(element) {
579 Effect.BlindUp = function(element) {
618 element = $(element);
580 element = $(element);
@@ -624,9 +586,9
624 afterFinishInternal: function(effect) {
586 afterFinishInternal: function(effect) {
625 effect.element.hide().undoClipping();
587 effect.element.hide().undoClipping();
626 }
588 }
627 - }, arguments[1] || {})
589 + }, arguments[1] || { })
628 );
590 );
629 - }
591 + };
630
592
631 Effect.BlindDown = function(element) {
593 Effect.BlindDown = function(element) {
632 element = $(element);
594 element = $(element);
@@ -643,8 +605,8
643 afterFinishInternal: function(effect) {
605 afterFinishInternal: function(effect) {
644 effect.element.undoClipping();
606 effect.element.undoClipping();
645 }
607 }
646 - }, arguments[1] || {}));
608 + }, arguments[1] || { }));
647 - }
609 + };
648
610
649 Effect.SwitchOff = function(element) {
611 Effect.SwitchOff = function(element) {
650 element = $(element);
612 element = $(element);
@@ -665,8 +627,8
665 }
627 }
666 })
628 })
667 }
629 }
668 - }, arguments[1] || {}));
630 + }, arguments[1] || { }));
669 - }
631 + };
670
632
671 Effect.DropOut = function(element) {
633 Effect.DropOut = function(element) {
672 element = $(element);
634 element = $(element);
@@ -685,29 +647,35
685 afterFinishInternal: function(effect) {
647 afterFinishInternal: function(effect) {
686 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
648 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
687 }
649 }
688 - }, arguments[1] || {}));
650 + }, arguments[1] || { }));
689 - }
651 + };
690
652
691 Effect.Shake = function(element) {
653 Effect.Shake = function(element) {
692 element = $(element);
654 element = $(element);
655 + var options = Object.extend({
656 + distance: 20,
657 + duration: 0.5
658 + }, arguments[1] || {});
659 + var distance = parseFloat(options.distance);
660 + var split = parseFloat(options.duration) / 10.0;
693 var oldStyle = {
661 var oldStyle = {
694 top: element.getStyle('top'),
662 top: element.getStyle('top'),
695 left: element.getStyle('left') };
663 left: element.getStyle('left') };
696 - return new Effect.Move(element,
664 + return new Effect.Move(element,
697 - { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
665 + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
698 new Effect.Move(effect.element,
666 new Effect.Move(effect.element,
699 - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
667 + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
700 new Effect.Move(effect.element,
668 new Effect.Move(effect.element,
701 - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
669 + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
702 new Effect.Move(effect.element,
670 new Effect.Move(effect.element,
703 - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
671 + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
704 new Effect.Move(effect.element,
672 new Effect.Move(effect.element,
705 - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
673 + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
706 new Effect.Move(effect.element,
674 new Effect.Move(effect.element,
707 - { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
675 + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
708 effect.element.undoPositioned().setStyle(oldStyle);
676 effect.element.undoPositioned().setStyle(oldStyle);
709 }}) }}) }}) }}) }}) }});
677 }}) }}) }}) }}) }}) }});
710 - }
678 + };
711
679
712 Effect.SlideDown = function(element) {
680 Effect.SlideDown = function(element) {
713 element = $(element).cleanWhitespace();
681 element = $(element).cleanWhitespace();
@@ -723,7 +691,7
723 afterSetup: function(effect) {
691 afterSetup: function(effect) {
724 effect.element.makePositioned();
692 effect.element.makePositioned();
725 effect.element.down().makePositioned();
693 effect.element.down().makePositioned();
726 - if(window.opera) effect.element.setStyle({top: ''});
694 + if (window.opera) effect.element.setStyle({top: ''});
727 effect.element.makeClipping().setStyle({height: '0px'}).show();
695 effect.element.makeClipping().setStyle({height: '0px'}).show();
728 },
696 },
729 afterUpdateInternal: function(effect) {
697 afterUpdateInternal: function(effect) {
@@ -733,23 +701,25
733 afterFinishInternal: function(effect) {
701 afterFinishInternal: function(effect) {
734 effect.element.undoClipping().undoPositioned();
702 effect.element.undoClipping().undoPositioned();
735 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
703 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
736 - }, arguments[1] || {})
704 + }, arguments[1] || { })
737 );
705 );
738 - }
706 + };
739
707
740 Effect.SlideUp = function(element) {
708 Effect.SlideUp = function(element) {
741 element = $(element).cleanWhitespace();
709 element = $(element).cleanWhitespace();
742 var oldInnerBottom = element.down().getStyle('bottom');
710 var oldInnerBottom = element.down().getStyle('bottom');
711 + var elementDimensions = element.getDimensions();
743 return new Effect.Scale(element, window.opera ? 0 : 1,
712 return new Effect.Scale(element, window.opera ? 0 : 1,
744 Object.extend({ scaleContent: false,
713 Object.extend({ scaleContent: false,
745 scaleX: false,
714 scaleX: false,
746 scaleMode: 'box',
715 scaleMode: 'box',
747 scaleFrom: 100,
716 scaleFrom: 100,
717 + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
748 restoreAfterFinish: true,
718 restoreAfterFinish: true,
749 - beforeStartInternal: function(effect) {
719 + afterSetup: function(effect) {
750 effect.element.makePositioned();
720 effect.element.makePositioned();
751 effect.element.down().makePositioned();
721 effect.element.down().makePositioned();
752 - if(window.opera) effect.element.setStyle({top: ''});
722 + if (window.opera) effect.element.setStyle({top: ''});
753 effect.element.makeClipping().show();
723 effect.element.makeClipping().show();
754 },
724 },
755 afterUpdateInternal: function(effect) {
725 afterUpdateInternal: function(effect) {
@@ -757,12 +727,12
757 (effect.dims[0] - effect.element.clientHeight) + 'px' });
727 (effect.dims[0] - effect.element.clientHeight) + 'px' });
758 },
728 },
759 afterFinishInternal: function(effect) {
729 afterFinishInternal: function(effect) {
760 - effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
730 + effect.element.hide().undoClipping().undoPositioned();
761 - effect.element.down().undoPositioned();
731 + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
762 }
732 }
763 - }, arguments[1] || {})
733 + }, arguments[1] || { })
764 );
734 );
765 - }
735 + };
766
736
767 // Bug in opera makes the TD containing this element expand for a instance after finish
737 // Bug in opera makes the TD containing this element expand for a instance after finish
768 Effect.Squish = function(element) {
738 Effect.Squish = function(element) {
@@ -775,7 +745,7
775 effect.element.hide().undoClipping();
745 effect.element.hide().undoClipping();
776 }
746 }
777 });
747 });
778 - }
748 + };
779
749
780 Effect.Grow = function(element) {
750 Effect.Grow = function(element) {
781 element = $(element);
751 element = $(element);
@@ -784,7 +754,7
784 moveTransition: Effect.Transitions.sinoidal,
754 moveTransition: Effect.Transitions.sinoidal,
785 scaleTransition: Effect.Transitions.sinoidal,
755 scaleTransition: Effect.Transitions.sinoidal,
786 opacityTransition: Effect.Transitions.full
756 opacityTransition: Effect.Transitions.full
787 - }, arguments[1] || {});
757 + }, arguments[1] || { });
788 var oldStyle = {
758 var oldStyle = {
789 top: element.style.top,
759 top: element.style.top,
790 left: element.style.left,
760 left: element.style.left,
@@ -849,7 +819,7
849 )
819 )
850 }
820 }
851 });
821 });
852 - }
822 + };
853
823
854 Effect.Shrink = function(element) {
824 Effect.Shrink = function(element) {
855 element = $(element);
825 element = $(element);
@@ -858,7 +828,7
858 moveTransition: Effect.Transitions.sinoidal,
828 moveTransition: Effect.Transitions.sinoidal,
859 scaleTransition: Effect.Transitions.sinoidal,
829 scaleTransition: Effect.Transitions.sinoidal,
860 opacityTransition: Effect.Transitions.none
830 opacityTransition: Effect.Transitions.none
861 - }, arguments[1] || {});
831 + }, arguments[1] || { });
862 var oldStyle = {
832 var oldStyle = {
863 top: element.style.top,
833 top: element.style.top,
864 left: element.style.left,
834 left: element.style.left,
@@ -903,11 +873,11
903 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
873 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
904 }, options)
874 }, options)
905 );
875 );
906 - }
876 + };
907
877
908 Effect.Pulsate = function(element) {
878 Effect.Pulsate = function(element) {
909 element = $(element);
879 element = $(element);
910 - var options = arguments[1] || {};
880 + var options = arguments[1] || { };
911 var oldOpacity = element.getInlineOpacity();
881 var oldOpacity = element.getInlineOpacity();
912 var transition = options.transition || Effect.Transitions.sinoidal;
882 var transition = options.transition || Effect.Transitions.sinoidal;
913 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
883 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
@@ -916,7 +886,7
916 Object.extend(Object.extend({ duration: 2.0, from: 0,
886 Object.extend(Object.extend({ duration: 2.0, from: 0,
917 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
887 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
918 }, options), {transition: reverser}));
888 }, options), {transition: reverser}));
919 - }
889 + };
920
890
921 Effect.Fold = function(element) {
891 Effect.Fold = function(element) {
922 element = $(element);
892 element = $(element);
@@ -936,37 +906,71
936 afterFinishInternal: function(effect) {
906 afterFinishInternal: function(effect) {
937 effect.element.hide().undoClipping().setStyle(oldStyle);
907 effect.element.hide().undoClipping().setStyle(oldStyle);
938 } });
908 } });
939 - }}, arguments[1] || {}));
909 + }}, arguments[1] || { }));
940 };
910 };
941
911
942 - Effect.Morph = Class.create();
912 + Effect.Morph = Class.create(Effect.Base, {
943 - Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
944 initialize: function(element) {
913 initialize: function(element) {
945 this.element = $(element);
914 this.element = $(element);
946 - if(!this.element) throw(Effect._elementDoesNotExistError);
915 + if (!this.element) throw(Effect._elementDoesNotExistError);
947 var options = Object.extend({
916 var options = Object.extend({
948 - style: ''
917 + style: { }
949 - }, arguments[1] || {});
918 + }, arguments[1] || { });
919 +
920 + if (!Object.isString(options.style)) this.style = $H(options.style);
921 + else {
922 + if (options.style.include(':'))
923 + this.style = options.style.parseStyle();
924 + else {
925 + this.element.addClassName(options.style);
926 + this.style = $H(this.element.getStyles());
927 + this.element.removeClassName(options.style);
928 + var css = this.element.getStyles();
929 + this.style = this.style.reject(function(style) {
930 + return style.value == css[style.key];
931 + });
932 + options.afterFinishInternal = function(effect) {
933 + effect.element.addClassName(effect.options.style);
934 + effect.transforms.each(function(transform) {
935 + effect.element.style[transform.style] = '';
936 + });
937 + }
938 + }
939 + }
950 this.start(options);
940 this.start(options);
951 },
941 },
942 +
952 setup: function(){
943 setup: function(){
953 function parseColor(color){
944 function parseColor(color){
954 - if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
945 + if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
955 color = color.parseColor();
946 color = color.parseColor();
956 return $R(0,2).map(function(i){
947 return $R(0,2).map(function(i){
957 return parseInt( color.slice(i*2+1,i*2+3), 16 )
948 return parseInt( color.slice(i*2+1,i*2+3), 16 )
958 });
949 });
959 }
950 }
960 - this.transforms = this.options.style.parseStyle().map(function(property){
951 + this.transforms = this.style.map(function(pair){
961 - var originalValue = this.element.getStyle(property[0]);
952 + var property = pair[0], value = pair[1], unit = null;
962 - return $H({
953 +
963 - style: property[0],
954 + if (value.parseColor('#zzzzzz') != '#zzzzzz') {
964 - originalValue: property[1].unit=='color' ?
955 + value = value.parseColor();
965 - parseColor(originalValue) : parseFloat(originalValue || 0),
956 + unit = 'color';
966 - targetValue: property[1].unit=='color' ?
957 + } else if (property == 'opacity') {
967 - parseColor(property[1].value) : property[1].value,
958 + value = parseFloat(value);
968 - unit: property[1].unit
959 + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
969 - });
960 + this.element.setStyle({zoom: 1});
961 + } else if (Element.CSS_LENGTH.test(value)) {
962 + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
963 + value = parseFloat(components[1]);
964 + unit = (components.length == 3) ? components[2] : null;
965 + }
966 +
967 + var originalValue = this.element.getStyle(property);
968 + return {
969 + style: property.camelize(),
970 + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
971 + targetValue: unit=='color' ? parseColor(value) : value,
972 + unit: unit
973 + };
970 }.bind(this)).reject(function(transform){
974 }.bind(this)).reject(function(transform){
971 return (
975 return (
972 (transform.originalValue == transform.targetValue) ||
976 (transform.originalValue == transform.targetValue) ||
@@ -978,32 +982,35
978 });
982 });
979 },
983 },
980 update: function(position) {
984 update: function(position) {
981 - var style = $H(), value = null;
985 + var style = { }, transform, i = this.transforms.length;
982 - this.transforms.each(function(transform){
986 + while(i--)
983 - value = transform.unit=='color' ?
987 + style[(transform = this.transforms[i]).style] =
984 - $R(0,2).inject('#',function(m,v,i){
988 + transform.unit=='color' ? '#'+
985 - return m+(Math.round(transform.originalValue[i]+
989 + (Math.round(transform.originalValue[0]+
986 - (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
990 + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
987 - transform.originalValue + Math.round(
991 + (Math.round(transform.originalValue[1]+
988 - ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
992 + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
989 - style[transform.style] = value;
993 + (Math.round(transform.originalValue[2]+
990 - });
994 + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
991 - this.element.setStyle(style);
995 + (transform.originalValue +
996 + (transform.targetValue - transform.originalValue) * position).toFixed(3) +
997 + (transform.unit === null ? '' : transform.unit);
998 + this.element.setStyle(style, true);
992 }
999 }
993 });
1000 });
994
1001
995 - Effect.Transform = Class.create();
1002 + Effect.Transform = Class.create({
996 - Object.extend(Effect.Transform.prototype, {
997 initialize: function(tracks){
1003 initialize: function(tracks){
998 this.tracks = [];
1004 this.tracks = [];
999 - this.options = arguments[1] || {};
1005 + this.options = arguments[1] || { };
1000 this.addTracks(tracks);
1006 this.addTracks(tracks);
1001 },
1007 },
1002 addTracks: function(tracks){
1008 addTracks: function(tracks){
1003 tracks.each(function(track){
1009 tracks.each(function(track){
1004 - var data = $H(track).values().first();
1010 + track = $H(track);
1011 + var data = track.values().first();
1005 this.tracks.push($H({
1012 this.tracks.push($H({
1006 - ids: $H(track).keys().first(),
1013 + ids: track.keys().first(),
1007 effect: Effect.Morph,
1014 effect: Effect.Morph,
1008 options: { style: data }
1015 options: { style: data }
1009 }));
1016 }));
@@ -1013,76 +1020,101
1013 play: function(){
1020 play: function(){
1014 return new Effect.Parallel(
1021 return new Effect.Parallel(
1015 this.tracks.map(function(track){
1022 this.tracks.map(function(track){
1016 - var elements = [$(track.ids) || $$(track.ids)].flatten();
1023 + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1017 - return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1024 + var elements = [$(ids) || $$(ids)].flatten();
1025 + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1018 }).flatten(),
1026 }).flatten(),
1019 this.options
1027 this.options
1020 );
1028 );
1021 }
1029 }
1022 });
1030 });
1023
1031
1024 - Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1032 + Element.CSS_PROPERTIES = $w(
1025 - 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1033 + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1026 - 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1034 + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1027 - 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1035 + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1028 - 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1036 + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1029 - 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1037 + 'fontSize fontWeight height left letterSpacing lineHeight ' +
1030 - 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1038 + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1031 - 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1039 + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1032 - 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1040 + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1033 - 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
1041 + 'right textIndent top width wordSpacing zIndex');
1034 - 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
1035 - 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
1036 - 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
1037 - 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
1038 - 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
1039 - 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
1040 - 'width', 'wordSpacing', 'zIndex'];
1041
1042
1042 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043
1044
1045 + String.__parseStyleElement = document.createElement('div');
1044 String.prototype.parseStyle = function(){
1046 String.prototype.parseStyle = function(){
1045 - var element = Element.extend(document.createElement('div'));
1047 + var style, styleRules = $H();
1046 - element.innerHTML = '<div style="' + this + '"></div>';
1048 + if (Prototype.Browser.WebKit)
1047 - var style = element.down().style, styleRules = $H();
1049 + style = new Element('div',{style:this}).style;
1050 + else {
1051 + String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
1052 + style = String.__parseStyleElement.childNodes[0].style;
1053 + }
1048
1054
1049 Element.CSS_PROPERTIES.each(function(property){
1055 Element.CSS_PROPERTIES.each(function(property){
1050 - if(style[property]) styleRules[property] = style[property];
1056 + if (style[property]) styleRules.set(property, style[property]);
1051 });
1057 });
1052
1058
1053 - var result = $H();
1059 + if (Prototype.Browser.IE && this.include('opacity'))
1054 -
1060 + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
1055 - styleRules.each(function(pair){
1061 +
1056 - var property = pair[0], value = pair[1], unit = null;
1062 + return styleRules;
1057 -
1063 + };
1058 - if(value.parseColor('#zzzzzz') != '#zzzzzz') {
1064 +
1059 - value = value.parseColor();
1065 + if (document.defaultView && document.defaultView.getComputedStyle) {
1060 - unit = 'color';
1066 + Element.getStyles = function(element) {
1061 - } else if(Element.CSS_LENGTH.test(value))
1067 + var css = document.defaultView.getComputedStyle($(element), null);
1062 - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
1068 + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1063 - value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
1069 + styles[property] = css[property];
1064 -
1070 + return styles;
1065 - result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1071 + });
1066 - }.bind(this));
1072 + };
1067 -
1073 + } else {
1068 - return result;
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 = {
1072 - new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1087 + morph: function(element, style) {
1073 - return element;
1088 + element = $(element);
1089 + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
1090 + return element;
1091 + },
1092 + visualEffect: function(element, effect, options) {
1093 + element = $(element)
1094 + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
1095 + new Effect[klass](element, options);
1096 + return element;
1097 + },
1098 + highlight: function(element, options) {
1099 + element = $(element);
1100 + new Effect.Highlight(element, options);
1101 + return element;
1102 + }
1074 };
1103 };
1075
1104
1076 - ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
1105 + $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1077 - 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1106 + 'pulsate shake puff squish switchOff dropOut').each(
1078 - function(f) { Element.Methods[f] = Element[f]; }
1107 + function(effect) {
1108 + Effect.Methods[effect] = function(element, options){
1109 + element = $(element);
1110 + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
1111 + return element;
1112 + }
1113 + }
1079 );
1114 );
1080
1115
1081 - Element.Methods.visualEffect = function(element, effect, options) {
1116 + $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1082 - s = effect.gsub(/_/, '-').camelize();
1117 + function(f) { Effect.Methods[f] = Element[f]; }
1083 - effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1118 + );
1084 - new Effect[effect_class](element, options);
1085 - return $(element);
1086 - };
1087
1119
1088 - Element.addMethods(); No newline at end of file
1120 + Element.addMethods(Effect.Methods);
You need to be logged in to leave comments. Login now