Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Update script.aculo.us to 1.5_rc2, and Prototype to 1.4.0_pre7

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2386 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 516dc2c0f16cf187f981b5e8648a7f7f1b31d190 1 parent dd21e9a
@madrobby madrobby authored
Showing with 4,584 additions and 1,507 deletions.
  1. +6 −1 actionpack/lib/action_view/helpers/asset_tag_helper.rb
  2. +34 −4 actionpack/lib/action_view/helpers/java_script_macros_helper.rb
  3. +0 −28 actionpack/lib/action_view/helpers/javascript_helper.rb
  4. +416 −159 actionpack/lib/action_view/helpers/javascripts/controls.js
  5. +238 −271 actionpack/lib/action_view/helpers/javascripts/dragdrop.js
  6. +460 −256 actionpack/lib/action_view/helpers/javascripts/effects.js
  7. +350 −49 actionpack/lib/action_view/helpers/javascripts/prototype.js
  8. +258 −0 actionpack/lib/action_view/helpers/javascripts/slider.js
  9. +521 −0 actionpack/lib/action_view/helpers/javascripts/util.js
  10. +2 −2 actionpack/test/template/asset_tag_helper_test.rb
  11. +1 −1  actionpack/test/template/java_script_macros_helper_test.rb
  12. +3 −0  actionpack/test/template/javascript_helper_test.rb
  13. +4 −1 railties/Rakefile
  14. +416 −159 railties/html/javascripts/controls.js
  15. +238 −271 railties/html/javascripts/dragdrop.js
  16. +460 −256 railties/html/javascripts/effects.js
  17. +350 −49 railties/html/javascripts/prototype.js
  18. +48 −0 railties/html/javascripts/scriptaculous.js
  19. +258 −0 railties/html/javascripts/slider.js
  20. +521 −0 railties/html/javascripts/util.js
