diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index c3a922604e1bdc6ab78b96ad68d1e6fbbb0a795f..ae8de12988df283d226f121c70bcaaa65d8162ed 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Added script.aculo.us Javascripts (controls.js, dragdrop.js, effects.js) (NEEDS MORE DESCRIPTION) #1509 [Thomas Fuchs]
+
* Fixed prototype to consider all fields it doesn't know as text (such as Safari's search) just like the browser in its serialization #1497 [Sean Treadway]
* Improved performance of Routes generation by a factor of 5 #1434 [Nicholas Seckar]
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index d5e2a8b2dccb7fcae822312794d652dda7209b35..31c15d222c8ad1d09dc2046d435f717c52fe238d 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -16,7 +16,8 @@ module Helpers
# the use of form_remote_tag.
module JavascriptHelper
unless const_defined? :CALLBACKS
- CALLBACKS = [:uninitialized, :loading, :loaded, :interactive, :complete]
+ CALLBACKS = [ :uninitialized, :loading, :loaded, :interactive, :complete ]
+ AJAX_OPTIONS = [ :url, :asynchronous, :method, :insertion, :form, :with ].concat(CALLBACKS)
JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
end
@@ -146,8 +147,10 @@ def remote_function(options) #:nodoc: for now
return function
end
- # Includes the Action Pack Javascript library inside a single '
end
# Observes the field with the DOM ID specified by +field_id+ and makes
- # an Ajax when its contents have changed.
+ # an Ajax call when its contents have changed.
#
# Required +options+ are:
# :frequency:: The frequency (in seconds) at which changes to
@@ -190,13 +203,111 @@ def observe_field(field_id, options = {})
def observe_form(form_id, options = {})
build_observer('Form.Observer', form_id, options)
end
+
+
+ # Adds Ajax autocomplete functionality to the text input field with the
+ # DOM ID specified by +field_id+.
+ #
+ # This function expects that the called action returns a HTML
list,
+ # or nothing if no entries should be displayed for autocompletion.
+ #
+ # Required +options+ are:
+ # :url:: Specifies the DOM ID of the element whose
+ # innerHTML should be updated with the autocomplete
+ # entries returned by XMLHttpRequest.
+ #
+ # Addtional +options+ are:
+ # :update:: Specifies the DOM ID of the element whose
+ # innerHTML should be updated with the autocomplete
+ # entries returned by the Ajax request.
+ # Defaults to field_id + '_autocomplete'
+ # :with:: A Javascript expression specifying the
+ # parameters for the XMLHttpRequest. This defaults
+ # to 'value', which in the evaluated context
+ # refers to the new field value.
+ # :indicator:: Specifies the DOM ID of an elment which will be
+ # displayed while autocomplete is running.
+ #
+ def remote_autocomplete(field_id, options = {})
+ function = "new Ajax.Autocompleter("
+ function << "'#{field_id}', "
+ function << "'" + (options[:update] || "#{field_id}_autocomplete") + "', "
+ function << "'#{url_for(options[:url])}'"
+
+ js_options = {}
+ js_options[:callback] = "function(element, value) {return #{options[:with]}}" if options[:with]
+ js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator]
+ function << (', ' + options_for_javascript(js_options) + ')')
+
+ javascript_tag(function)
+ end
+
+ # Use this method in your view to generate a return for the Ajax automplete requests.
+ #
+ # Example Action:
+ # @items = Item.find(:all, :conditions => [ 'LOWER(description) LIKE ?',
+ # '%' + params[:for].downcase + '%' ], 'description ASC')
+ # render :layout => false
+ #
+ # Example View:
+ # <%= autocomplete_responder @items, 'description' %>
+ def autocomplete_responder(entries, field, phrase = nil)
+ "#{entries.map { |entry| '- ' + (phrase ? highlight(entry[field],phrase) : h(entry[field])) + '
' }.join}
" if entries
+ end
+
+ # Returns a JavaScript snippet to be used on the Ajax callbacks for starting
+ # visual effects.
+ #
+ # Example:
+ # <%= link_to_remote "Reload", :update => "posts",
+ # :url => { :action => "reload" },
+ # :complete => visual_effect(:highlight, "posts", :duration => 0.5 )
+ #
+ # You can change the behaviour with various options, see
+ # http://script.aculo.us for more documentation.
+ def visual_effect(name, element_id, js_options = {})
+ "new Effect.#{name.to_s.capitalize}('#{element_id}',#{options_for_javascript(js_options)});"
+ end
+
+ # Makes the element with the DOM ID specified by +element_id+ sortable
+ # by drag-and-drop and make an Ajax call whenever the sort order has
+ # changed. By default, the action called gets the serialized sortable
+ # element as parameters.
+ #
+ # Example:
+ # <%= remote_sortable("my_list", :url => { :action => "order" }) %>
+ #
+ # In the example, the action gets a "my_list" array parameter
+ # containing the values of the ids of elements the sortable consists
+ # of, in the current order.
+ #
+ # You can change the behaviour with various options, see
+ # http://script.aculo.us for more documentation.
+ #
+ def remote_sortable(element_id, options = {})
+ options[:with] ||= "Sortable.serialize('#{element_id}')"
+ options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
+ options.delete_if { |key, value| AJAX_OPTIONS.include?(key) }
+
+ javascript_tag("Sortable.create('#{element_id}', #{options_for_javascript(options)})")
+ end
# Escape carrier returns and single and double quotes for Javascript segments.
def escape_javascript(javascript)
(javascript || '').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
end
+ # Returns a Javascript tag with the +content+ inside. Example:
+ # javascript_tag "alert('All is good')" # =>
+ def javascript_tag(content)
+ content_tag("script", content, :type => "text/javascript")
+ end
+
private
+ def options_for_javascript(options)
+ '{' + options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
+ end
+
def options_for_ajax(options)
js_options = build_callbacks(options)
@@ -210,7 +321,7 @@ def options_for_ajax(options)
js_options['parameters'] = options[:with]
end
- '{' + js_options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
+ options_for_javascript(js_options)
end
def method_option_to_s(method)
diff --git a/actionpack/lib/action_view/helpers/javascripts/controls.js b/actionpack/lib/action_view/helpers/javascripts/controls.js
new file mode 100644
index 0000000000000000000000000000000000000000..94d438a7847389992165485b41db61b9971760ce
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/javascripts/controls.js
@@ -0,0 +1,218 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Ajax.Autocompleter = Class.create();
+Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({
+ initialize: function(element, update, url, options) {
+ this.element = $(element);
+ this.update = $(update);
+ this.has_focus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entry_count = 0;
+ this.url = url;
+
+ this.setOptions(options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this)
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.min_chars = this.options.min_chars || 1;
+ this.options.method = 'post';
+
+ if(this.options.indicator)
+ this.indicator = $(this.options.indicator);
+
+ this.observer = null;
+
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+ Event.observe(document, "click", this.onBlur.bindAsEventListener(this));
+ },
+
+ show: function() {
+ Element.show(this.update);
+ if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0)) {
+ new Insertion.Before(this.update,
+ '');
+ this.iefix = $(this.update.id+'_iefix');
+ this.iefix.style.position = 'absolute';
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ }
+ if(this.iefix) {
+ Position.clone(this.update, this.iefix);
+ Element.show(this.iefix);
+ }
+ },
+
+ hide: function() {
+ if(this.iefix) Element.hide(this.iefix);
+ Element.hide(this.update);
+ },
+
+ startIndicator: function() {
+ if(this.indicator) Element.show(this.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.indicator) Element.hide(this.indicator);
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ if(this.element.value.length>=this.options.min_chars) {
+ this.startIndicator();
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, Form.Element.getValue(this.element)) :
+ Form.Element.getValue(this.element);
+ new Ajax.Request(this.url, this.options);
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ onComplete: function(request) {
+ if(!this.changed) {
+ this.update.innerHTML = request.responseText;
+
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
+ this.entry_count =
+ this.update.firstChild.childNodes.length;
+ for (var i = 0; i < this.entry_count; i++) {
+ entry = this.get_entry(i);
+ entry.autocompleteIndex = i;
+ Event.observe(entry, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(entry, "click", this.onClick.bindAsEventListener(this));
+ }
+ } else {
+ this.entry_count = 0;
+ }
+
+ this.stopIndicator();
+
+ this.index = 0;
+ this.render();
+ }
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.select_entry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.mark_previous();
+ this.render();
+ return;
+ case Event.KEY_DOWN:
+ this.mark_next();
+ this.render();
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
+ return;
+
+ this.changed = true;
+ this.has_focus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ onHover: function(event) {
+ element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ },
+
+ onClick: function(event) {
+ element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.select_entry();
+ },
+
+ onBlur: function(event) {
+ element = Event.element(event);
+ if(element==this.update) return;
+ while(element.parentNode)
+ { element = element.parentNode; if(element==this.update) return; }
+ this.hide();
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entry_count > 0) {
+ for (var i = 0; i < this.entry_count; i++)
+ this.index==i ?
+ Element.Class.add(this.get_entry(i),"selected") :
+ Element.Class.remove(this.get_entry(i),"selected");
+
+ if(this.has_focus) {
+ if(this.get_current_entry().scrollIntoView)
+ this.get_current_entry().scrollIntoView(false);
+
+ this.show();
+ this.active = true;
+ }
+ } else this.hide();
+ },
+
+ mark_previous: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entry_count-1;
+ },
+
+ mark_next: function() {
+ if(this.index < this.entry_count-1) this.index++
+ else this.index = 0;
+ },
+
+ get_entry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ get_current_entry: function() {
+ return this.get_entry(this.index);
+ },
+
+ select_entry: function() {
+ this.hide();
+ this.active = false;
+ value = Text.decodeHTML(Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal'));
+ this.element.value = value;
+ this.element.focus();
+ }
+});
\ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/javascripts/dragdrop.js b/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
new file mode 100644
index 0000000000000000000000000000000000000000..f9ed0b789873fdc9b1a20c4d4306bc0f8aeaa9a3
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -0,0 +1,385 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Element.Class part Copyright (c) 2005 by Rick Olson
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+ drops: false,
+ include_scroll_offsets: false,
+
+ add: function(element) {
+ var element = $(element);
+ var options = {
+ greedy: true,
+ hoverclass: null
+ }.extend(arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = new Array();
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ for(var i=0; i0) window.scrollBy(0,0);
+
+ Event.stop(event);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+ onEnd: function() {
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+Sortable = {
+ create: function(element) {
+ var element = $(element);
+ var options = {
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ hoverclass: null,
+ onChange: function() {},
+ onUpdate: function() {}
+ }.extend(arguments[1] || {});
+ element.sortable = options;
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ constraint: options.constraint,
+ handle: handle };
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass,
+ onHover: function(element, dropon, overlap) {
+ if(overlap>0.5) {
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
+ oldParentNode.sortable.onChange(element);
+ if(dropon.parentNode.sortable)
+ dropon.parentNode.sortable.onChange(element);
+ }
+ } else {
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
+ oldParentNode.sortable.onChange(element);
+ if(dropon.parentNode.sortable)
+ dropon.parentNode.sortable.onChange(element);
+ }
+ }
+ }
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ // make it so
+ var elements = element.childNodes;
+ for (var i = 0; i < elements.length; i++)
+ if(elements[i].tagName && elements[i].tagName==options.tag.toUpperCase() &&
+ (!options.only || (Element.Class.has(elements[i], options.only)))) {
+
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.Class.childrenWith(elements[i], options.handle)[0] : elements[i];
+
+ new Draggable(elements[i], options_for_draggable.extend({ handle: handle }));
+ Droppables.add(elements[i], options_for_droppable);
+ }
+
+ },
+ serialize: function(element) {
+ var element = $(element);
+ var options = {
+ tag: element.sortable.tag,
+ only: element.sortable.only,
+ name: element.id
+ }.extend(arguments[1] || {});
+
+ var items = $(element).childNodes;
+ var queryComponents = new Array();
+
+ for(var i=0; i= this.finishOn) {
+ this.render(this.options.to);
+ if(this.finish) this.finish();
+ if(this.options.afterFinish) this.options.afterFinish(this);
+ return;
+ }
+ pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ this.timeout = setTimeout(this.loop.bind(this), 10);
+ },
+ render: function(pos) {
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos = pos * (this.options.to-this.options.from);
+ pos += this.options.from;
+ if(this.options.beforeUpdate) this.options.beforeUpdate(this);
+ if(this.update) this.update(pos);
+ if(this.options.afterUpdate) this.options.afterUpdate(this);
+ },
+ cancel: function() {
+ if(this.timeout) clearTimeout(this.timeout);
+ }
+}
+
+Effect.Parallel = Class.create();
+ Effect.Parallel.prototype = (new Effect.Base()).extend({
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ for (var i = 0; i < this.effects.length; i++)
+ this.effects[i].render(position);
+ },
+ finish: function(position) {
+ for (var i = 0; i < this.effects.length; i++)
+ if(this.effects[i].finish) this.effects[i].finish(position);
+ }
+ });
+
+Effect.Opacity = Class.create();
+Effect.Opacity.prototype = (new Effect.Base()).extend({
+ initialize: function(element) {
+ this.element = $(element);
+ options = {
+ from: 0.0,
+ to: 1.0
+ }.extend(arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ this.setOpacity(position);
+ },
+ setOpacity: function(opacity) {
+ opacity = (opacity == 1) ? 0.99999 : opacity;
+ this.element.style.opacity = opacity;
+ this.element.style.filter = "alpha(opacity:"+opacity*100+")";
+ }
+});
+
+Effect.MoveBy = Class.create();
+ Effect.MoveBy.prototype = (new Effect.Base()).extend({
+ initialize: function(element, toTop, toLeft) {
+ this.element = $(element);
+ this.originalTop = parseFloat(this.element.style.top || '0');
+ this.originalLeft = parseFloat(this.element.style.left || '0');
+ this.toTop = toTop;
+ this.toLeft = toLeft;
+ if(this.element.style.position == "")
+ this.element.style.position = "relative";
+ this.start(arguments[3]);
+ },
+ update: function(position) {
+ topd = this.toTop * position + this.originalTop;
+ leftd = this.toLeft * position + this.originalLeft;
+ this.setPosition(topd, leftd);
+ },
+ setPosition: function(topd, leftd) {
+ this.element.style.top = topd + "px";
+ this.element.style.left = leftd + "px";
+ }
+});
+
+Effect.Scale = Class.create();
+Effect.Scale.prototype = (new Effect.Base()).extend({
+ initialize: function(element, percent) {
+ this.element = $(element)
+ options = {
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0
+ }.extend(arguments[2] || {});
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+ if (this.element.style.fontSize=="") this.sizeEm = 1.0;
+ if (this.element.style.fontSize && this.element.style.fontSize.indexOf("em")>0)
+ this.sizeEm = parseFloat(this.element.style.fontSize);
+ this.factor = (percent/100.0) - (options.scaleFrom/100.0);
+ if(options.scaleMode=='box') {
+ this.originalHeight = this.element.clientHeight;
+ this.originalWidth = this.element.clientWidth;
+ } else
+ if(options.scaleMode=='contents') {
+ this.originalHeight = this.element.scrollHeight;
+ this.originalWidth = this.element.scrollWidth;
+ } else {
+ this.originalHeight = options.scaleMode.originalHeight;
+ this.originalWidth = options.scaleMode.originalWidth;
+ }
+ this.start(options);
+ },
+
+ update: function(position) {
+ currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.sizeEm)
+ this.element.style.fontSize = this.sizeEm*currentScale + "em";
+ this.setDimensions(
+ this.originalWidth * currentScale,
+ this.originalHeight * currentScale);
+ },
+
+ setDimensions: function(width, height) {
+ if(this.options.scaleX) this.element.style.width = width + 'px';
+ if(this.options.scaleY) this.element.style.height = height + 'px';
+ if(this.options.scaleFromCenter) {
+ topd = (height - this.originalHeight)/2;
+ leftd = (width - this.originalWidth)/2;
+ if(this.element.style.position=='absolute') {
+ if(this.options.scaleY) this.element.style.top = this.originalTop-topd + "px";
+ if(this.options.scaleX) this.element.style.left = this.originalLeft-leftd + "px";
+ } else {
+ if(this.options.scaleY) this.element.style.top = -topd + "px";
+ if(this.options.scaleX) this.element.style.left = -leftd + "px";
+ }
+ }
+ }
+});
+
+Effect.Highlight = Class.create();
+Effect.Highlight.prototype = (new Effect.Base()).extend({
+ initialize: function(element) {
+ this.element = $(element);
+
+ // try to parse current background color as default for endcolor
+ // browser stores this as: "rgb(255, 255, 255)", convert to "#ffffff" format
+ var endcolor = "#ffffff";
+ var current = this.element.style.backgroundColor;
+ if(current && current.slice(0,4) == "rgb(") {
+ endcolor = "#";
+ var cols = current.slice(4,current.length-1).split(',');
+ var i=0; do { endcolor += parseInt(cols[i]).toColorPart() } while (++i<3); }
+
+ var options = {
+ startcolor: "#ffff99",
+ endcolor: endcolor
+ }.extend(arguments[1] || {});
+
+ // init color calculations
+ this.colors_base = [
+ parseInt(options.startcolor.slice(1,3),16),
+ parseInt(options.startcolor.slice(3,5),16),
+ parseInt(options.startcolor.slice(5),16) ];
+ this.colors_delta = [
+ parseInt(options.endcolor.slice(1,3),16)-this.colors_base[0],
+ parseInt(options.endcolor.slice(3,5),16)-this.colors_base[1],
+ parseInt(options.endcolor.slice(5),16)-this.colors_base[2] ];
+
+ this.start(options);
+ },
+ update: function(position) {
+ var colors = [
+ Math.round(this.colors_base[0]+(this.colors_delta[0]*position)),
+ Math.round(this.colors_base[1]+(this.colors_delta[1]*position)),
+ Math.round(this.colors_base[2]+(this.colors_delta[2]*position)) ];
+ this.element.style.backgroundColor = "#" +
+ colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart();
+ }
+});
+
+
+/* ------------- prepackaged effects ------------- */
+
+Effect.Fade = function(element) {
+ options = {
+ from: 1.0,
+ to: 0.0,
+ afterFinish: function(effect)
+ { Element.hide(effect.element);
+ effect.setOpacity(1); }
+ }.extend(arguments[1] || {});
+ new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+ options = {
+ from: 0.0,
+ to: 1.0,
+ beforeStart: function(effect)
+ { effect.setOpacity(0);
+ Element.show(effect.element); },
+ afterUpdate: function(effect)
+ { Element.show(effect.element); }
+ }.extend(arguments[1] || {});
+ new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+ new Effect.Parallel(
+ [ new Effect.Scale(element, 200, { sync: true, scaleFromCenter: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ],
+ { duration: 1.0,
+ afterUpdate: function(effect)
+ { effect.effects[0].element.style.position = 'absolute'; },
+ afterFinish: function(effect)
+ { Element.hide(effect.effects[0].element); }
+ }
+ );
+}
+
+Effect.BlindUp = function(element) {
+ $(element).style.overflow = 'hidden';
+ new Effect.Scale(element, 0,
+ { scaleContent: false,
+ scaleX: false,
+ afterFinish: function(effect)
+ { Element.hide(effect.element) }
+ }.extend(arguments[1] || {})
+ );
+}
+
+Effect.BlindDown = function(element) {
+ $(element).style.height = '0px';
+ $(element).style.overflow = 'hidden';
+ Element.show(element);
+ new Effect.Scale(element, 100,
+ { scaleContent: false,
+ scaleX: false,
+ scaleMode: 'contents',
+ scaleFrom: 0
+ }.extend(arguments[1] || {})
+ );
+}
+
+Effect.SwitchOff = function(element) {
+ new Effect.Appear(element,
+ { duration: 0.4,
+ transition: Effect.Transitions.flicker,
+ afterFinish: function(effect)
+ { effect.element.style.overflow = 'hidden';
+ new Effect.Scale(effect.element, 1,
+ { duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false,
+ afterUpdate: function(effect) {
+ if(effect.element.style.position=="")
+ effect.element.style.position = 'relative'; },
+ afterFinish: function(effect) { Element.hide(effect.element); }
+ } )
+ }
+ } )
+}
+
+Effect.DropOut = function(element) {
+ new Effect.Parallel(
+ [ new Effect.MoveBy(element, 100, 0, { sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ],
+ { duration: 0.5,
+ afterFinish: function(effect)
+ { Element.hide(effect.effects[0].element); }
+ });
+}
+
+Effect.Shake = function(element) {
+ new Effect.MoveBy(element, 0, 20,
+ { duration: 0.05, afterFinish: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new Effect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new Effect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -20,
+ { duration: 0.05, afterFinish: function(effect) {
+ }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+ $(element).style.height = '0px';
+ $(element).style.overflow = 'hidden';
+ $(element).firstChild.style.position = 'relative';
+ Element.show(element);
+ new Effect.Scale(element, 100,
+ { scaleContent: false,
+ scaleX: false,
+ scaleMode: 'contents',
+ scaleFrom: 0,
+ afterUpdate: function(effect)
+ { effect.element.firstChild.style.bottom =
+ (effect.originalHeight - effect.element.clientHeight) + 'px'; }
+ }.extend(arguments[1] || {})
+ );
+}
+
+Effect.SlideUp = function(element) {
+ $(element).style.overflow = 'hidden';
+ $(element).firstChild.style.position = 'relative';
+ Element.show(element);
+ new Effect.Scale(element, 0,
+ { scaleContent: false,
+ scaleX: false,
+ afterUpdate: function(effect)
+ { effect.element.firstChild.style.bottom =
+ (effect.originalHeight - effect.element.clientHeight) + 'px'; },
+ afterFinish: function(effect)
+ { Element.hide(effect.element); }
+ }.extend(arguments[1] || {})
+ );
+}
+
+Effect.Squish = function(element) {
+ new Effect.Scale(element, 0,
+ { afterFinish: function(effect) { Element.hide(effect.element); } });
+}
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ element.style.overflow = 'hidden';
+ Element.show(element);
+
+ var direction = options.direction || 'center';
+ var moveTransition = options.moveTransition || Effect.Transitions.sinoidal;
+ var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal;
+ var opacityTransition = options.opacityTransition || Effect.Transitions.full;
+
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = originalWidth;
+ initialMoveY = moveY = 0;
+ moveX = -originalWidth;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = originalHeight;
+ moveY = -originalHeight;
+ break;
+ case 'bottom-right':
+ initialMoveX = originalWidth;
+ initialMoveY = originalHeight;
+ moveX = -originalWidth;
+ moveY = -originalHeight;
+ break;
+ case 'center':
+ initialMoveX = originalWidth / 2;
+ initialMoveY = originalHeight / 2;
+ moveX = -originalWidth / 2;
+ moveY = -originalHeight / 2;
+ break;
+ }
+
+ new Effect.MoveBy(element, initialMoveY, initialMoveX, {
+ duration: 0.01,
+ beforeUpdate: function(effect) { $(element).style.height = '0px'; },
+ afterFinish: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }),
+ new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }),
+ new Effect.Scale(element, 100, {
+ scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth },
+ sync: true, scaleFrom: 0, scaleTo: 100, transition: scaleTransition })],
+ options); }
+ });
+}
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ element.style.overflow = 'hidden';
+ Element.show(element);
+
+ var direction = options.direction || 'center';
+ var moveTransition = options.moveTransition || Effect.Transitions.sinoidal;
+ var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal;
+ var opacityTransition = options.opacityTransition || Effect.Transitions.none;
+
+ var moveX, moveY;
+
+ switch (direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = originalWidth;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = originalHeight;
+ break;
+ case 'bottom-right':
+ moveX = originalWidth;
+ moveY = originalHeight;
+ break;
+ case 'center':
+ moveX = originalWidth / 2;
+ moveY = originalHeight / 2;
+ break;
+ }
+
+ new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }),
+ new Effect.Scale(element, 0, { sync: true, transition: moveTransition }),
+ new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: scaleTransition }) ],
+ options);
+}
+
+Effect.Pulsate = function(element) {
+ var options = arguments[1] || {};
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ reverser.bind(transition);
+ new Effect.Opacity(element,
+ { duration: 3.0,
+ afterFinish: function(effect) { Element.show(effect.element); }
+ }.extend(options).extend({transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+ $(element).style.overflow = 'hidden';
+ new Effect2.Scale(element, 5, {
+ scaleContent: false,
+ scaleTo: 100,
+ scaleX: false,
+ afterFinish: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleTo: 0,
+ scaleY: false,
+ afterFinish: function(effect) { Element.hide(effect.element) } });
+ }}.extend(arguments[1] || {}));
+}
+
+// old: new Effect.ContentZoom(element, percent)
+// new: Element.setContentZoom(element, percent)
+
+Element.setContentZoom = function(element, percent) {
+ var element = $(element);
+
+ var sizeEm = 1.0;
+ if (element.style.fontSize.indexOf("em")>0)
+ sizeEm = parseFloat(element.style.fontSize);
+
+ element.style.fontSize = sizeEm*(percent/100) + "em";
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
\ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js
index ca772aa8fa5427ebf47904c5fc776886db918d0e..1ebb2a46b7910f302a6fd47af463eccc0f463fd7 100644
--- a/actionpack/lib/action_view/helpers/javascripts/prototype.js
+++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -559,205 +559,279 @@ Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
}
});
-/*--------------------------------------------------------------------------*/
-
-var Effect = new Object();
-Effect.Highlight = Class.create();
-Effect.Highlight.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 153;
- this.finish = 255;
- this.current = this.start;
- this.fade();
- },
-
- fade: function() {
- if (this.isFinished()) return;
- if (this.timer) clearTimeout(this.timer);
- this.highlight(this.element, this.current);
- this.current += 17;
- this.timer = setTimeout(this.fade.bind(this), 250);
- },
-
- isFinished: function() {
- return this.current > this.finish;
- },
-
- highlight: function(element, current) {
- element.style.backgroundColor = "#ffff" + current.toColorPart();
+// === Prototype Extension ====================================================
+
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Element.Class part Copyright (c) 2005 by Rick Olson
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Event = {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.srcElement || event.currentTarget;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if(event.preventDefault)
+ { event.preventDefault(); event.stopPropagation(); }
+ else
+ event.returnValue = false;
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on, traverses the DOM upwards
+ findElement: function(event, tagName) {
+ element = Event.element(event);
+ while(element.tagName.toUpperCase() != tagName.toUpperCase() && element.parentNode)
+ element = element.parentNode;
+ return element;
+ },
+
+ observe: function(element, name, observer) {
+ if(name=='keypress') {
+ if(navigator.appVersion.indexOf('AppleWebKit')>0)
+ { $(element).addEventListener("keydown",observer,false); return; }
+ if($(element).addEventListener) $(element).addEventListener("keypress",observer,false)
+ else if($(element).attachEvent) $(element).attachEvent("onkeydown",observer);
+ } else {
+ if($(element).addEventListener) $(element).addEventListener(name,observer,false)
+ else if($(element).attachEvent) $(element).attachEvent("on" + name,observer);
+ }
}
}
+/*--------------------------------------------------------------------------*/
-Effect.Fade = Class.create();
-Effect.Fade.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 100;
- this.finish = 0;
- this.current = this.start;
- this.fade();
- },
-
- fade: function() {
- if (this.isFinished()) { this.element.style.display = 'none'; return; }
- if (this.timer) clearTimeout(this.timer);
- this.setOpacity(this.element, this.current);
- this.current -= 10;
- this.timer = setTimeout(this.fade.bind(this), 50);
- },
-
- isFinished: function() {
- return this.current <= this.finish;
- },
-
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
+// removes whitespace-only text node children
+// needed to make Gecko-based browsers happy
+Element.cleanWhitespace = function(element) {
+ var element = $(element);
+ for(var i=0;i0)
- this.sizeEm = parseFloat(this.element.style.fontSize);
- if(this.element.effect_scale) {
- clearTimeout(this.element.effect_scale.timer);
- this.startScale = this.element.effect_scale.currentScale;
- this.startHeight = this.element.effect_scale.startHeight;
- this.startWidth = this.element.effect_scale.startWidth;
- if(this.element.effect_scale.sizeEm)
- this.sizeEm = this.element.effect_scale.sizeEm;
- }
- this.element.effect_scale = this;
- this.currentScale = this.startScale;
- this.factor = this.finishScale - this.startScale;
- this.options = arguments[2] || {};
- this.scale();
- },
-
- scale: function() {
- if (this.isFinished()) {
- this.setDimensions(this.element, this.startWidth*this.finishScale, this.startHeight*this.finishScale);
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.finishScale + "em";
- if(this.options.complete) this.options.complete(this);
- return;
- }
- if (this.timer) clearTimeout(this.timer);
- if (this.options.step) this.options.step(this);
- this.setDimensions(this.element, this.currentWidth, this.currentHeight);
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.currentScale + "em";
- this.currentScale += (this.factor/10) /*//*/;
- this.currentWidth = this.startWidth * this.currentScale;
- this.currentHeight = this.startHeight * this.currentScale;
- this.timer = setTimeout(this.scale.bind(this), 50);
- },
-
- isFinished: function() {
- return (this.factor < 0) ?
- this.currentScale <= this.finishScale : this.currentScale >= this.finishScale;
- },
+Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
+ var children = $(element).childNodes;
+ var text = "";
+ var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
- setDimensions: function(element, width, height) {
- element.style.width = width + 'px';
- element.style.height = height + 'px';
+ for (var i = 0; i < children.length; i++) {
+ if(children[i].nodeType==3) {
+ text+=children[i].nodeValue;
+ } else {
+ if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
+ text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
+ }
}
+
+ return text;
}
-Effect.Squish = Class.create();
-Effect.Squish.prototype = {
- initialize: function(element) {
- this.element = $(element);
- new Effect.Scale(this.element, 1, { complete: this.hide.bind(this) } );
- },
- hide: function() {
- this.element.style.display = 'none';
- }
-}
+/*--------------------------------------------------------------------------*/
-Effect.Puff = Class.create();
-Effect.Puff.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.opacity = 100;
- this.startTop = this.element.top || this.element.offsetTop;
- this.startLeft = this.element.left || this.element.offsetLeft;
- new Effect.Scale(this.element, 200, { step: this.fade.bind(this), complete: this.hide.bind(this) } );
- },
- fade: function(effect) {
- topd = (((effect.currentScale)*effect.startHeight) - effect.startHeight)/2;
- leftd = (((effect.currentScale)*effect.startWidth) - effect.startWidth)/2;
- this.element.style.position='absolute';
- this.element.style.top = this.startTop-topd + "px";
- this.element.style.left = this.startLeft-leftd + "px";
- this.opacity -= 10;
- this.setOpacity(this.element, this.opacity);
- if(navigator.appVersion.indexOf('AppleWebKit')>0) this.element.innerHTML += ''; //force redraw on safari
- },
- hide: function() {
- this.element.style.display = 'none';
+Text = {
+ stripTags: function(htmlstr) {
+ return htmlstr.replace(/<\/?[^>]+>/gi,"");
},
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
+ decodeHTML: function(htmlstr) {
+ return htmlstr.replace(/</gi,"<").replace(/>/gi,">").replace(/"/gi,'"').replace(/'/gi,"'").replace(/&/gi,"&").replace(/[\n\r]/gi,"");
}
}
-Effect.Appear = Class.create();
-Effect.Appear.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 0;
- this.finish = 100;
- this.current = this.start;
- this.fade();
- },
-
- fade: function() {
- if (this.isFinished()) return;
- if (this.timer) clearTimeout(this.timer);
- this.setOpacity(this.element, this.current);
- this.current += 10;
- this.timer = setTimeout(this.fade.bind(this), 50);
- },
-
- isFinished: function() {
- return this.current > this.finish;
- },
-
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
- element.style.display = '';
- }
-}
+/*--------------------------------------------------------------------------*/
-Effect.ContentZoom = Class.create();
-Effect.ContentZoom.prototype = {
- initialize: function(element, percent) {
- this.element = $(element);
- if (this.element.style.fontSize=="") this.sizeEm = 1.0;
- if (this.element.style.fontSize.indexOf("em")>0)
- this.sizeEm = parseFloat(this.element.style.fontSize);
- if(this.element.effect_contentzoom) {
- this.sizeEm = this.element.effect_contentzoom.sizeEm;
+Element.Class = {
+ // Element.toggleClass(element, className) toggles the class being on/off
+ // Element.toggleClass(element, className1, className2) toggles between both classes,
+ // defaulting to className1 if neither exist
+ toggle: function(element, className) {
+ if(Element.Class.has(element, className)) {
+ Element.Class.remove(element, className);
+ if(arguments.length == 3) Element.Class.add(element, arguments[2]);
+ } else {
+ Element.Class.add(element, className);
+ if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
+ }
+ },
+
+ // gets space-delimited classnames of an element as an array
+ get: function(element) {
+ element = $(element);
+ return element.className.split(' ');
+ },
+
+ // functions adapted from original functions by Gavin Kistner
+ remove: function(element) {
+ element = $(element);
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g');
+ element.className = element.className.replace(regEx, '')
+ }
+ },
+
+ add: function(element) {
+ element = $(element);
+ for(var i = 1; i < arguments.length; i++) {
+ Element.Class.remove(element, arguments[i]);
+ element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
+ }
+ },
+
+ // returns true if all given classes exist in said element
+ has: function(element) {
+ element = $(element);
+ if(!element || !element.className) return false;
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ regEx = new RegExp("\\b" + arguments[i] + "\\b");
+ if(!regEx.test(element.className)) return false;
+ }
+ return true;
+ },
+
+ // expects arrays of strings and/or strings as optional paramters
+ // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
+ has_any: function(element) {
+ element = $(element);
+ if(!element || !element.className) return false;
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ if((typeof arguments[i] == 'object') &&
+ (arguments[i].constructor == Array)) {
+ for(var j = 0; j < arguments[i].length; j++) {
+ regEx = new RegExp("\\b" + arguments[i][j] + "\\b");
+ if(regEx.test(element.className)) return true;
+ }
+ } else {
+ regEx = new RegExp("\\b" + arguments[i] + "\\b");
+ if(regEx.test(element.className)) return true;
+ }
+ }
+ return false;
+ },
+
+ childrenWith: function(element, className) {
+ var children = $(element).getElementsByTagName('*');
+ var elements = new Array();
+
+ for (var i = 0; i < children.length; i++) {
+ if (Element.Class.has(children[i], className)) {
+ elements.push(children[i]);
+ break;
+ }
+ }
+
+ return elements;
}
- this.element.effect_contentzoom = this;
- this.element.style.fontSize = this.sizeEm*(percent/100) + "em" /*//*/;
- if(navigator.appVersion.indexOf('AppleWebKit')>0) { this.element.scrollTop -= 1; };
- }
}
+
+/*--------------------------------------------------------------------------*/
+
+var Position = {
+ // must be called before calling within_including_scrolloffset, every time the page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
+ this.deltaY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
+ this.include_scroll_offsets = true;
+ },
+
+ real_offset: function(element) {
+ var valueT = 0; var valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while(element);
+ return [valueL, valueT];
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if(this.include_scroll_offsets)
+ return within_including_scrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ var offsettop = element.offsetTop;
+ var offsetleft = element.offsetLeft;
+ return (y>=offsettop &&
+ y=offsetleft &&
+ x=offsettop &&
+ y=offsetleft &&
+ x),
+ assert_equal %(