Author: jmorliaguet Date: Mon Dec 19 17:47:56 2005 New Revision: 2052 Modified: cpsskins/branches/jmo-perspectives/ui/framework/prototype.js Log:
- updated to prototype.js 1.4.0 Modified: cpsskins/branches/jmo-perspectives/ui/framework/prototype.js ============================================================================== --- cpsskins/branches/jmo-perspectives/ui/framework/prototype.js (original) +++ cpsskins/branches/jmo-perspectives/ui/framework/prototype.js Mon Dec 19 17:47:56 2005 @@ -1,8 +1,8 @@ -/* Prototype JavaScript framework, version 1.3.1 +/* Prototype JavaScript framework, version 1.4.0 * (c) 2005 Sam Stephenson <[EMAIL PROTECTED]> * * 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,13 +11,16 @@ /*--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.3.1', - emptyFunction: function() {} + Version: '1.4.0', + ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} } var Class = { create: function() { - return function() { + return function() { this.initialize.apply(this, arguments); } } @@ -32,29 +35,47 @@ return destination; } -Object.prototype.extend = function(object) { - return Object.extend.apply(this, [this, object]); +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } } -Function.prototype.bind = function(object) { - var __method = this; +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); return function() { - __method.apply(object, arguments); + return __method.apply(object, args.concat($A(arguments))); } } Function.prototype.bindAsEventListener = function(object) { var __method = this; return function(event) { - __method.call(object, event || window.event); + return __method.call(object, event || window.event); } } -Number.prototype.toColorPart = function() { - var digits = this.toString(16); - if (this < 16) return '0' + digits; - return digits; -} +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); var Try = { these: function() { @@ -90,10 +111,10 @@ onTimerEvent: function() { if (!this.currentlyExecuting) { - try { + try { this.currentlyExecuting = true; - this.callback(); - } finally { + this.callback(); + } finally { this.currentlyExecuting = false; } } @@ -110,7 +131,7 @@ if (typeof element == 'string') element = document.getElementById(element); - if (arguments.length == 1) + if (arguments.length == 1) return element; elements.push(element); @@ -118,38 +139,25 @@ return elements; } +Object.extend(String.prototype, { + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, -if (!Array.prototype.push) { - Array.prototype.push = function() { - var startLength = this.length; - for (var i = 0; i < arguments.length; i++) - this[startLength + i] = arguments[i]; - return this.length; - } -} + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, -if (!Function.prototype.apply) { - // Based on code from http://www.youngpup.net/ - Function.prototype.apply = function(object, parameters) { - var parameterStrings = new Array(); - if (!object) object = window; - if (!parameters) parameters = new Array(); - - for (var i = 0; i < parameters.length; i++) - parameterStrings[i] = 'parameters[' + i + ']'; - - object.__apply__ = this; - var result = eval('object.__apply__(' + - parameterStrings.join(', ') + ')'); - object.__apply__ = null; - - return result; - } -} + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, -String.prototype.extend({ - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); + evalScripts: function() { + return this.extractScripts().map(eval); }, escapeHTML: function() { @@ -162,9 +170,381 @@ unescapeHTML: function() { var div = document.createElement('div'); div.innerHTML = this.stripTags(); - return div.childNodes[0].nodeValue; + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + 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("'", '\\\'') + "'"; + } +}); + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + 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) { + value = (iterator || Prototype.K)(value, index); + if (value >= (result || value)) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value <= (result || value)) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + 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') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + iterator(value = collections.pluck(index)); + return value; + }); + }, + + inspect: function() { + return '#<Enumerable:' + this.toArray().inspect() + '>'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + 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 -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + shift: function() { + var result = this[0]; + for (var i = 0; i < this.length - 1; i++) + this[i] = this[i + 1]; + this.length--; + 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 '#<Hash:{' + this.map(function(pair) { + return pair.map(Object.inspect).join(': '); + }).join(', ') + '}>'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} var Ajax = { getTransport: function() { @@ -173,9 +553,50 @@ function() {return new ActiveXObject('Microsoft.XMLHTTP')}, function() {return new XMLHttpRequest()} ) || false; - } + }, + + activeRequestCount: 0 } +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + Ajax.Base = function() {}; Ajax.Base.prototype = { setOptions: function(options) { @@ -183,12 +604,13 @@ method: 'post', asynchronous: true, parameters: '' - }.extend(options || {}); + } + Object.extend(this.options, options || {}); }, responseIsSuccess: function() { return this.transport.status == undefined - || this.transport.status == 0 + || this.transport.status == 0 || (this.transport.status >= 200 && this.transport.status < 300); }, @@ -198,10 +620,10 @@ } Ajax.Request = Class.create(); -Ajax.Request.Events = +Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; -Ajax.Request.prototype = (new Ajax.Base()).extend({ +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); @@ -213,10 +635,13 @@ if (parameters.length > 0) parameters += '&_='; try { - if (this.options.method == 'get') - url += '?' + parameters; + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; - this.transport.open(this.options.method, url, + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, this.options.asynchronous); if (this.options.asynchronous) { @@ -230,21 +655,22 @@ this.transport.send(this.options.method == 'post' ? body : null); } catch (e) { + this.dispatchException(e); } }, 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'); @@ -263,26 +689,64 @@ this.respondToReadyState(this.transport.readyState); }, + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval(this.header('X-JSON')); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + respondToReadyState: function(readyState) { var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); - if (event == 'Complete') - (this.options['on' + this.transport.status] - || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(this.transport); + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } - (this.options['on' + event] || Prototype.emptyFunction)(this.transport); + if ((this.header('Content-type') || '').match(/^text\/javascript/i)) + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ if (event == 'Complete') this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); } }); Ajax.Updater = Class.create(); -Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)'; -Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({ +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { initialize: function(container, url, options) { this.containers = { success: container.success ? $(container.success) : $(container), @@ -294,9 +758,9 @@ this.setOptions(options); var onComplete = this.options.onComplete || Prototype.emptyFunction; - this.options.onComplete = (function() { + this.options.onComplete = (function(transport, object) { this.updateContent(); - onComplete(this.transport); + onComplete(transport, object); }).bind(this); this.request(url); @@ -305,43 +769,34 @@ updateContent: function() { var receiver = this.responseIsSuccess() ? this.containers.success : this.containers.failure; + var response = this.transport.responseText; - var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); - var response = this.transport.responseText.replace(match, ''); - var scripts = this.transport.responseText.match(match); + if (!this.options.evalScripts) + response = response.stripScripts(); if (receiver) { if (this.options.insertion) { new this.options.insertion(receiver, response); } else { - receiver.innerHTML = response; + Element.update(receiver, response); } } if (this.responseIsSuccess()) { if (this.onComplete) - setTimeout((function() {this.onComplete( - this.transport)}).bind(this), 10); - } - - if (this.options.evalScripts && scripts) { - match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); - setTimeout((function() { - for (var i = 0; i < scripts.length; i++) - eval(scripts[i].match(match)[1]); - }).bind(this), 10); + setTimeout(this.onComplete.bind(this), 10); } } }); Ajax.PeriodicalUpdater = Class.create(); -Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({ +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { initialize: function(container, url, options) { this.setOptions(options); 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; @@ -358,17 +813,17 @@ 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); }, @@ -376,23 +831,13 @@ this.updater = new Ajax.Updater(this.container, this.url, this.options); } }); - -document.getElementsByClassName = function(className) { - var children = document.getElementsByTagName('*') || document.all; - var elements = new Array(); - - for (var i = 0; i < children.length; i++) { - var child = children[i]; - var classNames = child.className.split(' '); - for (var j = 0; j < classNames.length; j++) { - if (classNames[j] == className) { - elements.push(child); - break; - } - } - } - - return elements; +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(child); + return elements; + }); } /*--------------------------------------------------------------------------*/ @@ -402,11 +847,14 @@ } 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]); - element.style.display = - (element.style.display == 'none' ? '' : 'none'); + Element[Element.visible(element) ? 'hide' : 'show'](element); } }, @@ -428,54 +876,142 @@ element = $(element); element.parentNode.removeChild(element); }, - + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + getHeight: function(element) { element = $(element); - return element.offsetHeight; + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); }, hasClassName: function(element, className) { - element = $(element); - if (!element) - return; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] == className) - return true; - } - return false; + if (!(element = $(element))) return; + return Element.classNames(element).include(className); }, addClassName: function(element, className) { - element = $(element); - Element.removeClassName(element, className); - element.className += ' ' + className; + if (!(element = $(element))) return; + return Element.classNames(element).add(className); }, removeClassName: function(element, className) { - element = $(element); - if (!element) - return; - var newClassName = ''; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] != className) { - if (i > 0) - newClassName += ' '; - newClassName += a[i]; - } - } - element.className = newClassName; + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); }, - + // removes whitespace-only text node children cleanWhitespace: function(element) { - var element = $(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; + }, + + setStyle: function(element, style) { + element = $(element); + for (name in style) + element.style[name.camelize()] = style[name]; + }, + + 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; } }); @@ -491,68 +1027,127 @@ Abstract.Insertion.prototype = { initialize: function(element, content) { this.element = $(element); - this.content = content; - + this.content = content.stripScripts(); + if (this.adjacency && this.element.insertAdjacentHTML) { - this.element.insertAdjacentHTML(this.adjacency, this.content); + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + if (this.element.tagName.toLowerCase() == 'tbody') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } } 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)]); } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; + return $A(div.childNodes[0].childNodes[0].childNodes); } } var Insertion = new Object(); Insertion.Before = Class.create(); -Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({ +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)); } }); Insertion.Top = Class.create(); -Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({ +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { initializeRange: function() { this.range.selectNodeContents(this.element); this.range.collapse(true); }, - - insertContent: function() { - this.element.insertBefore(this.fragment, this.element.firstChild); + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); } }); Insertion.Bottom = Class.create(); -Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({ +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { initializeRange: function() { 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)); } }); Insertion.After = Class.create(); -Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({ +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)); } }); +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + 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; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); var Field = { clear: function() { for (var i = 0; i < arguments.length; i++) @@ -562,20 +1157,22 @@ 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(); + element = $(element); + element.focus(); + if (element.select) + element.select(); } } @@ -585,18 +1182,18 @@ 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); + form = $(form); var elements = new Array(); for (tagName in Form.Element.Serializers) { @@ -606,19 +1203,19 @@ } return elements; }, - + getInputs: function(form, typeName, name) { - var form = $(form); + 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); } @@ -643,16 +1240,15 @@ } }, + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + focusFirstElement: function(form) { - var form = $(form); - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - if (element.type != 'hidden' && !element.disabled) { - Field.activate(element); - break; - } - } + Field.activate(Form.findFirstElement(form)); }, reset: function(form) { @@ -662,21 +1258,29 @@ Form.Element = { serialize: function(element) { - var element = $(element); + element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); - - if (parameter) - return encodeURIComponent(parameter[0]) + '=' + - encodeURIComponent(parameter[1]); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } }, - + getValue: function(element) { - var element = $(element); + element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); - - if (parameter) + + if (parameter) return parameter[1]; } } @@ -689,7 +1293,7 @@ case 'password': case 'text': return Form.Element.Serializers.textarea(element); - case 'checkbox': + case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element); } @@ -706,17 +1310,30 @@ }, select: function(element) { - var value = ''; - if (element.type == 'select-one') { - var index = element.selectedIndex; - if (index >= 0) - value = element.options[index].value || element.options[index].text; - } else { - value = new Array(); - for (var i = 0; i < element.length; i++) { - var opt = element.options[i]; - if (opt.selected) - value.push(opt.value || opt.text); + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value; + if (!value && !('value' in opt)) + value = opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) { + var optValue = opt.value; + if (!optValue && !('value' in opt)) + optValue = opt.text; + value.push(optValue); } } return [element.name, value]; @@ -735,15 +1352,15 @@ 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) { @@ -754,14 +1371,14 @@ } Form.Element.Observer = Class.create(); -Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({ +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { getValue: function() { return Form.Element.getValue(this.element); } }); Form.Observer = Class.create(); -Form.Observer.prototype = (new Abstract.TimedObserver()).extend({ +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { getValue: function() { return Form.serialize(this.element); } @@ -774,14 +1391,14 @@ 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) { @@ -789,57 +1406,45 @@ 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.target.onElementEvent(); - } + Event.observe(element, 'click', this.onElementEvent.bind(this)); break; case 'password': case 'text': case 'textarea': case 'select-one': case 'select-multiple': - element.target = this; - element.prev_onchange = element.onchange || Prototype.emptyFunction; - element.onchange = function() { - this.prev_onchange(); - this.target.onElementEvent(); - } + Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } - } + } } } Form.Element.EventObserver = Class.create(); -Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({ +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { getValue: function() { return Form.Element.getValue(this.element); } }); Form.EventObserver = Class.create(); -Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({ +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { getValue: function() { return Form.serialize(this.element); } }); - - if (!window.Event) { var Event = new Object(); } @@ -865,21 +1470,22 @@ }, 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; } }, @@ -894,7 +1500,7 @@ }, observers: false, - + _observeAndCache: function(element, name, observer, useCapture) { if (!this.observers) this.observers = []; if (element.addEventListener) { @@ -905,7 +1511,7 @@ element.attachEvent('on' + name, observer); } }, - + unloadCache: function() { if (!Event.observers) return; for (var i = 0; i < Event.observers.length; i++) { @@ -918,24 +1524,24 @@ observe: function(element, name, observer, useCapture) { var element = $(element); useCapture = useCapture || false; - + if (name == 'keypress' && - ((navigator.appVersion.indexOf('AppleWebKit') > 0) + (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.indexOf('AppleWebKit') > 0) + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent)) name = 'keydown'; - + if (element.removeEventListener) { element.removeEventListener(name, observer, useCapture); } else if (element.detachEvent) { @@ -946,24 +1552,22 @@ /* 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; }, @@ -971,7 +1575,7 @@ 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]; @@ -987,6 +1591,31 @@ 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) @@ -997,7 +1626,7 @@ 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); }, @@ -1010,18 +1639,18 @@ 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; }, @@ -1034,5 +1663,123 @@ 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 -- http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins