User:Zocky/Prototype.js

// //from http://www.json.org/json.js

String.prototype.parseJSON = function { try { return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( this.replace(/"(\\.|[^"\\])*"/g, ''))) &&           eval('(' + this + ')');    } catch (e) {        return false;    } };

/* Prototype JavaScript framework, version 1.4.0 * (c) 2005 Sam Stephenson  * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://prototype.conio.net/ * /*--*/

var Prototype = { Version: '1.4.0', ScriptFragment: '(?:[<]script.*?>)((\n|\r|.)*?)(?:[<]\/script>)',

emptyFunction: function {}, K: function(x) {return x} }

var Class = { create: function { return function { this.initialize.apply(this, arguments); } } }

var Abstract = new Object;

Object.extend = function(destination, source) { for (property in source) { destination[property] = source[property]; } 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; } }

Function.prototype.bind = function { var __method = this, args = $A(arguments), object = args.shift; return function { return __method.apply(object, args.concat($A(arguments))); } }

Function.prototype.bindAsEventListener = function(object) { var __method = this; return function(event) { return __method.call(object, event || window.event); } }

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 { var returnValue;

for (var i = 0; i < arguments.length; i++) { var lambda = arguments[i]; try { returnValue = lambda; break; } catch (e) {} }

return returnValue; } }

/*--*/

var PeriodicalExecuter = Class.create; PeriodicalExecuter.prototype = { initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false;

this.registerCallback; },

registerCallback: function { setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); },

onTimerEvent: function { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.callback; } finally { this.currentlyExecuting = false; }   }  } }

/*--*/

function $ { var elements = new Array;

for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; if (typeof element == 'string') element = document.getElementById(element);

if (arguments.length == 1) return element;

elements.push(element); }

return elements; } Object.extend(String.prototype, { stripTags: function {    return this.replace(/<\/?[^>]+>/gi, '');  },

stripScripts: function { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); },

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];    }); },

evalScripts: function { return this.extractScripts.map(eval); },

escapeHTML: function { var div = document.createElement('div'); var text = document.createTextNode(this); div.appendChild(text); return div.innerHTML; },

unescapeHTML: function { var div = document.createElement('div'); 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) {     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 '#'; } }

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 '#'; } }

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 { return Try.these(     function {return new ActiveXObject('Msxml2.XMLHTTP')},      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) { this.options = { method:      'post', asynchronous: true, parameters:  '' }   Object.extend(this.options, options || {}); },

responseIsSuccess: function { return this.transport.status == undefined || this.transport.status == 0 || (this.transport.status >= 200 && this.transport.status < 300); },

responseIsFailure: function { return !this.responseIsSuccess; } }

Ajax.Request = Class.create; Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base, { initialize: function(url, options) {    this.transport = Ajax.getTransport;    this.setOptions(options);    this.request(url);  },

request: function(url) { var parameters = this.options.parameters || ''; if (parameters.length > 0) parameters += '&_=';

try { this.url = url; if (this.options.method == 'get' && parameters.length > 0) this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

Ajax.Responders.dispatch('onCreate', this, this.transport);

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); }

this.setRequestHeaders;

var body = this.options.postBody ? this.options.postBody : parameters; this.transport.send(this.options.method == 'post' ? body : null);

} catch (e) { this.dispatchException(e); } },

setRequestHeaders: function { var requestHeaders = ['X-Requested-With', 'XMLHttpRequest', 'X-Prototype-Version', Prototype.Version];

if (this.options.method == 'post') { 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. */     if (this.transport.overrideMimeType) requestHeaders.push('Connection', 'close'); }

if (this.options.requestHeaders) requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);

for (var i = 0; i < requestHeaders.length; i += 2) this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); },

onStateChange: function { var readyState = this.transport.readyState; if (readyState != 1) 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') { try { (this.options['on' + this.transport.status]        || this.options['on' + (this.responseIsSuccess ? 'Success' : 'Failure')]        || Prototype.emptyFunction)(transport, json); } catch (e) { this.dispatchException(e); }

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;

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { initialize: function(container, url, options) {    this.containers = {      success: container.success ? $(container.success) : $(container),      failure: container.failure ? $(container.failure) :        (container.success ? null : $(container))   }

this.transport = Ajax.getTransport; this.setOptions(options);

var onComplete = this.options.onComplete || Prototype.emptyFunction; this.options.onComplete = (function(transport, object) {     this.updateContent;      onComplete(transport, object);    }).bind(this);

this.request(url); },

updateContent: function { var receiver = this.responseIsSuccess ? this.containers.success : this.containers.failure; var response = this.transport.responseText;

if (!this.options.evalScripts) response = response.stripScripts;

if (receiver) { if (this.options.insertion) { new this.options.insertion(receiver, response); } else { Element.update(receiver, response); }   }

if (this.responseIsSuccess) { if (this.onComplete) setTimeout(this.onComplete.bind(this), 10); } } });

Ajax.PeriodicalUpdater = Class.create; 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 = (this.options.decay || 1);

this.updater = {}; this.container = container; this.url = url;

this.start; },

start: function { this.options.onComplete = this.updateComplete.bind(this); this.onTimerEvent; },

stop: function { this.updater.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); },

updateComplete: function(request) { if (this.options.decay) { this.decay = (request.responseText == this.lastText ?       this.decay * this.options.decay : 1);

this.lastText = request.responseText; }   this.timer = setTimeout(this.onTimerEvent.bind(this),      this.decay * this.frequency * 1000); },

onTimerEvent: function { this.updater = new Ajax.Updater(this.container, this.url, this.options); } }); 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; }); }

/*--*/

if (!window.Element) { var Element = new Object; }

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[Element.visible(element) ? 'hide' : 'show'](element); } },

hide: function { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); element.style.display = 'none'; } },

show: function { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); element.style.display = ''; } },

remove: function(element) { 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; },

classNames: function(element) { return new Element.ClassNames(element); },

hasClassName: function(element, className) { if (!(element = $(element))) return; return Element.classNames(element).include(className); },

addClassName: function(element, className) { if (!(element = $(element))) return; return Element.classNames(element).add(className); },

removeClassName: function(element, className) { 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)) 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; } });

var Toggle = new Object; Toggle.display = Element.toggle;

/*--*/

Abstract.Insertion = function(adjacency) { this.adjacency = adjacency; }

Abstract.Insertion.prototype = { initialize: function(element, content) { this.element = $(element); this.content = content.stripScripts;

if (this.adjacency && this.element.insertAdjacentHTML) { 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.insertContent([this.range.createContextualFragment(this.content)]); }

setTimeout(function {content.evalScripts}, 10); },

contentFromAnonymousTable: function { var div = document.createElement('div'); div.innerHTML = ' '; return $A(div.childNodes[0].childNodes[0].childNodes); } }

var Insertion = new Object;

Insertion.Before = Class.create; Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { initializeRange: function {    this.range.setStartBefore(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 = Object.extend(new Abstract.Insertion('afterBegin'), { initializeRange: function {    this.range.selectNodeContents(this.element);    this.range.collapse(true);  },

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 = Object.extend(new Abstract.Insertion('beforeEnd'), { initializeRange: function {    this.range.selectNodeContents(this.element);    this.range.collapse(this.element);  },

insertContent: function(fragments) { fragments.each((function(fragment) { this.element.appendChild(fragment); }).bind(this)); } });

Insertion.After = Class.create; Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { initializeRange: function {    this.range.setStartAfter(this.element);  },

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++) $(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; },

select: function(element) { $(element).select; },

activate: function(element) { element = $(element); element.focus; if (element.select) element.select; } }

/*--*/

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) { form = $(form); var elements = new Array;

for (tagName in Form.Element.Serializers) { var tagElements = form.getElementsByTagName(tagName); for (var j = 0; j < tagElements.length; j++) elements.push(tagElements[j]); }   return elements; },

getInputs: function(form, typeName, name) { 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)) continue; matchingInputs.push(input); }

return matchingInputs; },

disable: function(form) { var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.blur; element.disabled = 'true'; } },

enable: function(form) { var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.disabled = ''; } },

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) { Field.activate(Form.findFirstElement(form)); },

reset: function(form) { $(form).reset; } }

Form.Element = { serialize: function(element) { element = $(element); var method = element.tagName.toLowerCase; var parameter = Form.Element.Serializers[method](element);

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) { element = $(element); var method = element.tagName.toLowerCase; var parameter = Form.Element.Serializers[method](element);

if (parameter) return parameter[1]; } }

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); }   return false; },

inputSelector: function(element) { if (element.checked) return [element.name, element.value]; },

