diff options
-rw-r--r-- | actionpack/CHANGELOG | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/javascripts/prototype.js | 479 | ||||
-rw-r--r-- | railties/CHANGELOG | 2 | ||||
-rw-r--r-- | railties/html/javascripts/prototype.js | 479 |
4 files changed, 710 insertions, 252 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5294d0bb19..7be4b0332d 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Update to Prototype 1.5.0_rc1 [sam] + * Added access to nested attributes in RJS #4548 [richcollins@gmail.com]. Examples: page['foo']['style'] # => $('foo').style; diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js index 5ba3a30218..81428620f6 100644 --- a/actionpack/lib/action_view/helpers/javascripts/prototype.js +++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js @@ -1,4 +1,4 @@ -/* Prototype JavaScript framework, version 1.5.0_rc0 +/* Prototype JavaScript framework, version 1.5.0_rc1 * (c) 2005 Sam Stephenson <sam@conio.net> * * Prototype is freely distributable under the terms of an MIT-style license. @@ -7,7 +7,7 @@ /*--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.5.0_rc0', + Version: '1.5.0_rc1', ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', emptyFunction: function() {}, @@ -31,16 +31,36 @@ Object.extend = function(destination, source) { return destination; } -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; +Object.extend(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; + } + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({}, object); } -} +}); Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); @@ -50,9 +70,9 @@ Function.prototype.bind = function() { } Function.prototype.bindAsEventListener = function(object) { - var __method = this; + var __method = this, args = $A(arguments), object = args.shift(); return function(event) { - return __method.call(object, event || window.event); + return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); } } @@ -102,14 +122,20 @@ PeriodicalExecuter.prototype = { }, registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; - this.callback(); + this.callback(this); } finally { this.currentlyExecuting = false; } @@ -195,8 +221,9 @@ Object.extend(String.prototype, { toQueryParams: function() { var pairs = this.match(/^\??(.*)$/)[1].split('&'); return pairs.inject({}, function(params, pairString) { - var pair = pairString.split('='); - params[pair[0]] = pair[1]; + var pair = pairString.split('='); + var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; + params[decodeURIComponent(pair[0])] = value; return params; }); }, @@ -221,8 +248,12 @@ Object.extend(String.prototype, { return camelizedString; }, - inspect: function() { - return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'"; + inspect: function(useDoubleQuotes) { + var escapedString = this.replace(/\\/g, '\\\\'); + if (useDoubleQuotes) + return '"' + escapedString.replace(/"/g, '\\"') + '"'; + else + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; } }); @@ -280,7 +311,7 @@ var Enumerable = { }, any: function(iterator) { - var result = true; + var result = false; this.each(function(value, index) { if (result = !!(iterator || Prototype.K)(value, index)) throw $break; @@ -499,6 +530,16 @@ Object.extend(Array.prototype, { return (inline !== false ? this : this.toArray())._reverse(); }, + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function() { + return this.inject([], function(array, value) { + return array.include(value) ? array : array.concat([value]); + }); + }, + inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; } @@ -561,10 +602,10 @@ Object.extend(ObjectRange.prototype, { _each: function(iterator) { var value = this.start; - do { + while (this.include(value)) { iterator(value); value = value.succ(); - } while (this.include(value)); + } }, include: function(value) { @@ -671,8 +712,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { /* Simulate other verbs over post */ if (this.options.method != 'get' && this.options.method != 'post') { - parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method - this.options.method = 'post' + parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method; + this.options.method = 'post'; } try { @@ -685,16 +726,19 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { this.transport.open(this.options.method, this.url, this.options.asynchronous); - if (this.options.asynchronous) { - this.transport.onreadystatechange = this.onStateChange.bind(this); - setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); - } + if (this.options.asynchronous) + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); + this.transport.onreadystatechange = this.onStateChange.bind(this); this.setRequestHeaders(); var body = this.options.postBody ? this.options.postBody : parameters; this.transport.send(this.options.method == 'post' ? body : null); + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + } catch (e) { this.dispatchException(e); } @@ -852,7 +896,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { }, stop: function() { - this.updater.onComplete = undefined; + this.updater.options.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); }, @@ -880,7 +924,7 @@ function $() { element = document.getElementById(element); results.push(Element.extend(element)); } - return results.length < 2 ? results[0] : results; + return results.reduce(); } document.getElementsByClassName = function(className, parentElement) { @@ -902,8 +946,14 @@ Element.extend = function(element) { if (_nativeExtensions) return element; if (!element._extended && element.tagName && element != window) { - var methods = Element.Methods, cache = Element.extend.cache; - for (property in methods) { + var methods = Object.clone(Element.Methods), cache = Element.extend.cache; + + if (element.tagName == 'FORM') + Object.extend(methods, Form.Methods); + if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) + Object.extend(methods, Form.Element.Methods); + + for (var property in methods) { var value = methods[property]; if (typeof value == 'function') element[property] = cache.findOrStore(value); @@ -927,35 +977,32 @@ Element.Methods = { return $(element).style.display != 'none'; }, - toggle: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - Element[Element.visible(element) ? 'hide' : 'show'](element); - } + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; }, - hide: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = 'none'; - } + hide: function(element) { + $(element).style.display = 'none'; + return element; }, - show: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = ''; - } + show: function(element) { + $(element).style.display = ''; + return element; }, remove: function(element) { element = $(element); element.parentNode.removeChild(element); + return element; }, update: function(element, html) { $(element).innerHTML = html.stripScripts(); setTimeout(function() {html.evalScripts()}, 10); + return element; }, replace: function(element, html) { @@ -969,6 +1016,82 @@ Element.Methods = { range.createContextualFragment(html.stripScripts()), element); } setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + element = $(element); + return $A(element.getElementsByTagName('*')); + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + element = $(element); + if (typeof selector == 'string') + selector = new Selector(selector); + return selector.match(element); + }, + + up: function(element, expression, index) { + return Selector.findElement($(element).ancestors(), expression, index); + }, + + down: function(element, expression, index) { + return Selector.findElement($(element).descendants(), expression, index); + }, + + previous: function(element, expression, index) { + return Selector.findElement($(element).previousSiblings(), expression, index); + }, + + next: function(element, expression, index) { + return Selector.findElement($(element).nextSiblings(), expression, index); + }, + + getElementsBySelector: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + getElementsByClassName: function(element, className) { + element = $(element); + return document.getElementsByClassName(className, element); }, getHeight: function(element) { @@ -987,12 +1110,24 @@ Element.Methods = { addClassName: function(element, className) { if (!(element = $(element))) return; - return Element.classNames(element).add(className); + Element.classNames(element).add(className); + return element; }, removeClassName: function(element, className) { if (!(element = $(element))) return; - return Element.classNames(element).remove(className); + Element.classNames(element).remove(className); + return element; + }, + + observe: function() { + Event.observe.apply(Event, arguments); + return $A(arguments).first(); + }, + + stopObserving: function() { + Event.stopObserving.apply(Event, arguments); + return $A(arguments).first(); }, // removes whitespace-only text node children @@ -1003,6 +1138,7 @@ Element.Methods = { if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node); } + return element; }, empty: function(element) { @@ -1021,6 +1157,7 @@ Element.Methods = { var x = element.x ? element.x : element.offsetLeft, y = element.y ? element.y : element.offsetTop; window.scrollTo(x, y); + return element; }, getStyle: function(element, style) { @@ -1045,6 +1182,7 @@ Element.Methods = { element = $(element); for (var name in style) element.style[name.camelize()] = style[name]; + return element; }, getDimensions: function(element) { @@ -1081,6 +1219,7 @@ Element.Methods = { element.style.left = 0; } } + return element; }, undoPositioned: function(element) { @@ -1093,21 +1232,60 @@ Element.Methods = { element.style.bottom = element.style.right = ''; } + return element; }, makeClipping: function(element) { element = $(element); if (element._overflow) return; - element._overflow = element.style.overflow; + element._overflow = element.style.overflow || 'auto'; if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') element.style.overflow = 'hidden'; + return element; }, undoClipping: function(element) { element = $(element); - if (element._overflow) return; - element.style.overflow = element._overflow; - element._overflow = undefined; + if (!element._overflow) return; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + delete element._overflow; + return element; + } +} + +// IE is missing .innerHTML support for TABLE-related elements +if(document.all){ + Element.Methods.update = function(element, html) { + element = $(element); + var tagName = element.tagName.toUpperCase(); + if (['THEAD','TBODY','TR','TD'].indexOf(tagName) > -1) { + var div = document.createElement('div'); + switch (tagName) { + case 'THEAD': + case 'TBODY': + div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>'; + depth = 2; + break; + case 'TR': + div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>'; + depth = 3; + break; + case 'TD': + div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>'; + depth = 4; + } + $A(element.childNodes).each(function(node){ + element.removeChild(node) + }); + depth.times(function(){ div = div.firstChild }); + + $A(div.childNodes).each( + function(node){ element.appendChild(node) }); + } else { + element.innerHTML = html.stripScripts(); + } + setTimeout(function() {html.evalScripts()}, 10); + return element; } } @@ -1115,27 +1293,36 @@ Object.extend(Element, Element.Methods); var _nativeExtensions = false; -if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { - var HTMLElement = {} - HTMLElement.prototype = document.createElement('div').__proto__; +if (!window.HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + /* Emulate HTMLElement, HTMLFormElement, HTMLInputElement, HTMLTextAreaElement, + and HTMLSelectElement in Safari */ + ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) { + var klass = window['HTML' + tag + 'Element'] = {}; + klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__; + }); } Element.addMethods = function(methods) { Object.extend(Element.Methods, methods || {}); - if(typeof HTMLElement != 'undefined') { - var methods = Element.Methods, cache = Element.extend.cache; - for (property in methods) { + function copy(methods, destination) { + var cache = Element.extend.cache; + for (var property in methods) { var value = methods[property]; - if (typeof value == 'function') - HTMLElement.prototype[property] = cache.findOrStore(value); + destination[property] = cache.findOrStore(value); } + } + + if (typeof HTMLElement != 'undefined') { + copy(Element.Methods, HTMLElement.prototype); + copy(Form.Methods, HTMLFormElement.prototype); + [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) { + copy(Form.Element.Methods, klass.prototype); + }); _nativeExtensions = true; } } -Element.addMethods(); - var Toggle = new Object(); Toggle.display = Element.toggle; @@ -1372,45 +1559,40 @@ Selector.prototype = { } } -function $$() { - return $A(arguments).map(function(expression) { - return expression.strip().split(/\s+/).inject([null], function(results, expr) { - var selector = new Selector(expr); - return results.map(selector.findElements.bind(selector)).flatten(); - }); - }).flatten(); -} -var Field = { - clear: function() { - for (var i = 0; i < arguments.length; i++) - $(arguments[i]).value = ''; - }, - - focus: function(element) { - $(element).focus(); - }, - - present: function() { - for (var i = 0; i < arguments.length; i++) - if ($(arguments[i]).value == '') return false; - return true; +Object.extend(Selector, { + matchElements: function(elements, expression) { + var selector = new Selector(expression); + return elements.select(selector.match.bind(selector)); }, - select: function(element) { - $(element).select(); + findElement: function(elements, expression, index) { + if (typeof expression == 'number') index = expression, expression = false; + return Selector.matchElements(elements, expression || '*')[index || 0]; }, - activate: function(element) { - element = $(element); - element.focus(); - if (element.select) - element.select(); + findChildElements: function(element, expressions) { + return expressions.map(function(expression) { + return expression.strip().split(/\s+/).inject([null], function(results, expr) { + var selector = new Selector(expr); + return results.inject([], function(elements, result) { + return elements.concat(selector.findElements(result || element)); + }); + }); + }).flatten(); } -} - -/*--------------------------------------------------------------------------*/ +}); +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} var Form = { + reset: function(form) { + $(form).reset(); + return form; + } +}; + +Form.Methods = { serialize: function(form) { var elements = Form.getElements($(form)); var queryComponents = new Array(); @@ -1456,20 +1638,24 @@ var Form = { }, disable: function(form) { + form = $(form); var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.blur(); element.disabled = 'true'; } + return form; }, enable: function(form) { + form = $(form); var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.disabled = ''; } + return form; }, findFirstElement: function(form) { @@ -1480,15 +1666,29 @@ var Form = { }, focusFirstElement: function(form) { + form = $(form); Field.activate(Form.findFirstElement(form)); + return form; + } +} + +Object.extend(Form, Form.Methods); + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; }, - reset: function(form) { - $(form).reset(); + select: function(element) { + $(element).select(); + return element; } } -Form.Element = { +Form.Element.Methods = { serialize: function(element) { element = $(element); var method = element.tagName.toLowerCase(); @@ -1514,20 +1714,52 @@ Form.Element = { if (parameter) return parameter[1]; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = ''; + return element; + }, + + enable: function(element) { + element = $(element); + element.blur(); + element.disabled = 'true'; + return element; } } +Object.extend(Form.Element, Form.Element.Methods); +var Field = Form.Element; + +/*--------------------------------------------------------------------------*/ + Form.Element.Serializers = { input: function(element) { switch (element.type.toLowerCase()) { - case 'submit': - case 'hidden': - case 'password': - case 'text': - return Form.Element.Serializers.textarea(element); case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element); + default: + return Form.Element.Serializers.textarea(element); } return false; }, @@ -1646,11 +1878,7 @@ Abstract.EventObserver.prototype = { case 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': + default: Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } @@ -1685,6 +1913,10 @@ Object.extend(Event, { KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, element: function(event) { return event.target || event.srcElement; @@ -1748,7 +1980,7 @@ Object.extend(Event, { }, observe: function(element, name, observer, useCapture) { - var element = $(element); + element = $(element); useCapture = useCapture || false; if (name == 'keypress' && @@ -1756,11 +1988,11 @@ Object.extend(Event, { || element.attachEvent)) name = 'keydown'; - this._observeAndCache(element, name, observer, useCapture); + Event._observeAndCache(element, name, observer, useCapture); }, stopObserving: function(element, name, observer, useCapture) { - var element = $(element); + element = $(element); useCapture = useCapture || false; if (name == 'keypress' && @@ -1771,7 +2003,9 @@ Object.extend(Event, { if (element.removeEventListener) { element.removeEventListener(name, observer, useCapture); } else if (element.detachEvent) { - element.detachEvent('on' + name, observer); + try { + element.detachEvent('on' + name, observer); + } catch (e) {} } } }); @@ -1881,17 +2115,6 @@ var Position = { element.offsetWidth; }, - clone: function(source, target) { - source = $(source); - target = $(target); - target.style.position = 'absolute'; - var offsets = this.cumulativeOffset(source); - target.style.top = offsets[1] + 'px'; - 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; @@ -1908,8 +2131,10 @@ var Position = { element = forElement; do { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; + if (!window.opera || element.tagName=='BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } } while (element = element.parentNode); return [valueL, valueT]; @@ -2009,4 +2234,6 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { return [valueL, valueT]; } -}
\ No newline at end of file +} + +Element.addMethods();
\ No newline at end of file diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 6a42f8fa17..9fb053ab94 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Update to Prototype 1.5.0_rc1 [sam] + * Formally Deprecate the old rake tasks. [Koz] * Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark] diff --git a/railties/html/javascripts/prototype.js b/railties/html/javascripts/prototype.js index 5ba3a30218..81428620f6 100644 --- a/railties/html/javascripts/prototype.js +++ b/railties/html/javascripts/prototype.js @@ -1,4 +1,4 @@ -/* Prototype JavaScript framework, version 1.5.0_rc0 +/* Prototype JavaScript framework, version 1.5.0_rc1 * (c) 2005 Sam Stephenson <sam@conio.net> * * Prototype is freely distributable under the terms of an MIT-style license. @@ -7,7 +7,7 @@ /*--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.5.0_rc0', + Version: '1.5.0_rc1', ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', emptyFunction: function() {}, @@ -31,16 +31,36 @@ Object.extend = function(destination, source) { return destination; } -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; +Object.extend(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; + } + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({}, object); } -} +}); Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); @@ -50,9 +70,9 @@ Function.prototype.bind = function() { } Function.prototype.bindAsEventListener = function(object) { - var __method = this; + var __method = this, args = $A(arguments), object = args.shift(); return function(event) { - return __method.call(object, event || window.event); + return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); } } @@ -102,14 +122,20 @@ PeriodicalExecuter.prototype = { }, registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; - this.callback(); + this.callback(this); } finally { this.currentlyExecuting = false; } @@ -195,8 +221,9 @@ Object.extend(String.prototype, { toQueryParams: function() { var pairs = this.match(/^\??(.*)$/)[1].split('&'); return pairs.inject({}, function(params, pairString) { - var pair = pairString.split('='); - params[pair[0]] = pair[1]; + var pair = pairString.split('='); + var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; + params[decodeURIComponent(pair[0])] = value; return params; }); }, @@ -221,8 +248,12 @@ Object.extend(String.prototype, { return camelizedString; }, - inspect: function() { - return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'"; + inspect: function(useDoubleQuotes) { + var escapedString = this.replace(/\\/g, '\\\\'); + if (useDoubleQuotes) + return '"' + escapedString.replace(/"/g, '\\"') + '"'; + else + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; } }); @@ -280,7 +311,7 @@ var Enumerable = { }, any: function(iterator) { - var result = true; + var result = false; this.each(function(value, index) { if (result = !!(iterator || Prototype.K)(value, index)) throw $break; @@ -499,6 +530,16 @@ Object.extend(Array.prototype, { return (inline !== false ? this : this.toArray())._reverse(); }, + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function() { + return this.inject([], function(array, value) { + return array.include(value) ? array : array.concat([value]); + }); + }, + inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; } @@ -561,10 +602,10 @@ Object.extend(ObjectRange.prototype, { _each: function(iterator) { var value = this.start; - do { + while (this.include(value)) { iterator(value); value = value.succ(); - } while (this.include(value)); + } }, include: function(value) { @@ -671,8 +712,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { /* Simulate other verbs over post */ if (this.options.method != 'get' && this.options.method != 'post') { - parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method - this.options.method = 'post' + parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method; + this.options.method = 'post'; } try { @@ -685,16 +726,19 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { this.transport.open(this.options.method, this.url, this.options.asynchronous); - if (this.options.asynchronous) { - this.transport.onreadystatechange = this.onStateChange.bind(this); - setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); - } + if (this.options.asynchronous) + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); + this.transport.onreadystatechange = this.onStateChange.bind(this); this.setRequestHeaders(); var body = this.options.postBody ? this.options.postBody : parameters; this.transport.send(this.options.method == 'post' ? body : null); + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + } catch (e) { this.dispatchException(e); } @@ -852,7 +896,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { }, stop: function() { - this.updater.onComplete = undefined; + this.updater.options.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); }, @@ -880,7 +924,7 @@ function $() { element = document.getElementById(element); results.push(Element.extend(element)); } - return results.length < 2 ? results[0] : results; + return results.reduce(); } document.getElementsByClassName = function(className, parentElement) { @@ -902,8 +946,14 @@ Element.extend = function(element) { if (_nativeExtensions) return element; if (!element._extended && element.tagName && element != window) { - var methods = Element.Methods, cache = Element.extend.cache; - for (property in methods) { + var methods = Object.clone(Element.Methods), cache = Element.extend.cache; + + if (element.tagName == 'FORM') + Object.extend(methods, Form.Methods); + if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) + Object.extend(methods, Form.Element.Methods); + + for (var property in methods) { var value = methods[property]; if (typeof value == 'function') element[property] = cache.findOrStore(value); @@ -927,35 +977,32 @@ Element.Methods = { return $(element).style.display != 'none'; }, - toggle: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - Element[Element.visible(element) ? 'hide' : 'show'](element); - } + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; }, - hide: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = 'none'; - } + hide: function(element) { + $(element).style.display = 'none'; + return element; }, - show: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = ''; - } + show: function(element) { + $(element).style.display = ''; + return element; }, remove: function(element) { element = $(element); element.parentNode.removeChild(element); + return element; }, update: function(element, html) { $(element).innerHTML = html.stripScripts(); setTimeout(function() {html.evalScripts()}, 10); + return element; }, replace: function(element, html) { @@ -969,6 +1016,82 @@ Element.Methods = { range.createContextualFragment(html.stripScripts()), element); } setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + element = $(element); + return $A(element.getElementsByTagName('*')); + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + element = $(element); + if (typeof selector == 'string') + selector = new Selector(selector); + return selector.match(element); + }, + + up: function(element, expression, index) { + return Selector.findElement($(element).ancestors(), expression, index); + }, + + down: function(element, expression, index) { + return Selector.findElement($(element).descendants(), expression, index); + }, + + previous: function(element, expression, index) { + return Selector.findElement($(element).previousSiblings(), expression, index); + }, + + next: function(element, expression, index) { + return Selector.findElement($(element).nextSiblings(), expression, index); + }, + + getElementsBySelector: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + getElementsByClassName: function(element, className) { + element = $(element); + return document.getElementsByClassName(className, element); }, getHeight: function(element) { @@ -987,12 +1110,24 @@ Element.Methods = { addClassName: function(element, className) { if (!(element = $(element))) return; - return Element.classNames(element).add(className); + Element.classNames(element).add(className); + return element; }, removeClassName: function(element, className) { if (!(element = $(element))) return; - return Element.classNames(element).remove(className); + Element.classNames(element).remove(className); + return element; + }, + + observe: function() { + Event.observe.apply(Event, arguments); + return $A(arguments).first(); + }, + + stopObserving: function() { + Event.stopObserving.apply(Event, arguments); + return $A(arguments).first(); }, // removes whitespace-only text node children @@ -1003,6 +1138,7 @@ Element.Methods = { if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node); } + return element; }, empty: function(element) { @@ -1021,6 +1157,7 @@ Element.Methods = { var x = element.x ? element.x : element.offsetLeft, y = element.y ? element.y : element.offsetTop; window.scrollTo(x, y); + return element; }, getStyle: function(element, style) { @@ -1045,6 +1182,7 @@ Element.Methods = { element = $(element); for (var name in style) element.style[name.camelize()] = style[name]; + return element; }, getDimensions: function(element) { @@ -1081,6 +1219,7 @@ Element.Methods = { element.style.left = 0; } } + return element; }, undoPositioned: function(element) { @@ -1093,21 +1232,60 @@ Element.Methods = { element.style.bottom = element.style.right = ''; } + return element; }, makeClipping: function(element) { element = $(element); if (element._overflow) return; - element._overflow = element.style.overflow; + element._overflow = element.style.overflow || 'auto'; if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') element.style.overflow = 'hidden'; + return element; }, undoClipping: function(element) { element = $(element); - if (element._overflow) return; - element.style.overflow = element._overflow; - element._overflow = undefined; + if (!element._overflow) return; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + delete element._overflow; + return element; + } +} + +// IE is missing .innerHTML support for TABLE-related elements +if(document.all){ + Element.Methods.update = function(element, html) { + element = $(element); + var tagName = element.tagName.toUpperCase(); + if (['THEAD','TBODY','TR','TD'].indexOf(tagName) > -1) { + var div = document.createElement('div'); + switch (tagName) { + case 'THEAD': + case 'TBODY': + div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>'; + depth = 2; + break; + case 'TR': + div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>'; + depth = 3; + break; + case 'TD': + div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>'; + depth = 4; + } + $A(element.childNodes).each(function(node){ + element.removeChild(node) + }); + depth.times(function(){ div = div.firstChild }); + + $A(div.childNodes).each( + function(node){ element.appendChild(node) }); + } else { + element.innerHTML = html.stripScripts(); + } + setTimeout(function() {html.evalScripts()}, 10); + return element; } } @@ -1115,27 +1293,36 @@ Object.extend(Element, Element.Methods); var _nativeExtensions = false; -if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { - var HTMLElement = {} - HTMLElement.prototype = document.createElement('div').__proto__; +if (!window.HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + /* Emulate HTMLElement, HTMLFormElement, HTMLInputElement, HTMLTextAreaElement, + and HTMLSelectElement in Safari */ + ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) { + var klass = window['HTML' + tag + 'Element'] = {}; + klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__; + }); } Element.addMethods = function(methods) { Object.extend(Element.Methods, methods || {}); - if(typeof HTMLElement != 'undefined') { - var methods = Element.Methods, cache = Element.extend.cache; - for (property in methods) { + function copy(methods, destination) { + var cache = Element.extend.cache; + for (var property in methods) { var value = methods[property]; - if (typeof value == 'function') - HTMLElement.prototype[property] = cache.findOrStore(value); + destination[property] = cache.findOrStore(value); } + } + + if (typeof HTMLElement != 'undefined') { + copy(Element.Methods, HTMLElement.prototype); + copy(Form.Methods, HTMLFormElement.prototype); + [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) { + copy(Form.Element.Methods, klass.prototype); + }); _nativeExtensions = true; } } -Element.addMethods(); - var Toggle = new Object(); Toggle.display = Element.toggle; @@ -1372,45 +1559,40 @@ Selector.prototype = { } } -function $$() { - return $A(arguments).map(function(expression) { - return expression.strip().split(/\s+/).inject([null], function(results, expr) { - var selector = new Selector(expr); - return results.map(selector.findElements.bind(selector)).flatten(); - }); - }).flatten(); -} -var Field = { - clear: function() { - for (var i = 0; i < arguments.length; i++) - $(arguments[i]).value = ''; - }, - - focus: function(element) { - $(element).focus(); - }, - - present: function() { - for (var i = 0; i < arguments.length; i++) - if ($(arguments[i]).value == '') return false; - return true; +Object.extend(Selector, { + matchElements: function(elements, expression) { + var selector = new Selector(expression); + return elements.select(selector.match.bind(selector)); }, - select: function(element) { - $(element).select(); + findElement: function(elements, expression, index) { + if (typeof expression == 'number') index = expression, expression = false; + return Selector.matchElements(elements, expression || '*')[index || 0]; }, - activate: function(element) { - element = $(element); - element.focus(); - if (element.select) - element.select(); + findChildElements: function(element, expressions) { + return expressions.map(function(expression) { + return expression.strip().split(/\s+/).inject([null], function(results, expr) { + var selector = new Selector(expr); + return results.inject([], function(elements, result) { + return elements.concat(selector.findElements(result || element)); + }); + }); + }).flatten(); } -} - -/*--------------------------------------------------------------------------*/ +}); +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} var Form = { + reset: function(form) { + $(form).reset(); + return form; + } +}; + +Form.Methods = { serialize: function(form) { var elements = Form.getElements($(form)); var queryComponents = new Array(); @@ -1456,20 +1638,24 @@ var Form = { }, disable: function(form) { + form = $(form); var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.blur(); element.disabled = 'true'; } + return form; }, enable: function(form) { + form = $(form); var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.disabled = ''; } + return form; }, findFirstElement: function(form) { @@ -1480,15 +1666,29 @@ var Form = { }, focusFirstElement: function(form) { + form = $(form); Field.activate(Form.findFirstElement(form)); + return form; + } +} + +Object.extend(Form, Form.Methods); + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; }, - reset: function(form) { - $(form).reset(); + select: function(element) { + $(element).select(); + return element; } } -Form.Element = { +Form.Element.Methods = { serialize: function(element) { element = $(element); var method = element.tagName.toLowerCase(); @@ -1514,20 +1714,52 @@ Form.Element = { if (parameter) return parameter[1]; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = ''; + return element; + }, + + enable: function(element) { + element = $(element); + element.blur(); + element.disabled = 'true'; + return element; } } +Object.extend(Form.Element, Form.Element.Methods); +var Field = Form.Element; + +/*--------------------------------------------------------------------------*/ + Form.Element.Serializers = { input: function(element) { switch (element.type.toLowerCase()) { - case 'submit': - case 'hidden': - case 'password': - case 'text': - return Form.Element.Serializers.textarea(element); case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element); + default: + return Form.Element.Serializers.textarea(element); } return false; }, @@ -1646,11 +1878,7 @@ Abstract.EventObserver.prototype = { case 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': + default: Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } @@ -1685,6 +1913,10 @@ Object.extend(Event, { KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, element: function(event) { return event.target || event.srcElement; @@ -1748,7 +1980,7 @@ Object.extend(Event, { }, observe: function(element, name, observer, useCapture) { - var element = $(element); + element = $(element); useCapture = useCapture || false; if (name == 'keypress' && @@ -1756,11 +1988,11 @@ Object.extend(Event, { || element.attachEvent)) name = 'keydown'; - this._observeAndCache(element, name, observer, useCapture); + Event._observeAndCache(element, name, observer, useCapture); }, stopObserving: function(element, name, observer, useCapture) { - var element = $(element); + element = $(element); useCapture = useCapture || false; if (name == 'keypress' && @@ -1771,7 +2003,9 @@ Object.extend(Event, { if (element.removeEventListener) { element.removeEventListener(name, observer, useCapture); } else if (element.detachEvent) { - element.detachEvent('on' + name, observer); + try { + element.detachEvent('on' + name, observer); + } catch (e) {} } } }); @@ -1881,17 +2115,6 @@ var Position = { element.offsetWidth; }, - clone: function(source, target) { - source = $(source); - target = $(target); - target.style.position = 'absolute'; - var offsets = this.cumulativeOffset(source); - target.style.top = offsets[1] + 'px'; - 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; @@ -1908,8 +2131,10 @@ var Position = { element = forElement; do { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; + if (!window.opera || element.tagName=='BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } } while (element = element.parentNode); return [valueL, valueT]; @@ -2009,4 +2234,6 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { return [valueL, valueT]; } -}
\ No newline at end of file +} + +Element.addMethods();
\ No newline at end of file |