From 22b77daeee65aa80061f247a3b36404bacd7dff5 Mon Sep 17 00:00:00 2001 From: Sam Stephenson Date: Sun, 16 Oct 2005 03:25:40 +0000 Subject: Upgrade to Prototype 1.4.0_rc0 git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2638 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/CHANGELOG | 2 + .../action_view/helpers/javascripts/prototype.js | 590 +++++++++++++++------ 2 files changed, 423 insertions(+), 169 deletions(-) (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 74adda0f54..1fe52f40fa 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Upgrade to Prototype 1.4.0_rc0 [Sam Stephenson] + * Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid [Tobias Luetke] * Add temporary support for passing locals to render using string keys [Nicholas Seckar] diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js index ee0cbfce65..120f4cb988 100644 --- a/actionpack/lib/action_view/helpers/javascripts/prototype.js +++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js @@ -1,8 +1,8 @@ -/* Prototype JavaScript framework, version 1.4.0_pre11 +/* Prototype JavaScript framework, version 1.4.0_rc0 * (c) 2005 Sam Stephenson * * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff - * against the source tree, available from the Prototype darcs repository. + * against the source tree, available from the Prototype darcs repository. * * Prototype is freely distributable under the terms of an MIT-style license. * @@ -11,15 +11,15 @@ /*--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.4.0_pre11', - + Version: '1.4.0_rc0', + emptyFunction: function() {}, K: function(x) {return x} } var Class = { create: function() { - return function() { + return function() { this.initialize.apply(this, arguments); } } @@ -69,7 +69,7 @@ Object.extend(Number.prototype, { succ: function() { return this + 1; }, - + times: function(iterator) { $R(0, this, true).each(iterator); return this; @@ -110,10 +110,10 @@ PeriodicalExecuter.prototype = { onTimerEvent: function() { if (!this.currentlyExecuting) { - try { + try { this.currentlyExecuting = true; - this.callback(); - } finally { + this.callback(); + } finally { this.currentlyExecuting = false; } } @@ -130,7 +130,7 @@ function $() { if (typeof element == 'string') element = document.getElementById(element); - if (arguments.length == 1) + if (arguments.length == 1) return element; elements.push(element); @@ -138,7 +138,6 @@ function $() { return elements; } - Object.extend(String.prototype, { stripTags: function() { return this.replace(/<\/?[^>]+>/gi, ''); @@ -156,7 +155,7 @@ Object.extend(String.prototype, { div.innerHTML = this.stripTags(); return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; }, - + toQueryParams: function() { var pairs = this.match(/^\??(.*)$/)[1].split('&'); return pairs.inject({}, function(params, pairString) { @@ -165,7 +164,27 @@ Object.extend(String.prototype, { return params; }); }, - + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + inspect: function() { return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; } @@ -173,7 +192,6 @@ Object.extend(String.prototype, { String.prototype.parseQuery = String.prototype.toQueryParams; - var $break = new Object(); var $continue = new Object(); @@ -192,25 +210,25 @@ var Enumerable = { if (e != $break) throw e; } }, - + all: function(iterator) { var result = true; this.each(function(value, index) { - if (!(result &= (iterator || Prototype.K)(value, index))) + if (!(result &= (iterator || Prototype.K)(value, index))) throw $break; }); return result; }, - + any: function(iterator) { var result = true; this.each(function(value, index) { - if (result &= (iterator || Prototype.K)(value, index)) + if (result &= (iterator || Prototype.K)(value, index)) throw $break; }); return result; }, - + collect: function(iterator) { var results = []; this.each(function(value, index) { @@ -218,7 +236,7 @@ var Enumerable = { }); return results; }, - + detect: function (iterator) { var result; this.each(function(value, index) { @@ -229,7 +247,7 @@ var Enumerable = { }); return result; }, - + findAll: function(iterator) { var results = []; this.each(function(value, index) { @@ -238,7 +256,7 @@ var Enumerable = { }); return results; }, - + grep: function(pattern, iterator) { var results = []; this.each(function(value, index) { @@ -248,7 +266,7 @@ var Enumerable = { }) return results; }, - + include: function(object) { var found = false; this.each(function(value) { @@ -259,21 +277,21 @@ var Enumerable = { }); return found; }, - + inject: function(memo, iterator) { this.each(function(value, index) { memo = iterator(memo, value, index); }); return memo; }, - + invoke: function(method) { var args = $A(arguments).slice(1); return this.collect(function(value) { return value[method].apply(value, args); }); }, - + max: function(iterator) { var result; this.each(function(value, index) { @@ -283,7 +301,7 @@ var Enumerable = { }); return result; }, - + min: function(iterator) { var result; this.each(function(value, index) { @@ -293,16 +311,16 @@ var Enumerable = { }); return result; }, - + partition: function(iterator) { var trues = [], falses = []; this.each(function(value, index) { - ((iterator || Prototype.K)(value, index) ? + ((iterator || Prototype.K)(value, index) ? trues : falses).push(value); }); return [trues, falses]; }, - + pluck: function(property) { var results = []; this.each(function(value, index) { @@ -310,7 +328,7 @@ var Enumerable = { }); return results; }, - + reject: function(iterator) { var results = []; this.each(function(value, index) { @@ -319,7 +337,7 @@ var Enumerable = { }); return results; }, - + sortBy: function(iterator) { return this.collect(function(value, index) { return {value: value, criteria: iterator(value, index)}; @@ -328,11 +346,11 @@ var Enumerable = { return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); }, - + toArray: function() { return this.collect(Prototype.K); }, - + zip: function() { var iterator = Prototype.K, args = $A(arguments); if (typeof args.last() == 'function') @@ -344,7 +362,7 @@ var Enumerable = { return value; }); }, - + inspect: function() { return '#'; } @@ -357,7 +375,6 @@ Object.extend(Enumerable, { member: Enumerable.include, entries: Enumerable.toArray }); - var $A = Array.from = function(iterable) { if (iterable.toArray) { return iterable.toArray(); @@ -376,74 +393,86 @@ Object.extend(Array.prototype, { for (var i = 0; i < this.length; i++) iterator(this[i]); }, - + first: function() { return this[0]; }, - + last: function() { return this[this.length - 1]; }, - + compact: function() { return this.select(function(value) { return value != undefined || value != null; }); }, - + flatten: function() { return this.inject([], function(array, value) { return array.concat(value.constructor == Array ? value.flatten() : [value]); }); }, - + without: function() { var values = $A(arguments); return this.select(function(value) { return !values.include(value); }); }, - + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return false; + }, + + reverse: function() { + var result = []; + for (var i = this.length; i > 0; i--) + result.push(this[i-1]); + return result; + }, + inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; } }); - var Hash = { _each: function(iterator) { for (key in this) { var value = this[key]; if (typeof value == 'function') continue; - + var pair = [key, value]; pair.key = key; pair.value = value; iterator(pair); } }, - + keys: function() { return this.pluck('key'); }, - + values: function() { return this.pluck('value'); }, - + merge: function(hash) { return $H(hash).inject($H(this), function(mergedHash, pair) { mergedHash[pair.key] = pair.value; return mergedHash; }); }, - + toQueryString: function() { return this.map(function(pair) { return pair.map(encodeURIComponent).join('='); }).join('&'); }, - + inspect: function() { return '#= 200 && this.transport.status < 300); }, @@ -563,7 +591,7 @@ Ajax.Base.prototype = { } Ajax.Request = Class.create(); -Ajax.Request.Events = +Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { @@ -579,12 +607,12 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { try { this.url = url; - if (this.options.method == 'get') + if (this.options.method == 'get') this.url += '?' + parameters; - + Ajax.Responders.dispatch('onCreate', this, this.transport); - - this.transport.open(this.options.method, this.url, + + this.transport.open(this.options.method, this.url, this.options.asynchronous); if (this.options.asynchronous) { @@ -602,17 +630,17 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { }, setRequestHeaders: function() { - var requestHeaders = + var requestHeaders = ['X-Requested-With', 'XMLHttpRequest', 'X-Prototype-Version', Prototype.Version]; if (this.options.method == 'post') { - requestHeaders.push('Content-type', + requestHeaders.push('Content-type', 'application/x-www-form-urlencoded'); /* Force "Connection: close" for Mozilla browsers to work around * a bug where XMLHttpReqeuest sends an incorrect Content-length - * header. See Mozilla Bugzilla #246651. + * header. See Mozilla Bugzilla #246651. */ if (this.transport.overrideMimeType) requestHeaders.push('Connection', 'close'); @@ -630,7 +658,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { if (readyState != 1) this.respondToReadyState(this.transport.readyState); }, - + evalJSON: function() { try { var json = this.transport.getResponseHeader('X-JSON'), object; @@ -719,7 +747,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { this.onComplete = this.options.onComplete; this.frequency = (this.options.frequency || 2); - this.decay = 1; + this.decay = (this.options.decay || 1); this.updater = {}; this.container = container; @@ -736,17 +764,17 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { stop: function() { this.updater.onComplete = undefined; clearTimeout(this.timer); - (this.onComplete || Ajax.emptyFunction).apply(this, arguments); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); }, updateComplete: function(request) { if (this.options.decay) { - this.decay = (request.responseText == this.lastText ? + this.decay = (request.responseText == this.lastText ? this.decay * this.options.decay : 1); this.lastText = request.responseText; } - this.timer = setTimeout(this.onTimerEvent.bind(this), + this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000); }, @@ -754,7 +782,6 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { this.updater = new Ajax.Updater(this.container, this.url, this.options); } }); - document.getElementsByClassName = function(className, parentElement) { var children = (document.body || $(parentElement)).getElementsByTagName('*'); return $A(children).inject([], function(elements, child) { @@ -774,7 +801,7 @@ Object.extend(Element, { visible: function(element) { return $(element).style.display != 'none'; }, - + toggle: function() { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); @@ -788,7 +815,7 @@ Object.extend(Element, { element.style.display = 'none'; } }, - + show: function() { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); @@ -800,12 +827,12 @@ Object.extend(Element, { element = $(element); element.parentNode.removeChild(element); }, - + getHeight: function(element) { element = $(element); - return element.offsetHeight; + return element.offsetHeight; }, - + classNames: function(element) { return new Element.ClassNames(element); }, @@ -824,26 +851,107 @@ Object.extend(Element, { if (!(element = $(element))) return; return Element.classNames(element).remove(className); }, - + // removes whitespace-only text node children cleanWhitespace: function(element) { element = $(element); for (var i = 0; i < element.childNodes.length; i++) { var node = element.childNodes[i]; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node); } }, - + empty: function(element) { return $(element).innerHTML.match(/^\s*$/); }, - + scrollTo: function(element) { element = $(element); var x = element.x ? element.x : element.offsetLeft, y = element.y ? element.y : element.offsetTop; window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; } }); @@ -860,14 +968,13 @@ Abstract.Insertion.prototype = { initialize: function(element, content) { this.element = $(element); this.content = content; - + if (this.adjacency && this.element.insertAdjacentHTML) { try { this.element.insertAdjacentHTML(this.adjacency, this.content); } catch (e) { if (this.element.tagName.toLowerCase() == 'tbody') { - this.fragment = this.contentFromAnonymousTable(); - this.insertContent(); + this.insertContent(this.contentFromAnonymousTable()); } else { throw e; } @@ -875,15 +982,14 @@ Abstract.Insertion.prototype = { } else { this.range = this.element.ownerDocument.createRange(); if (this.initializeRange) this.initializeRange(); - this.fragment = this.range.createContextualFragment(this.content); - this.insertContent(); + this.insertContent([this.range.createContextualFragment(this.content)]); } }, - + contentFromAnonymousTable: function() { var div = document.createElement('div'); div.innerHTML = '' + this.content + '
'; - return div.childNodes[0].childNodes[0].childNodes[0]; + return $A(div.childNodes[0].childNodes[0].childNodes); } } @@ -894,9 +1000,11 @@ Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin') initializeRange: function() { this.range.setStartBefore(this.element); }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, this.element); + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); } }); @@ -906,9 +1014,11 @@ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { this.range.selectNodeContents(this.element); this.range.collapse(true); }, - - insertContent: function() { - this.element.insertBefore(this.fragment, this.element.firstChild); + + insertContent: function(fragments) { + fragments.reverse().each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); } }); @@ -918,9 +1028,11 @@ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), this.range.selectNodeContents(this.element); this.range.collapse(this.element); }, - - insertContent: function() { - this.element.appendChild(this.fragment); + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); } }); @@ -929,10 +1041,12 @@ Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { initializeRange: function() { this.range.setStartAfter(this.element); }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, - this.element.nextSibling); + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); } }); @@ -949,30 +1063,29 @@ Element.ClassNames.prototype = { return name.length > 0; })._each(iterator); }, - + set: function(className) { this.element.className = className; }, - + add: function(classNameToAdd) { if (this.include(classNameToAdd)) return; this.set(this.toArray().concat(classNameToAdd).join(' ')); }, - + remove: function(classNameToRemove) { if (!this.include(classNameToRemove)) return; this.set(this.select(function(className) { return className != classNameToRemove; })); }, - + toString: function() { return this.toArray().join(' '); } } Object.extend(Element.ClassNames.prototype, Enumerable); - var Field = { clear: function() { for (var i = 0; i < arguments.length; i++) @@ -982,17 +1095,17 @@ var Field = { focus: function(element) { $(element).focus(); }, - + present: function() { for (var i = 0; i < arguments.length; i++) if ($(arguments[i]).value == '') return false; return true; }, - + select: function(element) { $(element).select(); }, - + activate: function(element) { $(element).focus(); $(element).select(); @@ -1005,16 +1118,16 @@ var Form = { serialize: function(form) { var elements = Form.getElements($(form)); var queryComponents = new Array(); - + for (var i = 0; i < elements.length; i++) { var queryComponent = Form.Element.serialize(elements[i]); if (queryComponent) queryComponents.push(queryComponent); } - + return queryComponents.join('&'); }, - + getElements: function(form) { var form = $(form); var elements = new Array(); @@ -1026,19 +1139,19 @@ var Form = { } return elements; }, - + getInputs: function(form, typeName, name) { var form = $(form); var inputs = form.getElementsByTagName('input'); - + if (!typeName && !name) return inputs; - + var matchingInputs = new Array(); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; if ((typeName && input.type != typeName) || - (name && input.name != name)) + (name && input.name != name)) continue; matchingInputs.push(input); } @@ -1085,18 +1198,18 @@ Form.Element = { var element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); - + if (parameter) - return encodeURIComponent(parameter[0]) + '=' + - encodeURIComponent(parameter[1]); + return encodeURIComponent(parameter[0]) + '=' + + encodeURIComponent(parameter[1]); }, - + getValue: function(element) { var element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); - - if (parameter) + + if (parameter) return parameter[1]; } } @@ -1109,7 +1222,7 @@ Form.Element.Serializers = { case 'password': case 'text': return Form.Element.Serializers.textarea(element); - case 'checkbox': + case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element); } @@ -1124,12 +1237,12 @@ Form.Element.Serializers = { textarea: function(element) { return [element.name, element.value]; }, - + select: function(element) { - return Form.Element.Serializers[element.type == 'select-one' ? + return Form.Element.Serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element); }, - + selectOne: function(element) { var value = '', opt, index = element.selectedIndex; if (index >= 0) { @@ -1140,7 +1253,7 @@ Form.Element.Serializers = { } return [element.name, value]; }, - + selectMany: function(element) { var value = new Array(); for (var i = 0; i < element.length; i++) { @@ -1168,15 +1281,15 @@ Abstract.TimedObserver.prototype = { this.frequency = frequency; this.element = $(element); this.callback = callback; - + this.lastValue = this.getValue(); this.registerCallback(); }, - + registerCallback: function() { setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, - + onTimerEvent: function() { var value = this.getValue(); if (this.lastValue != value) { @@ -1207,14 +1320,14 @@ Abstract.EventObserver.prototype = { initialize: function(element, callback) { this.element = $(element); this.callback = callback; - + this.lastValue = this.getValue(); if (this.element.tagName.toLowerCase() == 'form') this.registerFormCallbacks(); else this.registerCallback(this.element); }, - + onElementEvent: function() { var value = this.getValue(); if (this.lastValue != value) { @@ -1222,22 +1335,22 @@ Abstract.EventObserver.prototype = { this.lastValue = value; } }, - + registerFormCallbacks: function() { var elements = Form.getElements(this.element); for (var i = 0; i < elements.length; i++) this.registerCallback(elements[i]); }, - + registerCallback: function(element) { if (element.type) { switch (element.type.toLowerCase()) { - case 'checkbox': + case 'checkbox': case 'radio': element.target = this; element.prev_onclick = element.onclick || Prototype.emptyFunction; element.onclick = function() { - this.prev_onclick(); + this.prev_onclick(); this.target.onElementEvent(); } break; @@ -1249,12 +1362,12 @@ Abstract.EventObserver.prototype = { element.target = this; element.prev_onchange = element.onchange || Prototype.emptyFunction; element.onchange = function() { - this.prev_onchange(); + this.prev_onchange(); this.target.onElementEvent(); } break; } - } + } } } @@ -1271,8 +1384,6 @@ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { return Form.serialize(this.element); } }); - - if (!window.Event) { var Event = new Object(); } @@ -1298,19 +1409,19 @@ Object.extend(Event, { }, pointerX: function(event) { - return event.pageX || (event.clientX + + return event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)); }, pointerY: function(event) { - return event.pageY || (event.clientY + + return event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)); }, stop: function(event) { - if (event.preventDefault) { - event.preventDefault(); - event.stopPropagation(); + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); } else { event.returnValue = false; event.cancelBubble = true; @@ -1328,7 +1439,7 @@ Object.extend(Event, { }, observers: false, - + _observeAndCache: function(element, name, observer, useCapture) { if (!this.observers) this.observers = []; if (element.addEventListener) { @@ -1339,7 +1450,7 @@ Object.extend(Event, { element.attachEvent('on' + name, observer); } }, - + unloadCache: function() { if (!Event.observers) return; for (var i = 0; i < Event.observers.length; i++) { @@ -1352,24 +1463,24 @@ Object.extend(Event, { observe: function(element, name, observer, useCapture) { var element = $(element); useCapture = useCapture || false; - + if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) name = 'keydown'; - + this._observeAndCache(element, name, observer, useCapture); }, stopObserving: function(element, name, observer, useCapture) { var element = $(element); useCapture = useCapture || false; - + if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent)) name = 'keydown'; - + if (element.removeEventListener) { element.removeEventListener(name, observer, useCapture); } else if (element.detachEvent) { @@ -1380,24 +1491,22 @@ Object.extend(Event, { /* prevent memory leaks in IE */ Event.observe(window, 'unload', Event.unloadCache, false); - var Position = { - // set to true if needed, warning: firefox performance problems // NOT neeeded for page scrolling, only if draggable contained in // scrollable elements - includeScrollOffsets: false, + includeScrollOffsets: false, // must be called before calling withinIncludingScrolloffset, every time the // page is scrolled prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop || 0; }, @@ -1405,7 +1514,7 @@ var Position = { var valueT = 0, valueL = 0; do { valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; + valueL += element.scrollLeft || 0; element = element.parentNode; } while (element); return [valueL, valueT]; @@ -1421,6 +1530,31 @@ var Position = { return [valueL, valueT]; }, + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + // caches x/y coordinate pair to use with overlap within: function(element, x, y) { if (this.includeScrollOffsets) @@ -1431,7 +1565,7 @@ var Position = { return (y >= this.offset[1] && y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && + x >= this.offset[0] && x < this.offset[0] + element.offsetWidth); }, @@ -1444,18 +1578,18 @@ var Position = { return (this.ycomp >= this.offset[1] && this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && + this.xcomp >= this.offset[0] && this.xcomp < this.offset[0] + element.offsetWidth); }, // within must be called directly before - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / element.offsetHeight; if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / element.offsetWidth; }, @@ -1468,5 +1602,123 @@ var Position = { target.style.left = offsets[0] + 'px'; target.style.width = source.offsetWidth + 'px'; target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; } } + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} \ No newline at end of file -- cgit v1.2.3