View
7 actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -51,7 +51,12 @@ def javascript_path(source)
# <script type="text/javascript" src="/javascripts/dragdrop.js"></script>
def javascript_include_tag(*sources)
options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
- sources = ['prototype', 'effects', 'controls', 'dragdrop'] if sources.first == :defaults
+ if sources.first == :defaults
+ sources = ['prototype', 'scriptaculous']
+ if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
+ sources << 'application'
+ end
+ end
sources.collect { |source|
source = javascript_path(source)
content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options))
View
38 actionpack/lib/action_view/helpers/java_script_macros_helper.rb
@@ -74,9 +74,8 @@ def in_place_editor_field(object, method, tag_options = {}, in_place_editor_opti
# input field.
#
# Required +options+ are:
- # <tt>:url</tt>:: Specifies the DOM ID of the element whose
- # innerHTML should be updated with the autocomplete
- # entries returned by XMLHttpRequest.
+ # <tt>:url</tt>:: URL to call for autocompletion results
+ # in url_for format.
#
# Addtional +options+ are:
# <tt>:update</tt>:: Specifies the DOM ID of the element whose
@@ -130,10 +129,41 @@ def auto_complete_result(entries, field, phrase = nil)
# See the RDoc on ActionController::AutoComplete to learn more about this.
def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
(completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
- text_field(object, method, { :autocomplete => "off" }.merge!(tag_options)) +
+ text_field(object, method, tag_options) +
content_tag("div", "", :id => "#{object}_#{method}_auto_complete", :class => "auto_complete") +
auto_complete_field("#{object}_#{method}", { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
end
+
+ private
+ def auto_complete_stylesheet
+ content_tag("style", <<-EOT
+ div.auto_complete {
+ width: 350px;
+ background: #fff;
+ }
+ div.auto_complete ul {
+ border:1px solid #888;
+ margin:0;
+ padding:0;
+ width:100%;
+ list-style-type:none;
+ }
+ div.auto_complete ul li {
+ margin:0;
+ padding:3px;
+ }
+ div.auto_complete ul li.selected {
+ background-color: #ffb;
+ }
+ div.auto_complete ul strong.highlight {
+ color: #800;
+ margin:0;
+ padding:0;
+ }
+ EOT
+ )
+ end
+
end
end
end
View
28 actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -498,34 +498,6 @@ def build_callbacks(options)
callbacks
end
- def auto_complete_stylesheet
- content_tag("style", <<-EOT
- div.auto_complete {
- width: 350px;
- background: #fff;
- }
- div.auto_complete ul {
- border:1px solid #888;
- margin:0;
- padding:0;
- width:100%;
- list-style-type:none;
- }
- div.auto_complete ul li {
- margin:0;
- padding:3px;
- }
- div.auto_complete ul li.selected {
- background-color: #ffb;
- }
- div.auto_complete ul strong.highlight {
- color: #800;
- margin:0;
- padding:0;
- }
- EOT
- )
- end
end
JavascriptHelper = JavaScriptHelper unless const_defined? :JavascriptHelper
View
575 actionpack/lib/action_view/helpers/javascripts/controls.js
@@ -1,41 +1,12 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
//
-// 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.
-
-Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
- var children = $(element).childNodes;
- var text = "";
- var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
-
- 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;
-}
+// See scriptaculous.js for full license.
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
@@ -46,7 +17,7 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
-// invoking this.getEntry(), NOT by directly accessing
+// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
@@ -57,7 +28,7 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
-// a token array, e.g. { tokens: new Array (',', '\n') } which
+// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
@@ -65,57 +36,54 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
var Autocompleter = {}
Autocompleter.Base = function() {};
Autocompleter.Base.prototype = {
- base_initialize: function(element, update, options) {
+ baseInitialize: function(element, update, options) {
this.element = $(element);
this.update = $(update);
- this.has_focus = false;
+ this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
- this.entry_count = 0;
+ this.entryCount = 0;
if (this.setOptions)
this.setOptions(options);
else
- this.options = {}
-
- this.options.tokens = this.options.tokens || new Array();
+ this.options = options || {};
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
- this.options.min_chars = this.options.min_chars || 1;
+ this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
- var offsets = Position.cumulativeOffset(element);
- update.style.left = offsets[0] + 'px';
- update.style.top = (offsets[1] + element.offsetHeight) + 'px';
- update.style.width = element.offsetWidth + 'px';
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
}
new Effect.Appear(update,{duration:0.15});
};
this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
-
- if(this.options.indicator)
- this.indicator = $(this.options.indicator);
if (typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
-
+
this.observer = null;
+ this.element.setAttribute('autocomplete','off');
+
Element.hide(this.update);
-
+
Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
},
show: function() {
- if(this.update.style.display=='none') this.options.onShow(this.element, this.update);
- if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && this.update.style.position=='absolute') {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
- 'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
this.iefix = $(this.update.id+'_iefix');
}
@@ -126,18 +94,18 @@ Autocompleter.Base.prototype = {
Element.show(this.iefix);
}
},
-
+
hide: function() {
- if(this.update.style.display=='') this.options.onHide(this.element, this.update);
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
if(this.iefix) Element.hide(this.iefix);
},
-
+
startIndicator: function() {
- if(this.indicator) Element.show(this.indicator);
+ if(this.options.indicator) Element.show(this.options.indicator);
},
-
+
stopIndicator: function() {
- if(this.indicator) Element.hide(this.indicator);
+ if(this.options.indicator) Element.hide(this.options.indicator);
},
onKeyPress: function(event) {
@@ -145,22 +113,23 @@ Autocompleter.Base.prototype = {
switch(event.keyCode) {
case Event.KEY_TAB:
case Event.KEY_RETURN:
- this.select_entry();
+ this.selectEntry();
Event.stop(event);
case Event.KEY_ESC:
this.hide();
this.active = false;
+ Event.stop(event);
return;
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_UP:
- this.mark_previous();
+ this.markPrevious();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
case Event.KEY_DOWN:
- this.mark_next();
+ this.markNext();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
@@ -168,15 +137,15 @@ Autocompleter.Base.prototype = {
else
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
return;
-
+
this.changed = true;
- this.has_focus = true;
-
+ this.hasFocus = true;
+
if(this.observer) clearTimeout(this.observer);
this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
-
+
onHover: function(event) {
var element = Event.findElement(event, 'LI');
if(this.index != element.autocompleteIndex)
@@ -190,92 +159,97 @@ Autocompleter.Base.prototype = {
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
- this.select_entry();
- Event.stop(event);
+ this.selectEntry();
+ this.hide();
},
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
- this.has_focus = false;
+ this.hasFocus = false;
this.active = false;
},
render: function() {
- if(this.entry_count > 0) {
- for (var i = 0; i < this.entry_count; i++)
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
this.index==i ?
- Element.addClassName(this.get_entry(i),"selected") :
- Element.removeClassName(this.get_entry(i),"selected");
-
- if(this.has_focus) {
- if(this.get_current_entry().scrollIntoView)
- this.get_current_entry().scrollIntoView(false);
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+ if(this.hasFocus) {
this.show();
this.active = true;
}
} else this.hide();
},
- mark_previous: function() {
+ markPrevious: function() {
if(this.index > 0) this.index--
- else this.index = this.entry_count-1;
+ else this.index = this.entryCcount-1;
},
- mark_next: function() {
- if(this.index < this.entry_count-1) this.index++
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
else this.index = 0;
},
- get_entry: function(index) {
+ getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
- get_current_entry: function() {
- return this.get_entry(this.index);
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
},
- select_entry: function() {
+ selectEntry: function() {
this.active = false;
- value = Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal').unescapeHTML();
- this.updateElement(value);
- this.element.focus();
+ this.updateElement(this.getCurrentEntry());
},
- updateElement: function(value) {
- var last_token_pos = this.findLastToken();
- if (last_token_pos != -1) {
- var new_value = this.element.value.substr(0, last_token_pos + 1);
- var whitespace = this.element.value.substr(last_token_pos + 1).match(/^\s+/);
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+
+ var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+ var lastTokenPos = this.findLastToken();
+ if (lastTokenPos != -1) {
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
if (whitespace)
- new_value += whitespace[0];
- this.element.value = new_value + value;
+ newValue += whitespace[0];
+ this.element.value = newValue + value;
} else {
this.element.value = value;
- }
+ }
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
},
-
+
updateChoices: function(choices) {
- if(!this.changed && this.has_focus) {
+ if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
Element.cleanWhitespace(this.update.firstChild);
if(this.update.firstChild && this.update.firstChild.childNodes) {
- this.entry_count =
+ this.entryCount =
this.update.firstChild.childNodes.length;
- for (var i = 0; i < this.entry_count; i++) {
- entry = this.get_entry(i);
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
- this.entry_count = 0;
+ this.entryCount = 0;
}
-
+
this.stopIndicator();
-
+
this.index = 0;
this.render();
}
@@ -288,7 +262,7 @@ Autocompleter.Base.prototype = {
onObserverEvent: function() {
this.changed = false;
- if(this.getEntry().length>=this.options.min_chars) {
+ if(this.getToken().length>=this.options.minChars) {
this.startIndicator();
this.getUpdatedChoices();
} else {
@@ -297,58 +271,56 @@ Autocompleter.Base.prototype = {
}
},
- getEntry: function() {
- var token_pos = this.findLastToken();
- if (token_pos != -1)
- var ret = this.element.value.substr(token_pos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+ getToken: function() {
+ var tokenPos = this.findLastToken();
+ if (tokenPos != -1)
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
else
var ret = this.element.value;
-
+
return /\n/.test(ret) ? '' : ret;
},
findLastToken: function() {
- var last_token_pos = -1;
+ var lastTokenPos = -1;
for (var i=0; i<this.options.tokens.length; i++) {
- var this_token_pos = this.element.value.lastIndexOf(this.options.tokens[i]);
- if (this_token_pos > last_token_pos)
- last_token_pos = this_token_pos;
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+ if (thisTokenPos > lastTokenPos)
+ lastTokenPos = thisTokenPos;
}
- return last_token_pos;
+ return lastTokenPos;
}
}
Ajax.Autocompleter = Class.create();
-Ajax.Autocompleter.prototype = Object.extend(new Autocompleter.Base(),
-Object.extend(new Ajax.Base(), {
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
initialize: function(element, update, url, options) {
- this.base_initialize(element, update, options);
+ this.baseInitialize(element, update, options);
this.options.asynchronous = true;
- this.options.onComplete = this.onComplete.bind(this)
- this.options.method = 'post';
+ this.options.onComplete = this.onComplete.bind(this);
this.options.defaultParams = this.options.parameters || null;
this.url = url;
},
-
+
getUpdatedChoices: function() {
- entry = encodeURIComponent(this.element.name) + '=' +
- encodeURIComponent(this.getEntry());
-
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
-
+
if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
-
+
new Ajax.Request(this.url, this.options);
},
-
+
onComplete: function(request) {
this.updateChoices(request.responseText);
}
-}));
+});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
@@ -362,22 +334,22 @@ Object.extend(new Ajax.Base(), {
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
-// - partial_search - If false, the autocompleter will match entered
+// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
-// the option full_search to true (default: off).
+// the option fullSearch to true (default: off).
//
-// - full_search - Search anywhere in autocomplete array strings.
+// - fullSsearch - Search anywhere in autocomplete array strings.
//
-// - partial_chars - How many characters to enter before triggering
-// a partial match (unlike min_chars, which defines
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
-// - ignore_case - Whether to ignore case when autocompleting.
+// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
@@ -388,7 +360,7 @@ Object.extend(new Ajax.Base(), {
Autocompleter.Local = Class.create();
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
initialize: function(element, update, array, options) {
- this.base_initialize(element, update, options);
+ this.baseInitialize(element, update, options);
this.options.array = array;
},
@@ -399,41 +371,42 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
setOptions: function(options) {
this.options = Object.extend({
choices: 10,
- partial_search: true,
- partial_chars: 2,
- ignore_case: true,
- full_search: false,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
selector: function(instance) {
- var ret = new Array(); // Beginning matches
- var partial = new Array(); // Inside matches
- var entry = instance.getEntry();
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
var count = 0;
-
+
for (var i = 0; i < instance.options.array.length &&
- ret.length < instance.options.choices ; i++) {
+ ret.length < instance.options.choices ; i++) {
+
var elem = instance.options.array[i];
- var found_pos = instance.options.ignore_case ?
+ var foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
- while (found_pos != -1) {
- if (found_pos == 0 && elem.length != entry.length) {
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
- } else if (entry.length >= instance.options.partial_chars &&
- instance.options.partial_search && found_pos != -1) {
- if (instance.options.full_search || /\s/.test(elem.substr(found_pos-1,1))) {
- partial.push("<li>" + elem.substr(0, found_pos) + "<strong>" +
- elem.substr(found_pos, entry.length) + "</strong>" + elem.substr(
- found_pos + entry.length) + "</li>");
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
break;
}
}
- found_pos = instance.options.ignore_case ?
- elem.toLowerCase().indexOf(entry.toLowerCase(), found_pos + 1) :
- elem.indexOf(entry, found_pos + 1);
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
}
}
@@ -444,3 +417,287 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
}, options || {});
}
});
+
+// AJAX in-place editor
+//
+// The constructor takes three parameters. The first is the element
+// that should support in-place editing. The second is the url to submit
+// the changed value to. The server should respond with the updated
+// value (the server might have post-processed it or validation might
+// have prevented it from changing). The third is a hash of options.
+//
+// Supported options are (all are optional and have sensible defaults):
+// - okText - The text of the submit button that submits the changed value
+// to the server (default: "ok")
+// - cancelText - The text of the link that cancels editing (default: "cancel")
+// - savingText - The text being displayed as the AJAX engine communicates
+// with the server (default: "Saving...")
+// - formId - The id given to the <form> element
+// (default: the id of the element to edit plus '-inplaceeditor')
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ okText: "ok",
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ handleLineBreaks: true,
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ ajaxOptions: {}
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function() {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.form = this.getForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ Field.focus(this.editField);
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ },
+ getForm: function() {
+ form = document.createElement("form");
+ form.id = this.options.formId;
+ Element.addClassName(form, this.options.formClassName)
+ form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField(form);
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ form.appendChild(br);
+ }
+
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ form.appendChild(okButton);
+
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ form.appendChild(cancelLink);
+ return form;
+ },
+ hasHTMLLineBreaks: function(string) {
+ if (!this.options.handleLineBreaks) return false;
+ return string.match(/<br/i) || string.match(/<p>/i);
+ },
+ convertHTMLLineBreaks: function(string) {
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+ },
+ createEditField: function(form) {
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(this.getText())) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.type = "text";
+ textField.name = "value";
+ textField.value = this.getText();
+ textField.style.backgroundColor = this.options.highlightcolor;
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0)
+ textField.size = size;
+ form.appendChild(textField);
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.name = "value";
+ textArea.value = this.convertHTMLLineBreaks(this.getText());
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ form.appendChild(textArea);
+ this.editField = textArea;
+ }
+ },
+ getText: function() {
+ if (this.options.loadTextURL) {
+ this.loadExternalText();
+ return this.options.loadingText;
+ } else {
+ return this.element.innerHTML;
+ }
+ },
+ loadExternalText: function() {
+ new Ajax.Request(
+ this.options.loadTextURL,
+ {
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ this.form.value.value = transport.responseText.stripTags();
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ this.saving = true;
+ new Ajax.Updater(
+ {
+ success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null
+ },
+ this.url,
+ Object.extend({
+ parameters: this.options.callback(this.form, this.editField.value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ this.onLoading();
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new Effect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
View
509 actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -2,193 +2,79 @@
//
// 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.
-
-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;
- }
-}
+// See scriptaculous.js for full license.
/*--------------------------------------------------------------------------*/
var Droppables = {
- drops: false,
-
+ drops: [],
+
remove: function(element) {
- for(var i = 0; i < this.drops.length; i++)
- if(this.drops[i].element == element)
- this.drops.splice(i,1);
+ this.drops = this.drops.reject(function(e) { return e==element });
},
-
+
add: function(element) {
- var element = $(element);
+ element = $(element);
var options = Object.extend({
greedy: true,
hoverclass: null
}, arguments[1] || {});
-
+
// cache containers
if(options.containment) {
- options._containers = new Array();
+ options._containers = [];
var containment = options.containment;
if((typeof containment == 'object') &&
(containment.constructor == Array)) {
- for(var i=0; i<containment.length; i++)
- options._containers.push($(containment[i]));
+ containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
}
- options._containers_length =
- options._containers.length-1;
}
-
+
Element.makePositioned(element); // fix IE
-
options.element = element;
-
- // activate the droppable
- if(!this.drops) this.drops = [];
+
this.drops.push(options);
},
-
- is_contained: function(element, drop) {
- var containers = drop._containers;
+
+ isContained: function(element, drop) {
var parentNode = element.parentNode;
- var i = drop._containers_length;
- do { if(parentNode==containers[i]) return true; } while (i--);
- return false;
+ return drop._containers.detect(function(c) { return parentNode == c });
},
-
- is_affected: function(pX, pY, element, drop) {
+
+ isAffected: function(pX, pY, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
- this.is_contained(element, drop)) &&
+ this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.Class.has_any(element, drop.accept))) &&
Position.within(drop.element, pX, pY) );
},
-
+
deactivate: function(drop) {
- Element.Class.remove(drop.element, drop.hoverclass);
+ if(drop.hoverclass)
+ Element.Class.remove(drop.element, drop.hoverclass);
this.last_active = null;
},
-
+
activate: function(drop) {
if(this.last_active) this.deactivate(this.last_active);
- if(drop.hoverclass) {
+ if(drop.hoverclass)
Element.Class.add(drop.element, drop.hoverclass);
- this.last_active = drop;
- }
+ this.last_active = drop;
},
-
+
show: function(event, element) {
- if(!this.drops) return;
+ if(!this.drops.length) return;
var pX = Event.pointerX(event);
var pY = Event.pointerY(event);
Position.prepare();
-
+
var i = this.drops.length-1; do {
var drop = this.drops[i];
- if(this.is_affected(pX, pY, element, drop)) {
+ if(this.isAffected(pX, pY, element, drop)) {
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
if(drop.greedy) {
@@ -197,43 +83,41 @@ var Droppables = {
}
}
} while (i--);
+
+ if(this.last_active) this.deactivate(this.last_active);
},
-
+
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
-
- if (this.is_affected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
+
+ if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
if (this.last_active.onDrop)
- this.last_active.onDrop(element, this.last_active);
-
+ this.last_active.onDrop(element, this.last_active.element);
},
-
+
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
}
-Draggables = {
- observers: new Array(),
+var Draggables = {
+ observers: [],
addObserver: function(observer) {
this.observers.push(observer);
},
removeObserver: function(element) { // element instead of obsever fixes mem leaks
- for(var i = 0; i < this.observers.length; i++)
- if(this.observers[i].element && (this.observers[i].element == element))
- this.observers.splice(i,1);
+ this.observers = this.observers.reject( function(o) { return o.element==element });
},
notify: function(eventName, draggable) { // 'onStart', 'onEnd'
- for(var i = 0; i < this.observers.length; i++)
- this.observers[i][eventName](draggable);
+ this.observers.invoke(eventName, draggable);
}
}
/*--------------------------------------------------------------------------*/
-Draggable = Class.create();
+var Draggable = Class.create();
Draggable.prototype = {
initialize: function(element) {
var options = Object.extend({
@@ -242,7 +126,8 @@ Draggable.prototype = {
new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
},
reverteffect: function(element, top_offset, left_offset) {
- new Effect.MoveBy(element, -top_offset, -left_offset, {duration:0.4});
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
},
endeffect: function(element) {
new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
@@ -250,12 +135,12 @@ Draggable.prototype = {
zindex: 1000,
revert: false
}, arguments[1] || {});
-
+
this.element = $(element);
this.handle = options.handle ? $(options.handle) : this.element;
-
- Element.makePositioned(this.element); // fix IE
-
+
+ Element.makePositioned(this.element); // fix IE
+
this.offsetX = 0;
this.offsetY = 0;
this.originalLeft = this.currentLeft();
@@ -263,27 +148,34 @@ Draggable.prototype = {
this.originalX = this.element.offsetLeft;
this.originalY = this.element.offsetTop;
this.originalZ = parseInt(this.element.style.zIndex || "0");
-
+
this.options = options;
-
+
this.active = false;
this.dragging = false;
-
+
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.update.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
- Event.observe(this.handle, "mousedown", this.eventMouseDown);
+ this.registerEvents();
+ },
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ this.unregisterEvents();
+ },
+ registerEvents: function() {
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
},
- destroy: function() {
- Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
- Event.stopObserving(document, "mouseup", this.eventMouseUp);
- Event.stopObserving(document, "mousemove", this.eventMouseMove);
- Event.stopObserving(document, "keypress", this.eventKeypress);
+ unregisterEvents: function() {
+ //if(!this.active) return;
+ //Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ //Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ //Event.stopObserving(document, "keypress", this.eventKeypress);
},
currentLeft: function() {
return parseInt(this.element.style.left || '0');
@@ -293,27 +185,42 @@ Draggable.prototype = {
},
startDrag: function(event) {
if(Event.isLeftClick(event)) {
- this.active = true;
- var style = this.element.style;
- this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
- this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
- this.offsetY = event.clientY - this.originalY - this.originalTop;
- this.offsetX = event.clientX - this.originalX - this.originalLeft;
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if(src.tagName && (
+ src.tagName=='INPUT' ||
+ src.tagName=='SELECT' ||
+ src.tagName=='BUTTON' ||
+ src.tagName=='TEXTAREA')) return;
+ // this.registerEvents();
+ this.active = true;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.element);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
Event.stop(event);
}
},
finishDrag: function(event, success) {
+ // this.unregisterEvents();
+
this.active = false;
this.dragging = false;
-
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
if(success) Droppables.fire(event, this.element);
Draggables.notify('onEnd', this);
-
+
var revert = this.options.revert;
if(revert && typeof revert == 'function') revert = revert(this.element);
-
+
if(revert && this.options.reverteffect) {
this.options.reverteffect(this.element,
this.currentTop()-this.originalTop,
@@ -322,12 +229,13 @@ Draggable.prototype = {
this.originalLeft = this.currentLeft();
this.originalTop = this.currentTop();
}
-
+
this.element.style.zIndex = this.originalZ;
-
+
if(this.options.endeffect)
this.options.endeffect(this.element);
-
+
+
Droppables.reset();
},
keyPress: function(event) {
@@ -347,13 +255,15 @@ Draggable.prototype = {
this.dragging = false;
},
draw: function(event) {
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.element);
+ offsets[0] -= this.currentLeft();
+ offsets[1] -= this.currentTop();
var style = this.element.style;
- this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
- this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
- style.left = ((event.clientX - this.originalX) - this.offsetX) + "px";
+ style.left = (pointer[0] - offsets[0] - this.offsetX) + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
- style.top = ((event.clientY - this.originalY) - this.offsetY) + "px";
+ style.top = (pointer[1] - offsets[1] - this.offsetY) + "px";
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
update: function(event) {
@@ -363,17 +273,24 @@ Draggable.prototype = {
this.dragging = true;
if(style.position=="") style.position = "relative";
style.zIndex = this.options.zindex;
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
Draggables.notify('onStart', this);
if(this.options.starteffect) this.options.starteffect(this.element);
}
-
+
Droppables.show(event, this.element);
this.draw(event);
if(this.options.change) this.options.change(this);
-
+
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-
+
Event.stop(event);
}
}
@@ -381,7 +298,7 @@ Draggable.prototype = {
/*--------------------------------------------------------------------------*/
-SortableObserver = Class.create();
+var SortableObserver = Class.create();
SortableObserver.prototype = {
initialize: function(element, observer) {
this.element = $(element);
@@ -391,147 +308,197 @@ SortableObserver.prototype = {
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
- onEnd: function() {
+ onEnd: function() {
+ Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
}
-Sortable = {
+var Sortable = {
sortables: new Array(),
options: function(element){
- var element = $(element);
- for(var i=0;i<this.sortables.length;i++)
- if(this.sortables[i].element == element)
- return this.sortables[i];
- return null;
+ element = $(element);
+ return this.sortables.detect(function(s) { return s.element == element });
},
destroy: function(element){
- var element = $(element);
- for(var i=0;i<this.sortables.length;i++) {
- if(this.sortables[i].element == element) {
- var s = this.sortables[i];
- Draggables.removeObserver(s.element);
- for(var j=0;j<s.droppables.length;j++)
- Droppables.remove(s.droppables[j]);
- for(var j=0;j<s.draggables.length;j++)
- s.draggables[j].destroy();
- this.sortables.splice(i,1);
- }
- }
+ element = $(element);
+ this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+ });
+ this.sortables = this.sortables.reject(function(s) { return s.element == element });
},
create: function(element) {
- var element = $(element);
+ element = $(element);
var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false, // fixme: unimplemented
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,
+ ghosting: false,
onChange: function() {},
onUpdate: function() {}
}, arguments[1] || {});
-
+
// clear any old sortable with same element
this.destroy(element);
-
+
// build options for the draggables
var options_for_draggable = {
revert: true,
+ ghosting: options.ghosting,
constraint: options.constraint,
- handle: handle };
+ handle: options.handle };
+
if(options.starteffect)
options_for_draggable.starteffect = options.starteffect;
+
if(options.reverteffect)
options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
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);
- }
- }
- }
+ onHover: Sortable.onHover,
+ greedy: !options.dropOnEmpty
}
// fix for gecko engine
Element.cleanWhitespace(element);
-
+
options.draggables = [];
options.droppables = [];
-
- // 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];
-
- options.draggables.push(new Draggable(elements[i], Object.extend(options_for_draggable, { handle: handle })));
-
- Droppables.add(elements[i], options_for_droppable);
- options.droppables.push(elements[i]);
-
- }
-
+
+ // make it so
+
+ // drop on empty handling
+ if(options.dropOnEmpty) {
+ Droppables.add(element,
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+ options.droppables.push(element);
+ }
+
+ (this.findElements(element, options) || []).each( function(e) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.Class.childrenWith(e, options.handle)[0] : e;
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ options.droppables.push(e);
+ });
+
// keep reference
this.sortables.push(options);
-
+
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
},
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ if(!element.hasChildNodes()) return null;
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName==options.tag.toUpperCase() &&
+ (!options.only || (Element.Class.has(e, options.only))))
+ elements.push(e);
+ if(options.tree) {
+ var grandchildren = this.findElements(e, options);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : null);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ 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)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon) {
+ if(element.parentNode!=dropon) {
+ dropon.appendChild(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Element.hide(Sortable._marker);
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
+ Element.hide(Sortable._marker);
+ Element.Class.add(Sortable._marker, 'dropmarker');
+ Sortable._marker.style.position = 'absolute';
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.style.top = offsets[1] + 'px';
+ if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+ Sortable._marker.style.left = offsets[0] + 'px';
+ Element.show(Sortable._marker);
+ },
+
serialize: function(element) {
- var element = $(element);
+ element = $(element);
var sortableOptions = this.options(element);
var options = Object.extend({
tag: sortableOptions.tag,
only: sortableOptions.only,
name: element.id
}, arguments[1] || {});
-
- var items = $(element).childNodes;
- var queryComponents = new Array();
-
- for(var i=0; i<items.length; i++)
- if(items[i].tagName && items[i].tagName==options.tag.toUpperCase() &&
- (!options.only || (Element.Class.has(items[i], options.only))))
- queryComponents.push(
- encodeURIComponent(options.name) + "[]=" +
- encodeURIComponent(items[i].id.split("_")[1]));
-
- return queryComponents.join("&");
+ return $A(element.childNodes).collect( function(item) {
+ return (encodeURIComponent(options.name) + "[]=" +
+ encodeURIComponent(item.id.split("_")[1]));
+ }).join("&");
}
-}
+}
View
716 actionpack/lib/action_view/helpers/javascripts/effects.js
@@ -1,30 +1,51 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//
-// Parts (c) 2005 Justin Palmer (http://encytemedia.com/)
-// Parts (c) 2005 Mark Pilgrim (http://diveintomark.org/)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
//
-// 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.
+// See scriptaculous.js for full license.
+
+var Effect = {
+ tagifyText: function(element) {
+ var tagifyStyle = "position:relative";
+ if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1";
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if(child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ character == " " ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var speed = options.speed;
+ var delay = options.delay;
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: delay + index * speed }));
+ });
+ }
+};
-Effect = {}
-Effect2 = Effect; // deprecated
+var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
@@ -40,7 +61,7 @@ Effect.Transitions.reverse = function(pos) {
return 1-pos;
}
Effect.Transitions.flicker = function(pos) {
- return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random(0.25);
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
}
Effect.Transitions.wobble = function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
@@ -56,73 +77,109 @@ Effect.Transitions.full = function(pos) {
return 1;
}
-/* ------------- element ext -------------- */
-
-Element.makePositioned = function(element) {
- element = $(element);
- if(element.style.position == "")
- element.style.position = "relative";
-}
-
-Element.makeClipping = function(element) {
- element = $(element);
- element._overflow = element.style.overflow || 'visible';
- if(element._overflow!='hidden') element.style.overflow = 'hidden';
-}
+/* ------------- core effects ------------- */
-Element.undoClipping = function(element) {
- element = $(element);
- if(element._overflow!='hidden') element.style.overflow = element._overflow;
+Effect.Queue = {
+ effects: [],
+ interval: null,
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ switch(effect.options.queue) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+ this.effects.push(effect);
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ this.effects.invoke('loop', timePos);
+ }
}
-/* ------------- core effects ------------- */
-
Effect.Base = function() {};
Effect.Base.prototype = {
setOptions: function(options) {
this.options = Object.extend({
transition: Effect.Transitions.sinoidal,
duration: 1.0, // seconds
- fps: 25.0, // max. 100fps
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
sync: false, // true for combining
from: 0.0,
- to: 1.0
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
}, options || {});
},
start: function(options) {
this.setOptions(options || {});
this.currentFrame = 0;
- this.startOn = new Date().getTime();
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
this.finishOn = this.startOn + (this.options.duration*1000);
- if(this.options.beforeStart) this.options.beforeStart(this);
- if(!this.options.sync) this.loop();
+ this.event('beforeStart');
+ if(!this.options.sync) Effect.Queue.add(this);
},
- loop: function() {
- var timePos = new Date().getTime();
- if(timePos >= this.finishOn) {
- this.render(this.options.to);
- if(this.finish) this.finish();
- if(this.options.afterFinish) this.options.afterFinish(this);
- return;
- }
- var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
- var frame = Math.round(pos * this.options.fps * this.options.duration);
- if(frame > this.currentFrame) {
- this.render(pos);
- this.currentFrame = frame;
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if(this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var 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.state == 'idle') {
+ this.state = 'running';
+ this.event('beforeSetup');
+ if(this.setup) this.setup();
+ this.event('afterSetup');
+ }
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
- pos += this.options.from;
- if(this.options.beforeUpdate) this.options.beforeUpdate(this);
+ pos += this.options.from;
+ this.event('beforeUpdate');
if(this.update) this.update(pos);
- if(this.options.afterUpdate) this.options.afterUpdate(this);
+ this.event('afterUpdate');
},
cancel: function() {
- if(this.timeout) clearTimeout(this.timeout);
+ if(!this.options.sync) Effect.Queue.remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if(this.options[eventName]) this.options[eventName](this);
}
}
@@ -133,35 +190,36 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
this.start(arguments[1]);
},
update: function(position) {
- for (var i = 0; i < this.effects.length; i++)
- this.effects[i].render(position);
+ this.effects.invoke('render', position);
},
finish: function(position) {
- for (var i = 0; i < this.effects.length; i++)
- if(this.effects[i].finish) this.effects[i].finish(position);
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if(effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
}
});
-// Internet Explorer caveat: works only on elements the have
-// a 'layout', meaning having a given width or height.
-// There is no way to safely set this automatically.
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
- options = Object.extend({
- from: 0.0,
+
+ // make this work on IE on elements without 'layout'
+ if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+ this.element.style.zoom = 1;
+
+ var options = Object.extend({
+ from: Element.getOpacity(this.element) || 0.0,
to: 1.0
}, 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+")";
+ Element.setOpacity(this.element, position);
}
});
@@ -169,16 +227,23 @@ Effect.MoveBy = Class.create();
Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
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;
- Element.makePositioned(this.element);
this.start(arguments[3]);
},
+ setup: function() {
+ // Bug in Opera: Opera returns the "real" position of a static element or
+ // relative element that does not have top/left explicitly set.
+ // ==> Always set top and left for position relative elements in your stylesheets
+ // (to 0 if you do not need them)
+
+ Element.makePositioned(this.element);
+ this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
+ this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
+ },
update: function(position) {
- topd = this.toTop * position + this.originalTop;
- leftd = this.toLeft * position + this.originalLeft;
+ var topd = this.toTop * position + this.originalTop;
+ var leftd = this.toLeft * position + this.originalLeft;
this.setPosition(topd, leftd);
},
setPosition: function(topd, leftd) {
@@ -191,50 +256,76 @@ Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
initialize: function(element, percent) {
this.element = $(element)
- options = Object.extend({
+ var options = Object.extend({
scaleX: true,
scaleY: true,
scaleContent: true,
scaleFromCenter: false,
scaleMode: 'box', // 'box' or 'contents' or {} with provided values
- scaleFrom: 100.0
+ scaleFrom: 100.0,
+ scaleTo: percent
}, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = Element.getStyle(this.element,'position');
+ this.elementStyleTop = this.element.style.top;
+ this.elementStyleLeft = this.element.style.left;
+ this.elementStyleWidth = this.element.style.width;
+ this.elementStyleHeight = this.element.style.height;
+ this.elementStyleFontSize = this.element.style.fontSize;
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') {
+ var fontSize = Element.getStyle(this.element,'font-size') || "100%";
+ if(fontSize.indexOf("em")>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = "em";
+ } else if(fontSize.indexOf("px")>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = "px";
+ } else if(fontSize.indexOf("%")>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = "%";
+ }
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+ if(this.options.scaleMode=='box') {
this.originalHeight = this.element.clientHeight;
this.originalWidth = this.element.clientWidth;
- } else
- if(options.scaleMode=='contents') {
+ } else if(this.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.originalHeight = this.options.scaleMode.originalHeight;
+ this.originalWidth = this.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";
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.fontSize)
+ this.element.style.fontSize = this.fontSize*currentScale + this.fontSizeType;
this.setDimensions(
this.originalWidth * currentScale,
this.originalHeight * currentScale);
},
-
+ finish: function(position) {
+ if (this.restoreAfterFinish) {
+ var els = this.element.style;
+ els.top = this.elementStyleTop;
+ els.left = this.elementStyleLeft;
+ els.width = this.elementStyleWidth;
+ els.height = this.elementStyleHeight;
+ els.height = this.elementStyleHeight;
+ els.fontSize = this.elementStyleFontSize;
+ }
+ },
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') {
+ var topd = (height - this.originalHeight)/2;
+ var leftd = (width - this.originalWidth)/2;
+ if(this.elementPositioning == '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 {
@@ -249,33 +340,28 @@ Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
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<