textarea: function(element) { return [element.name, element.value]; },

select: function(element) { 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]; } }

/*--*/

var $F = Form.Element.getValue;

/*--*/

Abstract.TimedObserver = function {} Abstract.TimedObserver.prototype = { initialize: function(element, frequency, callback) { 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) { this.callback(this.element, value); this.lastValue = value; } } }

Form.Element.Observer = Class.create; Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver, { getValue: function {    return Form.Element.getValue(this.element);  } });

Form.Observer = Class.create; Form.Observer.prototype = Object.extend(new Abstract.TimedObserver, { getValue: function {    return Form.serialize(this.element);  } });

/*--*/

Abstract.EventObserver = function {} 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) { this.callback(this.element, value); 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 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; case 'password': case 'text': case 'textarea': case 'select-one': case 'select-multiple': Event.observe(element, 'change', this.onElementEvent.bind(this)); break; }   }  } }

Form.Element.EventObserver = Class.create; Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver, { getValue: function {    return Form.Element.getValue(this.element);  } });

Form.EventObserver = Class.create; Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver, { getValue: function {    return Form.serialize(this.element);  } }); if (!window.Event) { var Event = new Object; }

Object.extend(Event, { KEY_BACKSPACE: 8,  KEY_TAB:       9,  KEY_RETURN:   13,  KEY_ESC:      27,  KEY_LEFT:     37,  KEY_UP:       38,  KEY_RIGHT:    39,  KEY_DOWN:     40,  KEY_DELETE:   46,

element: function(event) { return event.target || event.srcElement; },

isLeftClick: function(event) { return (((event.which) && (event.which == 1)) ||           ((event.button) && (event.button == 1))); },

pointerX: function(event) { return event.pageX || (event.clientX +     (document.documentElement.scrollLeft || document.body.scrollLeft)); },

pointerY: function(event) { return event.pageY || (event.clientY +     (document.documentElement.scrollTop || document.body.scrollTop)); },

stop: function(event) { if (event.preventDefault) { event.preventDefault; event.stopPropagation; } else { event.returnValue = false; event.cancelBubble = true; } },

// find the first node with the given tagName, starting from the // node the event was triggered on; traverses the DOM upwards findElement: function(event, tagName) { var element = Event.element(event); while (element.parentNode && (!element.tagName || (element.tagName.toUpperCase != tagName.toUpperCase))) element = element.parentNode; return element; }, findElementByClass: function(event, className) { var element = Event.element(event); while (element.parentNode && !Element.hasClassName(element,className)) element = element.parentNode; return element; },

observers: false,

_observeAndCache: function(element, name, observer, useCapture) { if (!this.observers) this.observers = []; if (element.addEventListener) { this.observers.push([element, name, observer, useCapture]); element.addEventListener(name, observer, useCapture); } else if (element.attachEvent) { this.observers.push([element, name, observer, useCapture]); element.attachEvent('on' + name, observer); } },

unloadCache: function { if (!Event.observers) return; for (var i = 0; i < Event.observers.length; i++) { Event.stopObserving.apply(this, Event.observers[i]); Event.observers[i][0] = null; }   Event.observers = false; },

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) { element.detachEvent('on' + name, observer); } } });

/* 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,

// must be called before calling withinIncludingScrolloffset, every time the // page is scrolled prepare: function { this.deltaX = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;   this.deltaY =  window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; },

realOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.scrollTop || 0; valueL += element.scrollLeft || 0; element = element.parentNode; } while (element); return [valueL, valueT]; },

cumulativeOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; } while (element); 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) return this.withinIncludingScrolloffsets(element, x, y); this.xcomp = x;   this.ycomp = y;    this.offset = this.cumulativeOffset(element);

return (y >= this.offset[1] &&           y <  this.offset[1] + element.offsetHeight &&            x >= this.offset[0] &&            x <  this.offset[0] + element.offsetWidth); },

withinIncludingScrolloffsets: function(element, x, y) { var offsetcache = this.realOffset(element);

this.xcomp = x + offsetcache[0] - this.deltaX; this.ycomp = y + offsetcache[1] - this.deltaY; this.offset = this.cumulativeOffset(element);

return (this.ycomp >= this.offset[1] &&           this.ycomp <  this.offset[1] + element.offsetHeight &&            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) / element.offsetHeight; if (mode == 'horizontal') return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 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;

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]; } } //