1 /* Prototype JavaScript framework, version 1.7_rc2
2 * (c) 2005-2010 Sam Stephenson
4 * Prototype is freely distributable under the terms of an MIT-style license.
5 * For details, see the Prototype web site: http://www.prototypejs.org/
7 *--------------------------------------------------------------------------*/
14 var ua = navigator.userAgent;
15 var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
17 IE: !!window.attachEvent && !isOpera,
19 WebKit: ua.indexOf('AppleWebKit/') > -1,
20 Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21 MobileSafari: /Apple.*Mobile/.test(ua)
26 XPath: !!document.evaluate,
28 SelectorsAPI: !!document.querySelector,
30 ElementExtensions: (function() {
31 var constructor = window.Element || window.HTMLElement;
32 return !!(constructor && constructor.prototype);
34 SpecificElementExtensions: (function() {
35 if (typeof window.HTMLDivElement !== 'undefined')
38 var div = document.createElement('div'),
39 form = document.createElement('form'),
42 if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
52 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
53 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
55 emptyFunction: function() { },
57 K: function(x) { return x }
60 if (Prototype.Browser.MobileSafari)
61 Prototype.BrowserFeatures.SpecificElementExtensions = false;
71 for (var i = 0, length = arguments.length; i < length; i++) {
72 var lambda = arguments[i];
74 returnValue = lambda();
83 /* Based on Alex Arnell's inheritance implementation. */
85 var Class = (function() {
87 var IS_DONTENUM_BUGGY = (function(){
88 for (var p in { toString: 1 }) {
89 if (p === 'toString') return false;
94 function subclass() {};
96 var parent = null, properties = $A(arguments);
97 if (Object.isFunction(properties[0]))
98 parent = properties.shift();
101 this.initialize.apply(this, arguments);
104 Object.extend(klass, Class.Methods);
105 klass.superclass = parent;
106 klass.subclasses = [];
109 subclass.prototype = parent.prototype;
110 klass.prototype = new subclass;
111 parent.subclasses.push(klass);
114 for (var i = 0, length = properties.length; i < length; i++)
115 klass.addMethods(properties[i]);
117 if (!klass.prototype.initialize)
118 klass.prototype.initialize = Prototype.emptyFunction;
120 klass.prototype.constructor = klass;
124 function addMethods(source) {
125 var ancestor = this.superclass && this.superclass.prototype,
126 properties = Object.keys(source);
128 if (IS_DONTENUM_BUGGY) {
129 if (source.toString != Object.prototype.toString)
130 properties.push("toString");
131 if (source.valueOf != Object.prototype.valueOf)
132 properties.push("valueOf");
135 for (var i = 0, length = properties.length; i < length; i++) {
136 var property = properties[i], value = source[property];
137 if (ancestor && Object.isFunction(value) &&
138 value.argumentNames()[0] == "$super") {
140 value = (function(m) {
141 return function() { return ancestor[m].apply(this, arguments); };
142 })(property).wrap(method);
144 value.valueOf = method.valueOf.bind(method);
145 value.toString = method.toString.bind(method);
147 this.prototype[property] = value;
156 addMethods: addMethods
162 var _toString = Object.prototype.toString,
164 UNDEFINED_TYPE = 'Undefined',
165 BOOLEAN_TYPE = 'Boolean',
166 NUMBER_TYPE = 'Number',
167 STRING_TYPE = 'String',
168 OBJECT_TYPE = 'Object',
169 BOOLEAN_CLASS = '[object Boolean]',
170 NUMBER_CLASS = '[object Number]',
171 STRING_CLASS = '[object String]',
172 ARRAY_CLASS = '[object Array]',
173 NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
174 typeof JSON.stringify === 'function' &&
175 JSON.stringify(0) === '0' &&
176 typeof JSON.stringify(Prototype.K) === 'undefined';
180 case null: return NULL_TYPE;
181 case (void 0): return UNDEFINED_TYPE;
185 case 'boolean': return BOOLEAN_TYPE;
186 case 'number': return NUMBER_TYPE;
187 case 'string': return STRING_TYPE;
192 function extend(destination, source) {
193 for (var property in source)
194 destination[property] = source[property];
198 function inspect(object) {
200 if (isUndefined(object)) return 'undefined';
201 if (object === null) return 'null';
202 return object.inspect ? object.inspect() : String(object);
204 if (e instanceof RangeError) return '...';
209 function toJSON(value) {
210 return Str('', { '': value }, []);
213 function Str(key, holder, stack) {
214 var value = holder[key],
217 if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
218 value = value.toJSON(key);
221 var _class = _toString.call(value);
227 value = value.valueOf();
231 case null: return 'null';
232 case true: return 'true';
233 case false: return 'false';
239 return value.inspect(true);
241 return isFinite(value) ? String(value) : 'null';
244 for (var i = 0, length = stack.length; i < length; i++) {
245 if (stack[i] === value) { throw new TypeError(); }
250 if (_class === ARRAY_CLASS) {
251 for (var i = 0, length = value.length; i < length; i++) {
252 var str = Str(i, value, stack);
253 partial.push(typeof str === 'undefined' ? 'null' : str);
255 partial = '[' + partial.join(',') + ']';
257 var keys = Object.keys(value);
258 for (var i = 0, length = keys.length; i < length; i++) {
259 var key = keys[i], str = Str(key, value, stack);
260 if (typeof str !== "undefined") {
261 partial.push(key.inspect(true)+ ':' + str);
264 partial = '{' + partial.join(',') + '}';
271 function stringify(object) {
272 return JSON.stringify(object);
275 function toQueryString(object) {
276 return $H(object).toQueryString();
279 function toHTML(object) {
280 return object && object.toHTML ? object.toHTML() : String.interpret(object);
283 function keys(object) {
284 if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
286 for (var property in object) {
287 if (object.hasOwnProperty(property)) {
288 results.push(property);
294 function values(object) {
296 for (var property in object)
297 results.push(object[property]);
301 function clone(object) {
302 return extend({ }, object);
305 function isElement(object) {
306 return !!(object && object.nodeType == 1);
309 function isArray(object) {
310 return _toString.call(object) === ARRAY_CLASS;
313 var hasNativeIsArray = (typeof Array.isArray == 'function')
314 && Array.isArray([]) && !Array.isArray({});
316 if (hasNativeIsArray) {
317 isArray = Array.isArray;
320 function isHash(object) {
321 return object instanceof Hash;
324 function isFunction(object) {
325 return typeof object === "function";
328 function isString(object) {
329 return _toString.call(object) === STRING_CLASS;
332 function isNumber(object) {
333 return _toString.call(object) === NUMBER_CLASS;
336 function isUndefined(object) {
337 return typeof object === "undefined";
343 toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
344 toQueryString: toQueryString,
346 keys: Object.keys || keys,
349 isElement: isElement,
352 isFunction: isFunction,
355 isUndefined: isUndefined
358 Object.extend(Function.prototype, (function() {
359 var slice = Array.prototype.slice;
361 function update(array, args) {
362 var arrayLength = array.length, length = args.length;
363 while (length--) array[arrayLength + length] = args[length];
367 function merge(array, args) {
368 array = slice.call(array, 0);
369 return update(array, args);
372 function argumentNames() {
373 var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
374 .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
375 .replace(/\s+/g, '').split(',');
376 return names.length == 1 && !names[0] ? [] : names;
379 function bind(context) {
380 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
381 var __method = this, args = slice.call(arguments, 1);
383 var a = merge(args, arguments);
384 return __method.apply(context, a);
388 function bindAsEventListener(context) {
389 var __method = this, args = slice.call(arguments, 1);
390 return function(event) {
391 var a = update([event || window.event], args);
392 return __method.apply(context, a);
397 if (!arguments.length) return this;
398 var __method = this, args = slice.call(arguments, 0);
400 var a = merge(args, arguments);
401 return __method.apply(this, a);
405 function delay(timeout) {
406 var __method = this, args = slice.call(arguments, 1);
407 timeout = timeout * 1000;
408 return window.setTimeout(function() {
409 return __method.apply(__method, args);
414 var args = update([0.01], arguments);
415 return this.delay.apply(this, args);
418 function wrap(wrapper) {
421 var a = update([__method.bind(this)], arguments);
422 return wrapper.apply(this, a);
426 function methodize() {
427 if (this._methodized) return this._methodized;
429 return this._methodized = function() {
430 var a = update([this], arguments);
431 return __method.apply(null, a);
436 argumentNames: argumentNames,
438 bindAsEventListener: bindAsEventListener,
452 function toISOString() {
453 return this.getUTCFullYear() + '-' +
454 (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
455 this.getUTCDate().toPaddedString(2) + 'T' +
456 this.getUTCHours().toPaddedString(2) + ':' +
457 this.getUTCMinutes().toPaddedString(2) + ':' +
458 this.getUTCSeconds().toPaddedString(2) + 'Z';
463 return this.toISOString();
466 if (!proto.toISOString) proto.toISOString = toISOString;
467 if (!proto.toJSON) proto.toJSON = toJSON;
472 RegExp.prototype.match = RegExp.prototype.test;
474 RegExp.escape = function(str) {
475 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
477 var PeriodicalExecuter = Class.create({
478 initialize: function(callback, frequency) {
479 this.callback = callback;
480 this.frequency = frequency;
481 this.currentlyExecuting = false;
483 this.registerCallback();
486 registerCallback: function() {
487 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
490 execute: function() {
495 if (!this.timer) return;
496 clearInterval(this.timer);
500 onTimerEvent: function() {
501 if (!this.currentlyExecuting) {
503 this.currentlyExecuting = true;
505 this.currentlyExecuting = false;
507 this.currentlyExecuting = false;
513 Object.extend(String, {
514 interpret: function(value) {
515 return value == null ? '' : String(value);
527 Object.extend(String.prototype, (function() {
528 var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
529 typeof JSON.parse === 'function' &&
530 JSON.parse('{"test": true}').test;
532 function prepareReplacement(replacement) {
533 if (Object.isFunction(replacement)) return replacement;
534 var template = new Template(replacement);
535 return function(match) { return template.evaluate(match) };
538 function gsub(pattern, replacement) {
539 var result = '', source = this, match;
540 replacement = prepareReplacement(replacement);
542 if (Object.isString(pattern))
543 pattern = RegExp.escape(pattern);
545 if (!(pattern.length || pattern.source)) {
546 replacement = replacement('');
547 return replacement + source.split('').join(replacement) + replacement;
550 while (source.length > 0) {
551 if (match = source.match(pattern)) {
552 result += source.slice(0, match.index);
553 result += String.interpret(replacement(match));
554 source = source.slice(match.index + match[0].length);
556 result += source, source = '';
562 function sub(pattern, replacement, count) {
563 replacement = prepareReplacement(replacement);
564 count = Object.isUndefined(count) ? 1 : count;
566 return this.gsub(pattern, function(match) {
567 if (--count < 0) return match[0];
568 return replacement(match);
572 function scan(pattern, iterator) {
573 this.gsub(pattern, iterator);
577 function truncate(length, truncation) {
578 length = length || 30;
579 truncation = Object.isUndefined(truncation) ? '...' : truncation;
580 return this.length > length ?
581 this.slice(0, length - truncation.length) + truncation : String(this);
585 return this.replace(/^\s+/, '').replace(/\s+$/, '');
588 function stripTags() {
589 return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
592 function stripScripts() {
593 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
596 function extractScripts() {
597 var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
598 matchOne = new RegExp(Prototype.ScriptFragment, 'im');
599 return (this.match(matchAll) || []).map(function(scriptTag) {
600 return (scriptTag.match(matchOne) || ['', ''])[1];
604 function evalScripts() {
605 return this.extractScripts().map(function(script) { return eval(script) });
608 function escapeHTML() {
609 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
612 function unescapeHTML() {
613 return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
617 function toQueryParams(separator) {
618 var match = this.strip().match(/([^?#]*)(#.*)?$/);
619 if (!match) return { };
621 return match[1].split(separator || '&').inject({ }, function(hash, pair) {
622 if ((pair = pair.split('='))[0]) {
623 var key = decodeURIComponent(pair.shift()),
624 value = pair.length > 1 ? pair.join('=') : pair[0];
626 if (value != undefined) value = decodeURIComponent(value);
629 if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
630 hash[key].push(value);
632 else hash[key] = value;
639 return this.split('');
643 return this.slice(0, this.length - 1) +
644 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
647 function times(count) {
648 return count < 1 ? '' : new Array(count + 1).join(this);
651 function camelize() {
652 return this.replace(/-+(.)?/g, function(match, chr) {
653 return chr ? chr.toUpperCase() : '';
657 function capitalize() {
658 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
661 function underscore() {
662 return this.replace(/::/g, '/')
663 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
664 .replace(/([a-z\d])([A-Z])/g, '$1_$2')
669 function dasherize() {
670 return this.replace(/_/g, '-');
673 function inspect(useDoubleQuotes) {
674 var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
675 if (character in String.specialChar) {
676 return String.specialChar[character];
678 return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
680 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
681 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
684 function unfilterJSON(filter) {
685 return this.replace(filter || Prototype.JSONFilter, '$1');
690 if (str.blank()) return false;
691 str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
692 str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
693 str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
694 return (/^[\],:{}\s]*$/).test(str);
697 function evalJSON(sanitize) {
698 var json = this.unfilterJSON(),
699 cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
701 json = json.replace(cx, function (a) {
702 return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
706 if (!sanitize || json.isJSON()) return eval('(' + json + ')');
708 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
711 function parseJSON() {
712 var json = this.unfilterJSON();
713 return JSON.parse(json);
716 function include(pattern) {
717 return this.indexOf(pattern) > -1;
720 function startsWith(pattern) {
721 return this.lastIndexOf(pattern, 0) === 0;
724 function endsWith(pattern) {
725 var d = this.length - pattern.length;
726 return d >= 0 && this.indexOf(pattern, d) === d;
734 return /^\s*$/.test(this);
737 function interpolate(object, pattern) {
738 return new Template(this, pattern).evaluate(object);
746 strip: String.prototype.trim || strip,
747 stripTags: stripTags,
748 stripScripts: stripScripts,
749 extractScripts: extractScripts,
750 evalScripts: evalScripts,
751 escapeHTML: escapeHTML,
752 unescapeHTML: unescapeHTML,
753 toQueryParams: toQueryParams,
754 parseQuery: toQueryParams,
759 capitalize: capitalize,
760 underscore: underscore,
761 dasherize: dasherize,
763 unfilterJSON: unfilterJSON,
765 evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
767 startsWith: startsWith,
771 interpolate: interpolate
775 var Template = Class.create({
776 initialize: function(template, pattern) {
777 this.template = template.toString();
778 this.pattern = pattern || Template.Pattern;
781 evaluate: function(object) {
782 if (object && Object.isFunction(object.toTemplateReplacements))
783 object = object.toTemplateReplacements();
785 return this.template.gsub(this.pattern, function(match) {
786 if (object == null) return (match[1] + '');
788 var before = match[1] || '';
789 if (before == '\\') return match[2];
791 var ctx = object, expr = match[3],
792 pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
794 match = pattern.exec(expr);
795 if (match == null) return before;
797 while (match != null) {
798 var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
800 if (null == ctx || '' == match[3]) break;
801 expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
802 match = pattern.exec(expr);
805 return before + String.interpret(ctx);
809 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
813 var Enumerable = (function() {
814 function each(iterator, context) {
817 this._each(function(value) {
818 iterator.call(context, value, index++);
821 if (e != $break) throw e;
826 function eachSlice(number, iterator, context) {
827 var index = -number, slices = [], array = this.toArray();
828 if (number < 1) return array;
829 while ((index += number) < array.length)
830 slices.push(array.slice(index, index+number));
831 return slices.collect(iterator, context);
834 function all(iterator, context) {
835 iterator = iterator || Prototype.K;
837 this.each(function(value, index) {
838 result = result && !!iterator.call(context, value, index);
839 if (!result) throw $break;
844 function any(iterator, context) {
845 iterator = iterator || Prototype.K;
847 this.each(function(value, index) {
848 if (result = !!iterator.call(context, value, index))
854 function collect(iterator, context) {
855 iterator = iterator || Prototype.K;
857 this.each(function(value, index) {
858 results.push(iterator.call(context, value, index));
863 function detect(iterator, context) {
865 this.each(function(value, index) {
866 if (iterator.call(context, value, index)) {
874 function findAll(iterator, context) {
876 this.each(function(value, index) {
877 if (iterator.call(context, value, index))
883 function grep(filter, iterator, context) {
884 iterator = iterator || Prototype.K;
887 if (Object.isString(filter))
888 filter = new RegExp(RegExp.escape(filter));
890 this.each(function(value, index) {
891 if (filter.match(value))
892 results.push(iterator.call(context, value, index));
897 function include(object) {
898 if (Object.isFunction(this.indexOf))
899 if (this.indexOf(object) != -1) return true;
902 this.each(function(value) {
903 if (value == object) {
911 function inGroupsOf(number, fillWith) {
912 fillWith = Object.isUndefined(fillWith) ? null : fillWith;
913 return this.eachSlice(number, function(slice) {
914 while(slice.length < number) slice.push(fillWith);
919 function inject(memo, iterator, context) {
920 this.each(function(value, index) {
921 memo = iterator.call(context, memo, value, index);
926 function invoke(method) {
927 var args = $A(arguments).slice(1);
928 return this.map(function(value) {
929 return value[method].apply(value, args);
933 function max(iterator, context) {
934 iterator = iterator || Prototype.K;
936 this.each(function(value, index) {
937 value = iterator.call(context, value, index);
938 if (result == null || value >= result)
944 function min(iterator, context) {
945 iterator = iterator || Prototype.K;
947 this.each(function(value, index) {
948 value = iterator.call(context, value, index);
949 if (result == null || value < result)
955 function partition(iterator, context) {
956 iterator = iterator || Prototype.K;
957 var trues = [], falses = [];
958 this.each(function(value, index) {
959 (iterator.call(context, value, index) ?
960 trues : falses).push(value);
962 return [trues, falses];
965 function pluck(property) {
967 this.each(function(value) {
968 results.push(value[property]);
973 function reject(iterator, context) {
975 this.each(function(value, index) {
976 if (!iterator.call(context, value, index))
982 function sortBy(iterator, context) {
983 return this.map(function(value, index) {
986 criteria: iterator.call(context, value, index)
988 }).sort(function(left, right) {
989 var a = left.criteria, b = right.criteria;
990 return a < b ? -1 : a > b ? 1 : 0;
999 var iterator = Prototype.K, args = $A(arguments);
1000 if (Object.isFunction(args.last()))
1001 iterator = args.pop();
1003 var collections = [this].concat(args).map($A);
1004 return this.map(function(value, index) {
1005 return iterator(collections.pluck(index));
1010 return this.toArray().length;
1013 function inspect() {
1014 return '#<Enumerable:' + this.toArray().inspect() + '>';
1027 eachSlice: eachSlice,
1041 inGroupsOf: inGroupsOf,
1046 partition: partition,
1059 function $A(iterable) {
1060 if (!iterable) return [];
1061 if ('toArray' in Object(iterable)) return iterable.toArray();
1062 var length = iterable.length || 0, results = new Array(length);
1063 while (length--) results[length] = iterable[length];
1068 function $w(string) {
1069 if (!Object.isString(string)) return [];
1070 string = string.strip();
1071 return string ? string.split(/\s+/) : [];
1078 var arrayProto = Array.prototype,
1079 slice = arrayProto.slice,
1080 _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1082 function each(iterator) {
1083 for (var i = 0, length = this.length; i < length; i++)
1086 if (!_each) _each = each;
1098 return this[this.length - 1];
1101 function compact() {
1102 return this.select(function(value) {
1103 return value != null;
1107 function flatten() {
1108 return this.inject([], function(array, value) {
1109 if (Object.isArray(value))
1110 return array.concat(value.flatten());
1116 function without() {
1117 var values = slice.call(arguments, 0);
1118 return this.select(function(value) {
1119 return !values.include(value);
1123 function reverse(inline) {
1124 return (inline === false ? this.toArray() : this)._reverse();
1127 function uniq(sorted) {
1128 return this.inject([], function(array, value, index) {
1129 if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1135 function intersect(array) {
1136 return this.uniq().findAll(function(item) {
1137 return array.detect(function(value) { return item === value });
1143 return slice.call(this, 0);
1150 function inspect() {
1151 return '[' + this.map(Object.inspect).join(', ') + ']';
1154 function indexOf(item, i) {
1156 var length = this.length;
1157 if (i < 0) i = length + i;
1158 for (; i < length; i++)
1159 if (this[i] === item) return i;
1163 function lastIndexOf(item, i) {
1164 i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1165 var n = this.slice(0, i).reverse().indexOf(item);
1166 return (n < 0) ? n : i - n - 1;
1170 var array = slice.call(this, 0), item;
1171 for (var i = 0, length = arguments.length; i < length; i++) {
1172 item = arguments[i];
1173 if (Object.isArray(item) && !('callee' in item)) {
1174 for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1175 array.push(item[j]);
1183 Object.extend(arrayProto, Enumerable);
1185 if (!arrayProto._reverse)
1186 arrayProto._reverse = arrayProto.reverse;
1188 Object.extend(arrayProto, {
1198 intersect: intersect,
1205 var CONCAT_ARGUMENTS_BUGGY = (function() {
1206 return [].concat(arguments)[0][0] !== 1;
1209 if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1211 if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1212 if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1214 function $H(object) {
1215 return new Hash(object);
1218 var Hash = Class.create(Enumerable, (function() {
1219 function initialize(object) {
1220 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1224 function _each(iterator) {
1225 for (var key in this._object) {
1226 var value = this._object[key], pair = [key, value];
1233 function set(key, value) {
1234 return this._object[key] = value;
1238 if (this._object[key] !== Object.prototype[key])
1239 return this._object[key];
1242 function unset(key) {
1243 var value = this._object[key];
1244 delete this._object[key];
1248 function toObject() {
1249 return Object.clone(this._object);
1255 return this.pluck('key');
1259 return this.pluck('value');
1262 function index(value) {
1263 var match = this.detect(function(pair) {
1264 return pair.value === value;
1266 return match && match.key;
1269 function merge(object) {
1270 return this.clone().update(object);
1273 function update(object) {
1274 return new Hash(object).inject(this, function(result, pair) {
1275 result.set(pair.key, pair.value);
1280 function toQueryPair(key, value) {
1281 if (Object.isUndefined(value)) return key;
1282 return key + '=' + encodeURIComponent(String.interpret(value));
1285 function toQueryString() {
1286 return this.inject([], function(results, pair) {
1287 var key = encodeURIComponent(pair.key), values = pair.value;
1289 if (values && typeof values == 'object') {
1290 if (Object.isArray(values))
1291 return results.concat(values.map(toQueryPair.curry(key)));
1292 } else results.push(toQueryPair(key, values));
1297 function inspect() {
1298 return '#<Hash:{' + this.map(function(pair) {
1299 return pair.map(Object.inspect).join(': ');
1300 }).join(', ') + '}>';
1304 return new Hash(this);
1308 initialize: initialize,
1314 toTemplateReplacements: toObject,
1320 toQueryString: toQueryString,
1328 Object.extend(Number.prototype, (function() {
1329 function toColorPart() {
1330 return this.toPaddedString(2, 16);
1337 function times(iterator, context) {
1338 $R(0, this, true).each(iterator, context);
1342 function toPaddedString(length, radix) {
1343 var string = this.toString(radix || 10);
1344 return '0'.times(length - string.length) + string;
1348 return Math.abs(this);
1352 return Math.round(this);
1356 return Math.ceil(this);
1360 return Math.floor(this);
1364 toColorPart: toColorPart,
1367 toPaddedString: toPaddedString,
1375 function $R(start, end, exclusive) {
1376 return new ObjectRange(start, end, exclusive);
1379 var ObjectRange = Class.create(Enumerable, (function() {
1380 function initialize(start, end, exclusive) {
1383 this.exclusive = exclusive;
1386 function _each(iterator) {
1387 var value = this.start;
1388 while (this.include(value)) {
1390 value = value.succ();
1394 function include(value) {
1395 if (value < this.start)
1398 return value < this.end;
1399 return value <= this.end;
1403 initialize: initialize,
1412 getTransport: function() {
1414 function() {return new XMLHttpRequest()},
1415 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1416 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1420 activeRequestCount: 0
1426 _each: function(iterator) {
1427 this.responders._each(iterator);
1430 register: function(responder) {
1431 if (!this.include(responder))
1432 this.responders.push(responder);
1435 unregister: function(responder) {
1436 this.responders = this.responders.without(responder);
1439 dispatch: function(callback, request, transport, json) {
1440 this.each(function(responder) {
1441 if (Object.isFunction(responder[callback])) {
1443 responder[callback].apply(responder, [request, transport, json]);
1450 Object.extend(Ajax.Responders, Enumerable);
1452 Ajax.Responders.register({
1453 onCreate: function() { Ajax.activeRequestCount++ },
1454 onComplete: function() { Ajax.activeRequestCount-- }
1456 Ajax.Base = Class.create({
1457 initialize: function(options) {
1461 contentType: 'application/x-www-form-urlencoded',
1467 Object.extend(this.options, options || { });
1469 this.options.method = this.options.method.toLowerCase();
1471 if (Object.isString(this.options.parameters))
1472 this.options.parameters = this.options.parameters.toQueryParams();
1473 else if (Object.isHash(this.options.parameters))
1474 this.options.parameters = this.options.parameters.toObject();
1477 Ajax.Request = Class.create(Ajax.Base, {
1480 initialize: function($super, url, options) {
1482 this.transport = Ajax.getTransport();
1486 request: function(url) {
1488 this.method = this.options.method;
1489 var params = Object.clone(this.options.parameters);
1491 if (!['get', 'post'].include(this.method)) {
1492 params['_method'] = this.method;
1493 this.method = 'post';
1496 this.parameters = params;
1498 if (params = Object.toQueryString(params)) {
1499 if (this.method == 'get')
1500 this.url += (this.url.include('?') ? '&' : '?') + params;
1501 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1506 var response = new Ajax.Response(this);
1507 if (this.options.onCreate) this.options.onCreate(response);
1508 Ajax.Responders.dispatch('onCreate', this, response);
1510 this.transport.open(this.method.toUpperCase(), this.url,
1511 this.options.asynchronous);
1513 if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1515 this.transport.onreadystatechange = this.onStateChange.bind(this);
1516 this.setRequestHeaders();
1518 this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1519 this.transport.send(this.body);
1521 /* Force Firefox to handle ready state 4 for synchronous requests */
1522 if (!this.options.asynchronous && this.transport.overrideMimeType)
1523 this.onStateChange();
1527 this.dispatchException(e);
1531 onStateChange: function() {
1532 var readyState = this.transport.readyState;
1533 if (readyState > 1 && !((readyState == 4) && this._complete))
1534 this.respondToReadyState(this.transport.readyState);
1537 setRequestHeaders: function() {
1539 'X-Requested-With': 'XMLHttpRequest',
1540 'X-Prototype-Version': Prototype.Version,
1541 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1544 if (this.method == 'post') {
1545 headers['Content-type'] = this.options.contentType +
1546 (this.options.encoding ? '; charset=' + this.options.encoding : '');
1548 /* Force "Connection: close" for older Mozilla browsers to work
1549 * around a bug where XMLHttpRequest sends an incorrect
1550 * Content-length header. See Mozilla Bugzilla #246651.
1552 if (this.transport.overrideMimeType &&
1553 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1554 headers['Connection'] = 'close';
1557 if (typeof this.options.requestHeaders == 'object') {
1558 var extras = this.options.requestHeaders;
1560 if (Object.isFunction(extras.push))
1561 for (var i = 0, length = extras.length; i < length; i += 2)
1562 headers[extras[i]] = extras[i+1];
1564 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1567 for (var name in headers)
1568 this.transport.setRequestHeader(name, headers[name]);
1571 success: function() {
1572 var status = this.getStatus();
1573 return !status || (status >= 200 && status < 300);
1576 getStatus: function() {
1578 return this.transport.status || 0;
1579 } catch (e) { return 0 }
1582 respondToReadyState: function(readyState) {
1583 var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1585 if (state == 'Complete') {
1587 this._complete = true;
1588 (this.options['on' + response.status]
1589 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1590 || Prototype.emptyFunction)(response, response.headerJSON);
1592 this.dispatchException(e);
1595 var contentType = response.getHeader('Content-type');
1596 if (this.options.evalJS == 'force'
1597 || (this.options.evalJS && this.isSameOrigin() && contentType
1598 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1599 this.evalResponse();
1603 (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1604 Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1606 this.dispatchException(e);
1609 if (state == 'Complete') {
1610 this.transport.onreadystatechange = Prototype.emptyFunction;
1614 isSameOrigin: function() {
1615 var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1616 return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1617 protocol: location.protocol,
1618 domain: document.domain,
1619 port: location.port ? ':' + location.port : ''
1623 getHeader: function(name) {
1625 return this.transport.getResponseHeader(name) || null;
1626 } catch (e) { return null; }
1629 evalResponse: function() {
1631 return eval((this.transport.responseText || '').unfilterJSON());
1633 this.dispatchException(e);
1637 dispatchException: function(exception) {
1638 (this.options.onException || Prototype.emptyFunction)(this, exception);
1639 Ajax.Responders.dispatch('onException', this, exception);
1643 Ajax.Request.Events =
1644 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1653 Ajax.Response = Class.create({
1654 initialize: function(request){
1655 this.request = request;
1656 var transport = this.transport = request.transport,
1657 readyState = this.readyState = transport.readyState;
1659 if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1660 this.status = this.getStatus();
1661 this.statusText = this.getStatusText();
1662 this.responseText = String.interpret(transport.responseText);
1663 this.headerJSON = this._getHeaderJSON();
1666 if (readyState == 4) {
1667 var xml = transport.responseXML;
1668 this.responseXML = Object.isUndefined(xml) ? null : xml;
1669 this.responseJSON = this._getResponseJSON();
1677 getStatus: Ajax.Request.prototype.getStatus,
1679 getStatusText: function() {
1681 return this.transport.statusText || '';
1682 } catch (e) { return '' }
1685 getHeader: Ajax.Request.prototype.getHeader,
1687 getAllHeaders: function() {
1689 return this.getAllResponseHeaders();
1690 } catch (e) { return null }
1693 getResponseHeader: function(name) {
1694 return this.transport.getResponseHeader(name);
1697 getAllResponseHeaders: function() {
1698 return this.transport.getAllResponseHeaders();
1701 _getHeaderJSON: function() {
1702 var json = this.getHeader('X-JSON');
1703 if (!json) return null;
1704 json = decodeURIComponent(escape(json));
1706 return json.evalJSON(this.request.options.sanitizeJSON ||
1707 !this.request.isSameOrigin());
1709 this.request.dispatchException(e);
1713 _getResponseJSON: function() {
1714 var options = this.request.options;
1715 if (!options.evalJSON || (options.evalJSON != 'force' &&
1716 !(this.getHeader('Content-type') || '').include('application/json')) ||
1717 this.responseText.blank())
1720 return this.responseText.evalJSON(options.sanitizeJSON ||
1721 !this.request.isSameOrigin());
1723 this.request.dispatchException(e);
1728 Ajax.Updater = Class.create(Ajax.Request, {
1729 initialize: function($super, container, url, options) {
1731 success: (container.success || container),
1732 failure: (container.failure || (container.success ? null : container))
1735 options = Object.clone(options);
1736 var onComplete = options.onComplete;
1737 options.onComplete = (function(response, json) {
1738 this.updateContent(response.responseText);
1739 if (Object.isFunction(onComplete)) onComplete(response, json);
1742 $super(url, options);
1745 updateContent: function(responseText) {
1746 var receiver = this.container[this.success() ? 'success' : 'failure'],
1747 options = this.options;
1749 if (!options.evalScripts) responseText = responseText.stripScripts();
1751 if (receiver = $(receiver)) {
1752 if (options.insertion) {
1753 if (Object.isString(options.insertion)) {
1754 var insertion = { }; insertion[options.insertion] = responseText;
1755 receiver.insert(insertion);
1757 else options.insertion(receiver, responseText);
1759 else receiver.update(responseText);
1764 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1765 initialize: function($super, container, url, options) {
1767 this.onComplete = this.options.onComplete;
1769 this.frequency = (this.options.frequency || 2);
1770 this.decay = (this.options.decay || 1);
1773 this.container = container;
1780 this.options.onComplete = this.updateComplete.bind(this);
1781 this.onTimerEvent();
1785 this.updater.options.onComplete = undefined;
1786 clearTimeout(this.timer);
1787 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1790 updateComplete: function(response) {
1791 if (this.options.decay) {
1792 this.decay = (response.responseText == this.lastText ?
1793 this.decay * this.options.decay : 1);
1795 this.lastText = response.responseText;
1797 this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1800 onTimerEvent: function() {
1801 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1806 function $(element) {
1807 if (arguments.length > 1) {
1808 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1809 elements.push($(arguments[i]));
1812 if (Object.isString(element))
1813 element = document.getElementById(element);
1814 return Element.extend(element);
1817 if (Prototype.BrowserFeatures.XPath) {
1818 document._getElementsByXPath = function(expression, parentElement) {
1820 var query = document.evaluate(expression, $(parentElement) || document,
1821 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1822 for (var i = 0, length = query.snapshotLength; i < length; i++)
1823 results.push(Element.extend(query.snapshotItem(i)));
1828 /*--------------------------------------------------------------------------*/
1830 if (!Node) var Node = { };
1832 if (!Node.ELEMENT_NODE) {
1833 Object.extend(Node, {
1837 CDATA_SECTION_NODE: 4,
1838 ENTITY_REFERENCE_NODE: 5,
1840 PROCESSING_INSTRUCTION_NODE: 7,
1843 DOCUMENT_TYPE_NODE: 10,
1844 DOCUMENT_FRAGMENT_NODE: 11,
1853 var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1855 var el = document.createElement('<input name="x">');
1856 return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1863 var element = global.Element;
1865 global.Element = function(tagName, attributes) {
1866 attributes = attributes || { };
1867 tagName = tagName.toLowerCase();
1868 var cache = Element.cache;
1869 if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1870 tagName = '<' + tagName + ' name="' + attributes.name + '">';
1871 delete attributes.name;
1872 return Element.writeAttribute(document.createElement(tagName), attributes);
1874 if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1875 return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1878 Object.extend(global.Element, element || { });
1879 if (element) global.Element.prototype = element.prototype;
1883 Element.idCounter = 1;
1884 Element.cache = { };
1886 function purgeElement(element) {
1887 var uid = element._prototypeUID;
1889 Element.stopObserving(element);
1890 element._prototypeUID = void 0;
1891 delete Element.Storage[uid];
1896 visible: function(element) {
1897 return $(element).style.display != 'none';
1900 toggle: function(element) {
1901 element = $(element);
1902 Element[Element.visible(element) ? 'hide' : 'show'](element);
1906 hide: function(element) {
1907 element = $(element);
1908 element.style.display = 'none';
1912 show: function(element) {
1913 element = $(element);
1914 element.style.display = '';
1918 remove: function(element) {
1919 element = $(element);
1920 element.parentNode.removeChild(element);
1924 update: (function(){
1926 var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1927 var el = document.createElement("select"),
1929 el.innerHTML = "<option value=\"test\">test</option>";
1930 if (el.options && el.options[0]) {
1931 isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1937 var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1939 var el = document.createElement("table");
1940 if (el && el.tBodies) {
1941 el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1942 var isBuggy = typeof el.tBodies[0] == "undefined";
1951 var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1952 var s = document.createElement("script"),
1955 s.appendChild(document.createTextNode(""));
1956 isBuggy = !s.firstChild ||
1957 s.firstChild && s.firstChild.nodeType !== 3;
1965 function update(element, content) {
1966 element = $(element);
1968 var descendants = element.getElementsByTagName('*'),
1969 i = descendants.length;
1970 while (i--) purgeElement(descendants[i]);
1972 if (content && content.toElement)
1973 content = content.toElement();
1975 if (Object.isElement(content))
1976 return element.update().insert(content);
1978 content = Object.toHTML(content);
1980 var tagName = element.tagName.toUpperCase();
1982 if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
1983 element.text = content;
1987 if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
1988 if (tagName in Element._insertionTranslations.tags) {
1989 while (element.firstChild) {
1990 element.removeChild(element.firstChild);
1992 Element._getContentFromAnonymousElement(tagName, content.stripScripts())
1993 .each(function(node) {
1994 element.appendChild(node)
1998 element.innerHTML = content.stripScripts();
2002 element.innerHTML = content.stripScripts();
2005 content.evalScripts.bind(content).defer();
2012 replace: function(element, content) {
2013 element = $(element);
2014 if (content && content.toElement) content = content.toElement();
2015 else if (!Object.isElement(content)) {
2016 content = Object.toHTML(content);
2017 var range = element.ownerDocument.createRange();
2018 range.selectNode(element);
2019 content.evalScripts.bind(content).defer();
2020 content = range.createContextualFragment(content.stripScripts());
2022 element.parentNode.replaceChild(content, element);
2026 insert: function(element, insertions) {
2027 element = $(element);
2029 if (Object.isString(insertions) || Object.isNumber(insertions) ||
2030 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2031 insertions = {bottom:insertions};
2033 var content, insert, tagName, childNodes;
2035 for (var position in insertions) {
2036 content = insertions[position];
2037 position = position.toLowerCase();
2038 insert = Element._insertionTranslations[position];
2040 if (content && content.toElement) content = content.toElement();
2041 if (Object.isElement(content)) {
2042 insert(element, content);
2046 content = Object.toHTML(content);
2048 tagName = ((position == 'before' || position == 'after')
2049 ? element.parentNode : element).tagName.toUpperCase();
2051 childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2053 if (position == 'top' || position == 'after') childNodes.reverse();
2054 childNodes.each(insert.curry(element));
2056 content.evalScripts.bind(content).defer();
2062 wrap: function(element, wrapper, attributes) {
2063 element = $(element);
2064 if (Object.isElement(wrapper))
2065 $(wrapper).writeAttribute(attributes || { });
2066 else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2067 else wrapper = new Element('div', wrapper);
2068 if (element.parentNode)
2069 element.parentNode.replaceChild(wrapper, element);
2070 wrapper.appendChild(element);
2074 inspect: function(element) {
2075 element = $(element);
2076 var result = '<' + element.tagName.toLowerCase();
2077 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2078 var property = pair.first(),
2079 attribute = pair.last(),
2080 value = (element[property] || '').toString();
2081 if (value) result += ' ' + attribute + '=' + value.inspect(true);
2083 return result + '>';
2086 recursivelyCollect: function(element, property, maximumLength) {
2087 element = $(element);
2088 maximumLength = maximumLength || -1;
2091 while (element = element[property]) {
2092 if (element.nodeType == 1)
2093 elements.push(Element.extend(element));
2094 if (elements.length == maximumLength)
2101 ancestors: function(element) {
2102 return Element.recursivelyCollect(element, 'parentNode');
2105 descendants: function(element) {
2106 return Element.select(element, "*");
2109 firstDescendant: function(element) {
2110 element = $(element).firstChild;
2111 while (element && element.nodeType != 1) element = element.nextSibling;
2115 immediateDescendants: function(element) {
2116 var results = [], child = $(element).firstChild;
2118 if (child.nodeType === 1) {
2119 results.push(Element.extend(child));
2121 child = child.nextSibling;
2126 previousSiblings: function(element, maximumLength) {
2127 return Element.recursivelyCollect(element, 'previousSibling');
2130 nextSiblings: function(element) {
2131 return Element.recursivelyCollect(element, 'nextSibling');
2134 siblings: function(element) {
2135 element = $(element);
2136 return Element.previousSiblings(element).reverse()
2137 .concat(Element.nextSiblings(element));
2140 match: function(element, selector) {
2141 element = $(element);
2142 if (Object.isString(selector))
2143 return Prototype.Selector.match(element, selector);
2144 return selector.match(element);
2147 up: function(element, expression, index) {
2148 element = $(element);
2149 if (arguments.length == 1) return $(element.parentNode);
2150 var ancestors = Element.ancestors(element);
2151 return Object.isNumber(expression) ? ancestors[expression] :
2152 Prototype.Selector.find(ancestors, expression, index);
2155 down: function(element, expression, index) {
2156 element = $(element);
2157 if (arguments.length == 1) return Element.firstDescendant(element);
2158 return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2159 Element.select(element, expression)[index || 0];
2162 previous: function(element, expression, index) {
2163 element = $(element);
2164 if (Object.isNumber(expression)) index = expression, expression = false;
2165 if (!Object.isNumber(index)) index = 0;
2168 return Prototype.Selector.find(element.previousSiblings(), expression, index);
2170 return element.recursivelyCollect("previousSibling", index + 1)[index];
2174 next: function(element, expression, index) {
2175 element = $(element);
2176 if (Object.isNumber(expression)) index = expression, expression = false;
2177 if (!Object.isNumber(index)) index = 0;
2180 return Prototype.Selector.find(element.nextSiblings(), expression, index);
2182 var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2183 return element.recursivelyCollect("nextSibling", index + 1)[index];
2188 select: function(element) {
2189 element = $(element);
2190 var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2191 return Prototype.Selector.select(expressions, element);
2194 adjacent: function(element) {
2195 element = $(element);
2196 var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2197 return Prototype.Selector.select(expressions, element.parentNode).without(element);
2200 identify: function(element) {
2201 element = $(element);
2202 var id = Element.readAttribute(element, 'id');
2204 do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2205 Element.writeAttribute(element, 'id', id);
2209 readAttribute: function(element, name) {
2210 element = $(element);
2211 if (Prototype.Browser.IE) {
2212 var t = Element._attributeTranslations.read;
2213 if (t.values[name]) return t.values[name](element, name);
2214 if (t.names[name]) name = t.names[name];
2215 if (name.include(':')) {
2216 return (!element.attributes || !element.attributes[name]) ? null :
2217 element.attributes[name].value;
2220 return element.getAttribute(name);
2223 writeAttribute: function(element, name, value) {
2224 element = $(element);
2225 var attributes = { }, t = Element._attributeTranslations.write;
2227 if (typeof name == 'object') attributes = name;
2228 else attributes[name] = Object.isUndefined(value) ? true : value;
2230 for (var attr in attributes) {
2231 name = t.names[attr] || attr;
2232 value = attributes[attr];
2233 if (t.values[attr]) name = t.values[attr](element, value);
2234 if (value === false || value === null)
2235 element.removeAttribute(name);
2236 else if (value === true)
2237 element.setAttribute(name, name);
2238 else element.setAttribute(name, value);
2243 getHeight: function(element) {
2244 return Element.getDimensions(element).height;
2247 getWidth: function(element) {
2248 return Element.getDimensions(element).width;
2251 classNames: function(element) {
2252 return new Element.ClassNames(element);
2255 hasClassName: function(element, className) {
2256 if (!(element = $(element))) return;
2257 var elementClassName = element.className;
2258 return (elementClassName.length > 0 && (elementClassName == className ||
2259 new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2262 addClassName: function(element, className) {
2263 if (!(element = $(element))) return;
2264 if (!Element.hasClassName(element, className))
2265 element.className += (element.className ? ' ' : '') + className;
2269 removeClassName: function(element, className) {
2270 if (!(element = $(element))) return;
2271 element.className = element.className.replace(
2272 new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2276 toggleClassName: function(element, className) {
2277 if (!(element = $(element))) return;
2278 return Element[Element.hasClassName(element, className) ?
2279 'removeClassName' : 'addClassName'](element, className);
2282 cleanWhitespace: function(element) {
2283 element = $(element);
2284 var node = element.firstChild;
2286 var nextNode = node.nextSibling;
2287 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2288 element.removeChild(node);
2294 empty: function(element) {
2295 return $(element).innerHTML.blank();
2298 descendantOf: function(element, ancestor) {
2299 element = $(element), ancestor = $(ancestor);
2301 if (element.compareDocumentPosition)
2302 return (element.compareDocumentPosition(ancestor) & 8) === 8;
2304 if (ancestor.contains)
2305 return ancestor.contains(element) && ancestor !== element;
2307 while (element = element.parentNode)
2308 if (element == ancestor) return true;
2313 scrollTo: function(element) {
2314 element = $(element);
2315 var pos = Element.cumulativeOffset(element);
2316 window.scrollTo(pos[0], pos[1]);
2320 getStyle: function(element, style) {
2321 element = $(element);
2322 style = style == 'float' ? 'cssFloat' : style.camelize();
2323 var value = element.style[style];
2324 if (!value || value == 'auto') {
2325 var css = document.defaultView.getComputedStyle(element, null);
2326 value = css ? css[style] : null;
2328 if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2329 return value == 'auto' ? null : value;
2332 getOpacity: function(element) {
2333 return $(element).getStyle('opacity');
2336 setStyle: function(element, styles) {
2337 element = $(element);
2338 var elementStyle = element.style, match;
2339 if (Object.isString(styles)) {
2340 element.style.cssText += ';' + styles;
2341 return styles.include('opacity') ?
2342 element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2344 for (var property in styles)
2345 if (property == 'opacity') element.setOpacity(styles[property]);
2347 elementStyle[(property == 'float' || property == 'cssFloat') ?
2348 (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2349 property] = styles[property];
2354 setOpacity: function(element, value) {
2355 element = $(element);
2356 element.style.opacity = (value == 1 || value === '') ? '' :
2357 (value < 0.00001) ? 0 : value;
2361 makePositioned: function(element) {
2362 element = $(element);
2363 var pos = Element.getStyle(element, 'position');
2364 if (pos == 'static' || !pos) {
2365 element._madePositioned = true;
2366 element.style.position = 'relative';
2367 if (Prototype.Browser.Opera) {
2368 element.style.top = 0;
2369 element.style.left = 0;
2375 undoPositioned: function(element) {
2376 element = $(element);
2377 if (element._madePositioned) {
2378 element._madePositioned = undefined;
2379 element.style.position =
2381 element.style.left =
2382 element.style.bottom =
2383 element.style.right = '';
2388 makeClipping: function(element) {
2389 element = $(element);
2390 if (element._overflow) return element;
2391 element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2392 if (element._overflow !== 'hidden')
2393 element.style.overflow = 'hidden';
2397 undoClipping: function(element) {
2398 element = $(element);
2399 if (!element._overflow) return element;
2400 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2401 element._overflow = null;
2405 cumulativeOffset: function(element) {
2406 var valueT = 0, valueL = 0;
2407 if (element.parentNode) {
2409 valueT += element.offsetTop || 0;
2410 valueL += element.offsetLeft || 0;
2411 element = element.offsetParent;
2414 return Element._returnOffset(valueL, valueT);
2417 positionedOffset: function(element) {
2418 var valueT = 0, valueL = 0;
2420 valueT += element.offsetTop || 0;
2421 valueL += element.offsetLeft || 0;
2422 element = element.offsetParent;
2424 if (element.tagName.toUpperCase() == 'BODY') break;
2425 var p = Element.getStyle(element, 'position');
2426 if (p !== 'static') break;
2429 return Element._returnOffset(valueL, valueT);
2432 absolutize: function(element) {
2433 element = $(element);
2434 if (Element.getStyle(element, 'position') == 'absolute') return element;
2436 var offsets = Element.positionedOffset(element),
2439 width = element.clientWidth,
2440 height = element.clientHeight;
2442 element._originalLeft = left - parseFloat(element.style.left || 0);
2443 element._originalTop = top - parseFloat(element.style.top || 0);
2444 element._originalWidth = element.style.width;
2445 element._originalHeight = element.style.height;
2447 element.style.position = 'absolute';
2448 element.style.top = top + 'px';
2449 element.style.left = left + 'px';
2450 element.style.width = width + 'px';
2451 element.style.height = height + 'px';
2455 relativize: function(element) {
2456 element = $(element);
2457 if (Element.getStyle(element, 'position') == 'relative') return element;
2459 element.style.position = 'relative';
2460 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0),
2461 left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2463 element.style.top = top + 'px';
2464 element.style.left = left + 'px';
2465 element.style.height = element._originalHeight;
2466 element.style.width = element._originalWidth;
2470 cumulativeScrollOffset: function(element) {
2471 var valueT = 0, valueL = 0;
2473 valueT += element.scrollTop || 0;
2474 valueL += element.scrollLeft || 0;
2475 element = element.parentNode;
2477 return Element._returnOffset(valueL, valueT);
2480 getOffsetParent: function(element) {
2481 if (element.offsetParent) return $(element.offsetParent);
2482 if (element == document.body) return $(element);
2484 while ((element = element.parentNode) && element != document.body)
2485 if (Element.getStyle(element, 'position') != 'static')
2488 return $(document.body);
2491 viewportOffset: function(forElement) {
2494 element = forElement;
2497 valueT += element.offsetTop || 0;
2498 valueL += element.offsetLeft || 0;
2500 if (element.offsetParent == document.body &&
2501 Element.getStyle(element, 'position') == 'absolute') break;
2503 } while (element = element.offsetParent);
2505 element = forElement;
2507 if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
2508 valueT -= element.scrollTop || 0;
2509 valueL -= element.scrollLeft || 0;
2511 } while (element = element.parentNode);
2513 return Element._returnOffset(valueL, valueT);
2516 clonePosition: function(element, source) {
2517 var options = Object.extend({
2524 }, arguments[2] || { });
2527 var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2529 element = $(element);
2531 if (Element.getStyle(element, 'position') == 'absolute') {
2532 parent = Element.getOffsetParent(element);
2533 delta = Element.viewportOffset(parent);
2536 if (parent == document.body) {
2537 delta[0] -= document.body.offsetLeft;
2538 delta[1] -= document.body.offsetTop;
2541 if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2542 if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2543 if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2544 if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2549 Object.extend(Element.Methods, {
2550 getElementsBySelector: Element.Methods.select,
2552 childElements: Element.Methods.immediateDescendants
2555 Element._attributeTranslations = {
2565 if (Prototype.Browser.Opera) {
2566 Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2567 function(proceed, element, style) {
2569 case 'left': case 'top': case 'right': case 'bottom':
2570 if (proceed(element, 'position') === 'static') return null;
2571 case 'height': case 'width':
2572 if (!Element.visible(element)) return null;
2574 var dim = parseInt(proceed(element, style), 10);
2576 if (dim !== element['offset' + style.capitalize()])
2580 if (style === 'height') {
2581 properties = ['border-top-width', 'padding-top',
2582 'padding-bottom', 'border-bottom-width'];
2585 properties = ['border-left-width', 'padding-left',
2586 'padding-right', 'border-right-width'];
2588 return properties.inject(dim, function(memo, property) {
2589 var val = proceed(element, property);
2590 return val === null ? memo : memo - parseInt(val, 10);
2592 default: return proceed(element, style);
2597 Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2598 function(proceed, element, attribute) {
2599 if (attribute === 'title') return element.title;
2600 return proceed(element, attribute);
2605 else if (Prototype.Browser.IE) {
2606 Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
2607 function(proceed, element) {
2608 element = $(element);
2609 if (!element.parentNode) return $(document.body);
2610 var position = element.getStyle('position');
2611 if (position !== 'static') return proceed(element);
2612 element.setStyle({ position: 'relative' });
2613 var value = proceed(element);
2614 element.setStyle({ position: position });
2619 $w('positionedOffset viewportOffset').each(function(method) {
2620 Element.Methods[method] = Element.Methods[method].wrap(
2621 function(proceed, element) {
2622 element = $(element);
2623 if (!element.parentNode) return Element._returnOffset(0, 0);
2624 var position = element.getStyle('position');
2625 if (position !== 'static') return proceed(element);
2626 var offsetParent = element.getOffsetParent();
2627 if (offsetParent && offsetParent.getStyle('position') === 'fixed')
2628 offsetParent.setStyle({ zoom: 1 });
2629 element.setStyle({ position: 'relative' });
2630 var value = proceed(element);
2631 element.setStyle({ position: position });
2637 Element.Methods.getStyle = function(element, style) {
2638 element = $(element);
2639 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2640 var value = element.style[style];
2641 if (!value && element.currentStyle) value = element.currentStyle[style];
2643 if (style == 'opacity') {
2644 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2645 if (value[1]) return parseFloat(value[1]) / 100;
2649 if (value == 'auto') {
2650 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2651 return element['offset' + style.capitalize()] + 'px';
2657 Element.Methods.setOpacity = function(element, value) {
2658 function stripAlpha(filter){
2659 return filter.replace(/alpha\([^\)]*\)/gi,'');
2661 element = $(element);
2662 var currentStyle = element.currentStyle;
2663 if ((currentStyle && !currentStyle.hasLayout) ||
2664 (!currentStyle && element.style.zoom == 'normal'))
2665 element.style.zoom = 1;
2667 var filter = element.getStyle('filter'), style = element.style;
2668 if (value == 1 || value === '') {
2669 (filter = stripAlpha(filter)) ?
2670 style.filter = filter : style.removeAttribute('filter');
2672 } else if (value < 0.00001) value = 0;
2673 style.filter = stripAlpha(filter) +
2674 'alpha(opacity=' + (value * 100) + ')';
2678 Element._attributeTranslations = (function(){
2680 var classProp = 'className',
2682 el = document.createElement('div');
2684 el.setAttribute(classProp, 'x');
2686 if (el.className !== 'x') {
2687 el.setAttribute('class', 'x');
2688 if (el.className === 'x') {
2689 classProp = 'class';
2694 el = document.createElement('label');
2695 el.setAttribute(forProp, 'x');
2696 if (el.htmlFor !== 'x') {
2697 el.setAttribute('htmlFor', 'x');
2698 if (el.htmlFor === 'x') {
2699 forProp = 'htmlFor';
2708 'className': classProp,
2713 _getAttr: function(element, attribute) {
2714 return element.getAttribute(attribute);
2716 _getAttr2: function(element, attribute) {
2717 return element.getAttribute(attribute, 2);
2719 _getAttrNode: function(element, attribute) {
2720 var node = element.getAttributeNode(attribute);
2721 return node ? node.value : "";
2723 _getEv: (function(){
2725 var el = document.createElement('div'), f;
2726 el.onclick = Prototype.emptyFunction;
2727 var value = el.getAttribute('onclick');
2729 if (String(value).indexOf('{') > -1) {
2730 f = function(element, attribute) {
2731 attribute = element.getAttribute(attribute);
2732 if (!attribute) return null;
2733 attribute = attribute.toString();
2734 attribute = attribute.split('{')[1];
2735 attribute = attribute.split('}')[0];
2736 return attribute.strip();
2739 else if (value === '') {
2740 f = function(element, attribute) {
2741 attribute = element.getAttribute(attribute);
2742 if (!attribute) return null;
2743 return attribute.strip();
2749 _flag: function(element, attribute) {
2750 return $(element).hasAttribute(attribute) ? attribute : null;
2752 style: function(element) {
2753 return element.style.cssText.toLowerCase();
2755 title: function(element) {
2756 return element.title;
2763 Element._attributeTranslations.write = {
2764 names: Object.extend({
2765 cellpadding: 'cellPadding',
2766 cellspacing: 'cellSpacing'
2767 }, Element._attributeTranslations.read.names),
2769 checked: function(element, value) {
2770 element.checked = !!value;
2773 style: function(element, value) {
2774 element.style.cssText = value ? value : '';
2779 Element._attributeTranslations.has = {};
2781 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2782 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2783 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2784 Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2792 action: v._getAttrNode,
2800 ondblclick: v._getEv,
2801 onmousedown: v._getEv,
2802 onmouseup: v._getEv,
2803 onmouseover: v._getEv,
2804 onmousemove: v._getEv,
2805 onmouseout: v._getEv,
2808 onkeypress: v._getEv,
2809 onkeydown: v._getEv,
2816 })(Element._attributeTranslations.read.values);
2818 if (Prototype.BrowserFeatures.ElementExtensions) {
2820 function _descendants(element) {
2821 var nodes = element.getElementsByTagName('*'), results = [];
2822 for (var i = 0, node; node = nodes[i]; i++)
2823 if (node.tagName !== "!") // Filter out comment nodes.
2828 Element.Methods.down = function(element, expression, index) {
2829 element = $(element);
2830 if (arguments.length == 1) return element.firstDescendant();
2831 return Object.isNumber(expression) ? _descendants(element)[expression] :
2832 Element.select(element, expression)[index || 0];
2839 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2840 Element.Methods.setOpacity = function(element, value) {
2841 element = $(element);
2842 element.style.opacity = (value == 1) ? 0.999999 :
2843 (value === '') ? '' : (value < 0.00001) ? 0 : value;
2848 else if (Prototype.Browser.WebKit) {
2849 Element.Methods.setOpacity = function(element, value) {
2850 element = $(element);
2851 element.style.opacity = (value == 1 || value === '') ? '' :
2852 (value < 0.00001) ? 0 : value;
2855 if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2856 element.width++; element.width--;
2858 var n = document.createTextNode(' ');
2859 element.appendChild(n);
2860 element.removeChild(n);
2866 Element.Methods.cumulativeOffset = function(element) {
2867 var valueT = 0, valueL = 0;
2869 valueT += element.offsetTop || 0;
2870 valueL += element.offsetLeft || 0;
2871 if (element.offsetParent == document.body)
2872 if (Element.getStyle(element, 'position') == 'absolute') break;
2874 element = element.offsetParent;
2877 return Element._returnOffset(valueL, valueT);
2881 if ('outerHTML' in document.documentElement) {
2882 Element.Methods.replace = function(element, content) {
2883 element = $(element);
2885 if (content && content.toElement) content = content.toElement();
2886 if (Object.isElement(content)) {
2887 element.parentNode.replaceChild(content, element);
2891 content = Object.toHTML(content);
2892 var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2894 if (Element._insertionTranslations.tags[tagName]) {
2895 var nextSibling = element.next(),
2896 fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2897 parent.removeChild(element);
2899 fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2901 fragments.each(function(node) { parent.appendChild(node) });
2903 else element.outerHTML = content.stripScripts();
2905 content.evalScripts.bind(content).defer();
2910 Element._returnOffset = function(l, t) {
2911 var result = [l, t];
2917 Element._getContentFromAnonymousElement = function(tagName, html) {
2918 var div = new Element('div'),
2919 t = Element._insertionTranslations.tags[tagName];
2921 div.innerHTML = t[0] + html + t[1];
2922 for (var i = t[2]; i--; ) {
2923 div = div.firstChild;
2927 div.innerHTML = html;
2929 return $A(div.childNodes);
2932 Element._insertionTranslations = {
2933 before: function(element, node) {
2934 element.parentNode.insertBefore(node, element);
2936 top: function(element, node) {
2937 element.insertBefore(node, element.firstChild);
2939 bottom: function(element, node) {
2940 element.appendChild(node);
2942 after: function(element, node) {
2943 element.parentNode.insertBefore(node, element.nextSibling);
2946 TABLE: ['<table>', '</table>', 1],
2947 TBODY: ['<table><tbody>', '</tbody></table>', 2],
2948 TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2949 TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2950 SELECT: ['<select>', '</select>', 1]
2955 var tags = Element._insertionTranslations.tags;
2956 Object.extend(tags, {
2963 Element.Methods.Simulated = {
2964 hasAttribute: function(element, attribute) {
2965 attribute = Element._attributeTranslations.has[attribute] || attribute;
2966 var node = $(element).getAttributeNode(attribute);
2967 return !!(node && node.specified);
2971 Element.Methods.ByTag = { };
2973 Object.extend(Element, Element.Methods);
2977 if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2978 window.HTMLElement = { };
2979 window.HTMLElement.prototype = div['__proto__'];
2980 Prototype.BrowserFeatures.ElementExtensions = true;
2985 })(document.createElement('div'));
2987 Element.extend = (function() {
2989 function checkDeficiency(tagName) {
2990 if (typeof window.Element != 'undefined') {
2991 var proto = window.Element.prototype;
2993 var id = '_' + (Math.random()+'').slice(2),
2994 el = document.createElement(tagName);
2996 var isBuggy = (el[id] !== 'x');
3005 function extendElementWith(element, methods) {
3006 for (var property in methods) {
3007 var value = methods[property];
3008 if (Object.isFunction(value) && !(property in element))
3009 element[property] = value.methodize();
3013 var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
3015 if (Prototype.BrowserFeatures.SpecificElementExtensions) {
3016 if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
3017 return function(element) {
3018 if (element && typeof element._extendedByPrototype == 'undefined') {
3019 var t = element.tagName;
3020 if (t && (/^(?:object|applet|embed)$/i.test(t))) {
3021 extendElementWith(element, Element.Methods);
3022 extendElementWith(element, Element.Methods.Simulated);
3023 extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
3032 var Methods = { }, ByTag = Element.Methods.ByTag;
3034 var extend = Object.extend(function(element) {
3035 if (!element || typeof element._extendedByPrototype != 'undefined' ||
3036 element.nodeType != 1 || element == window) return element;
3038 var methods = Object.clone(Methods),
3039 tagName = element.tagName.toUpperCase();
3041 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
3043 extendElementWith(element, methods);
3045 element._extendedByPrototype = Prototype.emptyFunction;
3049 refresh: function() {
3050 if (!Prototype.BrowserFeatures.ElementExtensions) {
3051 Object.extend(Methods, Element.Methods);
3052 Object.extend(Methods, Element.Methods.Simulated);
3061 if (document.documentElement.hasAttribute) {
3062 Element.hasAttribute = function(element, attribute) {
3063 return element.hasAttribute(attribute);
3067 Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
3070 Element.addMethods = function(methods) {
3071 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
3074 Object.extend(Form, Form.Methods);
3075 Object.extend(Form.Element, Form.Element.Methods);
3076 Object.extend(Element.Methods.ByTag, {
3077 "FORM": Object.clone(Form.Methods),
3078 "INPUT": Object.clone(Form.Element.Methods),
3079 "SELECT": Object.clone(Form.Element.Methods),
3080 "TEXTAREA": Object.clone(Form.Element.Methods)
3084 if (arguments.length == 2) {
3085 var tagName = methods;
3086 methods = arguments[1];
3089 if (!tagName) Object.extend(Element.Methods, methods || { });
3091 if (Object.isArray(tagName)) tagName.each(extend);
3092 else extend(tagName);
3095 function extend(tagName) {
3096 tagName = tagName.toUpperCase();
3097 if (!Element.Methods.ByTag[tagName])
3098 Element.Methods.ByTag[tagName] = { };
3099 Object.extend(Element.Methods.ByTag[tagName], methods);
3102 function copy(methods, destination, onlyIfAbsent) {
3103 onlyIfAbsent = onlyIfAbsent || false;
3104 for (var property in methods) {
3105 var value = methods[property];
3106 if (!Object.isFunction(value)) continue;
3107 if (!onlyIfAbsent || !(property in destination))
3108 destination[property] = value.methodize();
3112 function findDOMClass(tagName) {
3115 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3116 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3117 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3118 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3119 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3120 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3121 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3122 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3123 "FrameSet", "IFRAME": "IFrame"
3125 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3126 if (window[klass]) return window[klass];
3127 klass = 'HTML' + tagName + 'Element';
3128 if (window[klass]) return window[klass];
3129 klass = 'HTML' + tagName.capitalize() + 'Element';
3130 if (window[klass]) return window[klass];
3132 var element = document.createElement(tagName),
3133 proto = element['__proto__'] || element.constructor.prototype;
3139 var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3142 if (F.ElementExtensions) {
3143 copy(Element.Methods, elementPrototype);
3144 copy(Element.Methods.Simulated, elementPrototype, true);
3147 if (F.SpecificElementExtensions) {
3148 for (var tag in Element.Methods.ByTag) {
3149 var klass = findDOMClass(tag);
3150 if (Object.isUndefined(klass)) continue;
3151 copy(T[tag], klass.prototype);
3155 Object.extend(Element, Element.Methods);
3156 delete Element.ByTag;
3158 if (Element.extend.refresh) Element.extend.refresh();
3159 Element.cache = { };
3163 document.viewport = {
3165 getDimensions: function() {
3166 return { width: this.getWidth(), height: this.getHeight() };
3169 getScrollOffsets: function() {
3170 return Element._returnOffset(
3171 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3172 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
3176 (function(viewport) {
3177 var B = Prototype.Browser, doc = document, element, property = {};
3179 function getRootElement() {
3180 if (B.WebKit && !doc.evaluate)
3183 if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3184 return document.body;
3186 return document.documentElement;
3189 function define(D) {
3190 if (!element) element = getRootElement();
3192 property[D] = 'client' + D;
3194 viewport['get' + D] = function() { return element[property[D]] };
3195 return viewport['get' + D]();
3198 viewport.getWidth = define.curry('Width');
3200 viewport.getHeight = define.curry('Height');
3201 })(document.viewport);
3208 Element.addMethods({
3209 getStorage: function(element) {
3210 if (!(element = $(element))) return;
3213 if (element === window) {
3216 if (typeof element._prototypeUID === "undefined")
3217 element._prototypeUID = Element.Storage.UID++;
3218 uid = element._prototypeUID;
3221 if (!Element.Storage[uid])
3222 Element.Storage[uid] = $H();
3224 return Element.Storage[uid];
3227 store: function(element, key, value) {
3228 if (!(element = $(element))) return;
3230 if (arguments.length === 2) {
3231 Element.getStorage(element).update(key);
3233 Element.getStorage(element).set(key, value);
3239 retrieve: function(element, key, defaultValue) {
3240 if (!(element = $(element))) return;
3241 var hash = Element.getStorage(element), value = hash.get(key);
3243 if (Object.isUndefined(value)) {
3244 hash.set(key, defaultValue);
3245 value = defaultValue;
3251 clone: function(element, deep) {
3252 if (!(element = $(element))) return;
3253 var clone = element.cloneNode(deep);
3254 clone._prototypeUID = void 0;
3256 var descendants = Element.select(clone, '*'),
3257 i = descendants.length;
3259 descendants[i]._prototypeUID = void 0;
3262 return Element.extend(clone);
3265 purge: function(element) {
3266 if (!(element = $(element))) return;
3267 purgeElement(element);
3269 var descendants = element.getElementsByTagName('*'),
3270 i = descendants.length;
3272 while (i--) purgeElement(descendants[i]);
3280 function toDecimal(pctString) {
3281 var match = pctString.match(/^(\d+)%?$/i);
3282 if (!match) return null;
3283 return (Number(match[1]) / 100);
3286 function getPixelValue(value, property) {
3287 if (Object.isElement(value)) {
3289 value = element.getStyle(property);
3291 if (value === null) {
3295 if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3296 return window.parseFloat(value);
3299 if (/\d/.test(value) && element.runtimeStyle) {
3300 var style = element.style.left, rStyle = element.runtimeStyle.left;
3301 element.runtimeStyle.left = element.currentStyle.left;
3302 element.style.left = value || 0;
3303 value = element.style.pixelLeft;
3304 element.style.left = style;
3305 element.runtimeStyle.left = rStyle;
3310 if (value.include('%')) {
3311 var decimal = toDecimal(value);
3313 if (property.include('left') || property.include('right') ||
3314 property.include('width')) {
3315 whole = $(element.parentNode).measure('width');
3316 } else if (property.include('top') || property.include('bottom') ||
3317 property.include('height')) {
3318 whole = $(element.parentNode).measure('height');
3321 return whole * decimal;
3327 function toCSSPixels(number) {
3328 if (Object.isString(number) && number.endsWith('px')) {
3331 return number + 'px';
3334 function isDisplayed(element) {
3335 var originalElement = element;
3336 while (element && element.parentNode) {
3337 var display = element.getStyle('display');
3338 if (display === 'none') {
3341 element = $(element.parentNode);
3346 var hasLayout = Prototype.K;
3347 if ('currentStyle' in document.documentElement) {
3348 hasLayout = function(element) {
3349 if (!element.currentStyle.hasLayout) {
3350 element.style.zoom = 1;
3356 function cssNameFor(key) {
3357 if (key.include('border')) key = key + '-width';
3358 return key.camelize();
3361 Element.Layout = Class.create(Hash, {
3362 initialize: function($super, element, preCompute) {
3364 this.element = $(element);
3366 Element.Layout.PROPERTIES.each( function(property) {
3367 this._set(property, null);
3371 this._preComputing = true;
3373 Element.Layout.PROPERTIES.each( this._compute, this );
3375 this._preComputing = false;
3379 _set: function(property, value) {
3380 return Hash.prototype.set.call(this, property, value);
3383 set: function(property, value) {
3384 throw "Properties of Element.Layout are read-only.";
3387 get: function($super, property) {
3388 var value = $super(property);
3389 return value === null ? this._compute(property) : value;
3392 _begin: function() {
3393 if (this._prepared) return;
3395 var element = this.element;
3396 if (isDisplayed(element)) {
3397 this._prepared = true;
3401 var originalStyles = {
3402 position: element.style.position || '',
3403 width: element.style.width || '',
3404 visibility: element.style.visibility || '',
3405 display: element.style.display || ''
3408 element.store('prototype_original_styles', originalStyles);
3410 var position = element.getStyle('position'),
3411 width = element.getStyle('width');
3414 position: 'absolute',
3415 visibility: 'hidden',
3419 var positionedWidth = element.getStyle('width');
3422 if (width && (positionedWidth === width)) {
3423 newWidth = getPixelValue(width);
3424 } else if (width && (position === 'absolute' || position === 'fixed')) {
3425 newWidth = getPixelValue(width);
3427 var parent = element.parentNode, pLayout = $(parent).getLayout();
3429 newWidth = pLayout.get('width') -
3430 this.get('margin-left') -
3431 this.get('border-left') -
3432 this.get('padding-left') -
3433 this.get('padding-right') -
3434 this.get('border-right') -
3435 this.get('margin-right');
3438 element.setStyle({ width: newWidth + 'px' });
3440 this._prepared = true;
3444 var element = this.element;
3445 var originalStyles = element.retrieve('prototype_original_styles');
3446 element.store('prototype_original_styles', null);
3447 element.setStyle(originalStyles);
3448 this._prepared = false;
3451 _compute: function(property) {
3452 var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3453 if (!(property in COMPUTATIONS)) {
3454 throw "Property not found.";
3456 return this._set(property, COMPUTATIONS[property].call(this, this.element));
3459 toObject: function() {
3460 var args = $A(arguments);
3461 var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3462 args.join(' ').split(' ');
3464 keys.each( function(key) {
3465 if (!Element.Layout.PROPERTIES.include(key)) return;
3466 var value = this.get(key);
3467 if (value != null) obj[key] = value;
3472 toHash: function() {
3473 var obj = this.toObject.apply(this, arguments);
3474 return new Hash(obj);
3478 var args = $A(arguments);
3479 var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3480 args.join(' ').split(' ');
3483 keys.each( function(key) {
3484 if (!Element.Layout.PROPERTIES.include(key)) return;
3485 if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3487 var value = this.get(key);
3488 if (value != null) css[cssNameFor(key)] = value + 'px';
3493 inspect: function() {
3494 return "#<Element.Layout>";
3498 Object.extend(Element.Layout, {
3499 PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3501 COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3504 'height': function(element) {
3505 if (!this._preComputing) this._begin();
3507 var bHeight = this.get('border-box-height');
3508 if (bHeight <= 0) return 0;
3510 var bTop = this.get('border-top'),
3511 bBottom = this.get('border-bottom');
3513 var pTop = this.get('padding-top'),
3514 pBottom = this.get('padding-bottom');
3516 if (!this._preComputing) this._end();
3518 return bHeight - bTop - bBottom - pTop - pBottom;
3521 'width': function(element) {
3522 if (!this._preComputing) this._begin();
3524 var bWidth = this.get('border-box-width');
3525 if (bWidth <= 0) return 0;
3527 var bLeft = this.get('border-left'),
3528 bRight = this.get('border-right');
3530 var pLeft = this.get('padding-left'),
3531 pRight = this.get('padding-right');
3533 if (!this._preComputing) this._end();
3535 return bWidth - bLeft - bRight - pLeft - pRight;
3538 'padding-box-height': function(element) {
3539 var height = this.get('height'),
3540 pTop = this.get('padding-top'),
3541 pBottom = this.get('padding-bottom');
3543 return height + pTop + pBottom;
3546 'padding-box-width': function(element) {
3547 var width = this.get('width'),
3548 pLeft = this.get('padding-left'),
3549 pRight = this.get('padding-right');
3551 return width + pLeft + pRight;
3554 'border-box-height': function(element) {
3555 return element.offsetHeight;
3558 'border-box-width': function(element) {
3559 return element.offsetWidth;
3562 'margin-box-height': function(element) {
3563 var bHeight = this.get('border-box-height'),
3564 mTop = this.get('margin-top'),
3565 mBottom = this.get('margin-bottom');
3567 if (bHeight <= 0) return 0;
3569 return bHeight + mTop + mBottom;
3572 'margin-box-width': function(element) {
3573 var bWidth = this.get('border-box-width'),
3574 mLeft = this.get('margin-left'),
3575 mRight = this.get('margin-right');
3577 if (bWidth <= 0) return 0;
3579 return bWidth + mLeft + mRight;
3582 'top': function(element) {
3583 var offset = element.positionedOffset();
3587 'bottom': function(element) {
3588 var offset = element.positionedOffset(),
3589 parent = element.getOffsetParent(),
3590 pHeight = parent.measure('height');
3592 var mHeight = this.get('border-box-height');
3594 return pHeight - mHeight - offset.top;
3597 'left': function(element) {
3598 var offset = element.positionedOffset();
3602 'right': function(element) {
3603 var offset = element.positionedOffset(),
3604 parent = element.getOffsetParent(),
3605 pWidth = parent.measure('width');
3607 var mWidth = this.get('border-box-width');
3609 return pWidth - mWidth - offset.left;
3612 'padding-top': function(element) {
3613 return getPixelValue(element, 'paddingTop');
3616 'padding-bottom': function(element) {
3617 return getPixelValue(element, 'paddingBottom');
3620 'padding-left': function(element) {
3621 return getPixelValue(element, 'paddingLeft');
3624 'padding-right': function(element) {
3625 return getPixelValue(element, 'paddingRight');
3628 'border-top': function(element) {
3629 return Object.isNumber(element.clientTop) ? element.clientTop :
3630 getPixelValue(element, 'borderTopWidth');
3633 'border-bottom': function(element) {
3634 return Object.isNumber(element.clientBottom) ? element.clientBottom :
3635 getPixelValue(element, 'borderBottomWidth');
3638 'border-left': function(element) {
3639 return Object.isNumber(element.clientLeft) ? element.clientLeft :
3640 getPixelValue(element, 'borderLeftWidth');
3643 'border-right': function(element) {
3644 return Object.isNumber(element.clientRight) ? element.clientRight :
3645 getPixelValue(element, 'borderRightWidth');
3648 'margin-top': function(element) {
3649 return getPixelValue(element, 'marginTop');
3652 'margin-bottom': function(element) {
3653 return getPixelValue(element, 'marginBottom');
3656 'margin-left': function(element) {
3657 return getPixelValue(element, 'marginLeft');
3660 'margin-right': function(element) {
3661 return getPixelValue(element, 'marginRight');
3666 if ('getBoundingClientRect' in document.documentElement) {
3667 Object.extend(Element.Layout.COMPUTATIONS, {
3668 'right': function(element) {
3669 var parent = hasLayout(element.getOffsetParent());
3670 var rect = element.getBoundingClientRect(),
3671 pRect = parent.getBoundingClientRect();
3673 return (pRect.right - rect.right).round();
3676 'bottom': function(element) {
3677 var parent = hasLayout(element.getOffsetParent());
3678 var rect = element.getBoundingClientRect(),
3679 pRect = parent.getBoundingClientRect();
3681 return (pRect.bottom - rect.bottom).round();
3686 Element.Offset = Class.create({
3687 initialize: function(left, top) {
3688 this.left = left.round();
3689 this.top = top.round();
3691 this[0] = this.left;
3695 relativeTo: function(offset) {
3696 return new Element.Offset(
3697 this.left - offset.left,
3698 this.top - offset.top
3702 inspect: function() {
3703 return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3706 toString: function() {
3707 return "[#{left}, #{top}]".interpolate(this);
3710 toArray: function() {
3711 return [this.left, this.top];
3715 function getLayout(element, preCompute) {
3716 return new Element.Layout(element, preCompute);
3719 function measure(element, property) {
3720 return $(element).getLayout().get(property);
3723 function getDimensions(element) {
3724 var layout = $(element).getLayout();
3726 width: layout.get('width'),
3727 height: layout.get('height')
3731 function getOffsetParent(element) {
3732 if (isDetached(element)) return $(document.body);
3734 var isInline = (Element.getStyle(element, 'display') === 'inline');
3735 if (!isInline && element.offsetParent) return $(element.offsetParent);
3736 if (element === document.body) return $(element);
3738 while ((element = element.parentNode) && element !== document.body) {
3739 if (Element.getStyle(element, 'position') !== 'static') {
3740 return (element.nodeName === 'HTML') ? $(document.body) : $(element);
3744 return $(document.body);
3748 function cumulativeOffset(element) {
3749 var valueT = 0, valueL = 0;
3751 valueT += element.offsetTop || 0;
3752 valueL += element.offsetLeft || 0;
3753 element = element.offsetParent;
3755 return new Element.Offset(valueL, valueT);
3758 function positionedOffset(element) {
3759 var layout = element.getLayout();
3761 var valueT = 0, valueL = 0;
3763 valueT += element.offsetTop || 0;
3764 valueL += element.offsetLeft || 0;
3765 element = element.offsetParent;
3767 if (isBody(element)) break;
3768 var p = Element.getStyle(element, 'position');
3769 if (p !== 'static') break;
3773 valueL -= layout.get('margin-top');
3774 valueT -= layout.get('margin-left');
3776 return new Element.Offset(valueL, valueT);
3779 function cumulativeScrollOffset(element) {
3780 var valueT = 0, valueL = 0;
3782 valueT += element.scrollTop || 0;
3783 valueL += element.scrollLeft || 0;
3784 element = element.parentNode;
3786 return new Element.Offset(valueL, valueT);
3789 function viewportOffset(forElement) {
3790 var valueT = 0, valueL = 0, docBody = document.body;
3792 var element = forElement;
3794 valueT += element.offsetTop || 0;
3795 valueL += element.offsetLeft || 0;
3796 if (element.offsetParent == docBody &&
3797 Element.getStyle(element, 'position') == 'absolute') break;
3798 } while (element = element.offsetParent);
3800 element = forElement;
3802 if (element != docBody) {
3803 valueT -= element.scrollTop || 0;
3804 valueL -= element.scrollLeft || 0;
3806 } while (element = element.parentNode);
3807 return new Element.Offset(valueL, valueT);
3810 function absolutize(element) {
3811 element = $(element);
3813 if (Element.getStyle(element, 'position') === 'absolute') {
3817 var offsetParent = getOffsetParent(element);
3818 var eOffset = element.viewportOffset(),
3819 pOffset = offsetParent.viewportOffset();
3821 var offset = eOffset.relativeTo(pOffset);
3822 var layout = element.getLayout();
3824 element.store('prototype_absolutize_original_styles', {
3825 left: element.getStyle('left'),
3826 top: element.getStyle('top'),
3827 width: element.getStyle('width'),
3828 height: element.getStyle('height')
3832 position: 'absolute',
3833 top: offset.top + 'px',
3834 left: offset.left + 'px',
3835 width: layout.get('width') + 'px',
3836 height: layout.get('height') + 'px'
3842 function relativize(element) {
3843 element = $(element);
3844 if (Element.getStyle(element, 'position') === 'relative') {
3848 var originalStyles =
3849 element.retrieve('prototype_absolutize_original_styles');
3851 if (originalStyles) element.setStyle(originalStyles);
3855 Element.addMethods({
3856 getLayout: getLayout,
3858 getDimensions: getDimensions,
3859 getOffsetParent: getOffsetParent,
3860 cumulativeOffset: cumulativeOffset,
3861 positionedOffset: positionedOffset,
3862 cumulativeScrollOffset: cumulativeScrollOffset,
3863 viewportOffset: viewportOffset,
3864 absolutize: absolutize,
3865 relativize: relativize
3868 function isBody(element) {
3869 return element.nodeName.toUpperCase() === 'BODY';
3872 function isDetached(element) {
3873 return element !== document.body &&
3874 !Element.descendantOf(element, document.body);
3877 if ('getBoundingClientRect' in document.documentElement) {
3878 Element.addMethods({
3879 viewportOffset: function(element) {
3880 element = $(element);
3881 if (isDetached(element)) return new Element.Offset(0, 0);
3883 var rect = element.getBoundingClientRect(),
3884 docEl = document.documentElement;
3885 return new Element.Offset(rect.left - docEl.clientLeft,
3886 rect.top - docEl.clientTop);
3889 positionedOffset: function(element) {
3890 element = $(element);
3891 var parent = element.getOffsetParent();
3892 if (isDetached(element)) return new Element.Offset(0, 0);
3894 if (element.offsetParent &&
3895 element.offsetParent.nodeName.toUpperCase() === 'HTML') {
3896 return positionedOffset(element);
3899 var eOffset = element.viewportOffset(),
3900 pOffset = isBody(parent) ? viewportOffset(parent) :
3901 parent.viewportOffset();
3902 var retOffset = eOffset.relativeTo(pOffset);
3904 var layout = element.getLayout();
3905 var top = retOffset.top - layout.get('margin-top');
3906 var left = retOffset.left - layout.get('margin-left');
3908 return new Element.Offset(left, top);
3913 window.$$ = function() {
3914 var expression = $A(arguments).join(', ');
3915 return Prototype.Selector.select(expression, document);
3918 Prototype.Selector = (function() {
3921 throw new Error('Method "Prototype.Selector.select" must be defined.');
3925 throw new Error('Method "Prototype.Selector.match" must be defined.');
3928 function find(elements, expression, index) {
3930 var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
3932 for (i = 0; i < length; i++) {
3933 if (match(elements[i], expression) && index == matchIndex++) {
3934 return Element.extend(elements[i]);
3939 function extendElements(elements) {
3940 for (var i = 0, length = elements.length; i < length; i++) {
3941 Element.extend(elements[i]);
3947 var K = Prototype.K;
3953 extendElements: (Element.extend === K) ? K : extendElements,
3954 extendElement: Element.extend
3957 Prototype._original_property = window.Sizzle;
3959 * Sizzle CSS Selector Engine - v1.0
3960 * Copyright 2009, The Dojo Foundation
3961 * Released under the MIT, BSD, and GPL Licenses.
3962 * More information: http://sizzlejs.com/
3966 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3968 toString = Object.prototype.toString,
3969 hasDuplicate = false,
3970 baseHasDuplicate = true;
3972 [0, 0].sort(function(){
3973 baseHasDuplicate = false;
3977 var Sizzle = function(selector, context, results, seed) {
3978 results = results || [];
3979 var origContext = context = context || document;
3981 if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3985 if ( !selector || typeof selector !== "string" ) {
3989 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
3992 while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
4003 if ( parts.length > 1 && origPOS.exec( selector ) ) {
4004 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4005 set = posProcess( parts[0] + parts[1], context );
4007 set = Expr.relative[ parts[0] ] ?
4009 Sizzle( parts.shift(), context );
4011 while ( parts.length ) {
4012 selector = parts.shift();
4014 if ( Expr.relative[ selector ] )
4015 selector += parts.shift();
4017 set = posProcess( selector, set );
4021 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4022 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4023 var ret = Sizzle.find( parts.shift(), context, contextXML );
4024 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4029 { expr: parts.pop(), set: makeArray(seed) } :
4030 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4031 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4033 if ( parts.length > 0 ) {
4034 checkSet = makeArray(set);
4039 while ( parts.length ) {
4040 var cur = parts.pop(), pop = cur;
4042 if ( !Expr.relative[ cur ] ) {
4048 if ( pop == null ) {
4052 Expr.relative[ cur ]( checkSet, pop, contextXML );
4055 checkSet = parts = [];
4064 throw "Syntax error, unrecognized expression: " + (cur || selector);
4067 if ( toString.call(checkSet) === "[object Array]" ) {
4069 results.push.apply( results, checkSet );
4070 } else if ( context && context.nodeType === 1 ) {
4071 for ( var i = 0; checkSet[i] != null; i++ ) {
4072 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
4073 results.push( set[i] );
4077 for ( var i = 0; checkSet[i] != null; i++ ) {
4078 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4079 results.push( set[i] );
4084 makeArray( checkSet, results );
4088 Sizzle( extra, origContext, results, seed );
4089 Sizzle.uniqueSort( results );
4095 Sizzle.uniqueSort = function(results){
4097 hasDuplicate = baseHasDuplicate;
4098 results.sort(sortOrder);
4100 if ( hasDuplicate ) {
4101 for ( var i = 1; i < results.length; i++ ) {
4102 if ( results[i] === results[i-1] ) {
4103 results.splice(i--, 1);
4112 Sizzle.matches = function(expr, set){
4113 return Sizzle(expr, null, null, set);
4116 Sizzle.find = function(expr, context, isXML){
4123 for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4124 var type = Expr.order[i], match;
4126 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4127 var left = match[1];
4130 if ( left.substr( left.length - 1 ) !== "\\" ) {
4131 match[1] = (match[1] || "").replace(/\\/g, "");
4132 set = Expr.find[ type ]( match, context, isXML );
4133 if ( set != null ) {
4134 expr = expr.replace( Expr.match[ type ], "" );
4142 set = context.getElementsByTagName("*");
4145 return {set: set, expr: expr};
4148 Sizzle.filter = function(expr, set, inplace, not){
4149 var old = expr, result = [], curLoop = set, match, anyFound,
4150 isXMLFilter = set && set[0] && isXML(set[0]);
4152 while ( expr && set.length ) {
4153 for ( var type in Expr.filter ) {
4154 if ( (match = Expr.match[ type ].exec( expr )) != null ) {
4155 var filter = Expr.filter[ type ], found, item;
4158 if ( curLoop == result ) {
4162 if ( Expr.preFilter[ type ] ) {
4163 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4166 anyFound = found = true;
4167 } else if ( match === true ) {
4173 for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4175 found = filter( item, match, i, curLoop );
4176 var pass = not ^ !!found;
4178 if ( inplace && found != null ) {
4184 } else if ( pass ) {
4185 result.push( item );
4192 if ( found !== undefined ) {
4197 expr = expr.replace( Expr.match[ type ], "" );
4208 if ( expr == old ) {
4209 if ( anyFound == null ) {
4210 throw "Syntax error, unrecognized expression: " + expr;
4222 var Expr = Sizzle.selectors = {
4223 order: [ "ID", "NAME", "TAG" ],
4225 ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4226 CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4227 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
4228 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4229 TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
4230 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
4231 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
4232 PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
4236 "class": "className",
4240 href: function(elem){
4241 return elem.getAttribute("href");
4245 "+": function(checkSet, part, isXML){
4246 var isPartStr = typeof part === "string",
4247 isTag = isPartStr && !/\W/.test(part),
4248 isPartStrNotTag = isPartStr && !isTag;
4250 if ( isTag && !isXML ) {
4251 part = part.toUpperCase();
4254 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4255 if ( (elem = checkSet[i]) ) {
4256 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4258 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
4264 if ( isPartStrNotTag ) {
4265 Sizzle.filter( part, checkSet, true );
4268 ">": function(checkSet, part, isXML){
4269 var isPartStr = typeof part === "string";
4271 if ( isPartStr && !/\W/.test(part) ) {
4272 part = isXML ? part : part.toUpperCase();
4274 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4275 var elem = checkSet[i];
4277 var parent = elem.parentNode;
4278 checkSet[i] = parent.nodeName === part ? parent : false;
4282 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4283 var elem = checkSet[i];
4285 checkSet[i] = isPartStr ?
4287 elem.parentNode === part;
4292 Sizzle.filter( part, checkSet, true );
4296 "": function(checkSet, part, isXML){
4297 var doneName = done++, checkFn = dirCheck;
4299 if ( !/\W/.test(part) ) {
4300 var nodeCheck = part = isXML ? part : part.toUpperCase();
4301 checkFn = dirNodeCheck;
4304 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4306 "~": function(checkSet, part, isXML){
4307 var doneName = done++, checkFn = dirCheck;
4309 if ( typeof part === "string" && !/\W/.test(part) ) {
4310 var nodeCheck = part = isXML ? part : part.toUpperCase();
4311 checkFn = dirNodeCheck;
4314 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4318 ID: function(match, context, isXML){
4319 if ( typeof context.getElementById !== "undefined" && !isXML ) {
4320 var m = context.getElementById(match[1]);
4321 return m ? [m] : [];
4324 NAME: function(match, context, isXML){
4325 if ( typeof context.getElementsByName !== "undefined" ) {
4326 var ret = [], results = context.getElementsByName(match[1]);
4328 for ( var i = 0, l = results.length; i < l; i++ ) {
4329 if ( results[i].getAttribute("name") === match[1] ) {
4330 ret.push( results[i] );
4334 return ret.length === 0 ? null : ret;
4337 TAG: function(match, context){
4338 return context.getElementsByTagName(match[1]);
4342 CLASS: function(match, curLoop, inplace, result, not, isXML){
4343 match = " " + match[1].replace(/\\/g, "") + " ";
4349 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4351 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
4353 result.push( elem );
4354 } else if ( inplace ) {
4362 ID: function(match){
4363 return match[1].replace(/\\/g, "");
4365 TAG: function(match, curLoop){
4366 for ( var i = 0; curLoop[i] === false; i++ ){}
4367 return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
4369 CHILD: function(match){
4370 if ( match[1] == "nth" ) {
4371 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4372 match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
4373 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4375 match[2] = (test[1] + (test[2] || 1)) - 0;
4376 match[3] = test[3] - 0;
4383 ATTR: function(match, curLoop, inplace, result, not, isXML){
4384 var name = match[1].replace(/\\/g, "");
4386 if ( !isXML && Expr.attrMap[name] ) {
4387 match[1] = Expr.attrMap[name];
4390 if ( match[2] === "~=" ) {
4391 match[4] = " " + match[4] + " ";
4396 PSEUDO: function(match, curLoop, inplace, result, not){
4397 if ( match[1] === "not" ) {
4398 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4399 match[3] = Sizzle(match[3], null, null, curLoop);
4401 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4403 result.push.apply( result, ret );
4407 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4413 POS: function(match){
4414 match.unshift( true );
4419 enabled: function(elem){
4420 return elem.disabled === false && elem.type !== "hidden";
4422 disabled: function(elem){
4423 return elem.disabled === true;
4425 checked: function(elem){
4426 return elem.checked === true;
4428 selected: function(elem){
4429 elem.parentNode.selectedIndex;
4430 return elem.selected === true;
4432 parent: function(elem){
4433 return !!elem.firstChild;
4435 empty: function(elem){
4436 return !elem.firstChild;
4438 has: function(elem, i, match){
4439 return !!Sizzle( match[3], elem ).length;
4441 header: function(elem){
4442 return /h\d/i.test( elem.nodeName );
4444 text: function(elem){
4445 return "text" === elem.type;
4447 radio: function(elem){
4448 return "radio" === elem.type;
4450 checkbox: function(elem){
4451 return "checkbox" === elem.type;
4453 file: function(elem){
4454 return "file" === elem.type;
4456 password: function(elem){
4457 return "password" === elem.type;
4459 submit: function(elem){
4460 return "submit" === elem.type;
4462 image: function(elem){
4463 return "image" === elem.type;
4465 reset: function(elem){
4466 return "reset" === elem.type;
4468 button: function(elem){
4469 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
4471 input: function(elem){
4472 return /input|select|textarea|button/i.test(elem.nodeName);
4476 first: function(elem, i){
4479 last: function(elem, i, match, array){
4480 return i === array.length - 1;
4482 even: function(elem, i){
4485 odd: function(elem, i){
4488 lt: function(elem, i, match){
4489 return i < match[3] - 0;
4491 gt: function(elem, i, match){
4492 return i > match[3] - 0;
4494 nth: function(elem, i, match){
4495 return match[3] - 0 == i;
4497 eq: function(elem, i, match){
4498 return match[3] - 0 == i;
4502 PSEUDO: function(elem, match, i, array){
4503 var name = match[1], filter = Expr.filters[ name ];
4506 return filter( elem, i, match, array );
4507 } else if ( name === "contains" ) {
4508 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
4509 } else if ( name === "not" ) {
4512 for ( var i = 0, l = not.length; i < l; i++ ) {
4513 if ( not[i] === elem ) {
4521 CHILD: function(elem, match){
4522 var type = match[1], node = elem;
4526 while ( (node = node.previousSibling) ) {
4527 if ( node.nodeType === 1 ) return false;
4529 if ( type == 'first') return true;
4532 while ( (node = node.nextSibling) ) {
4533 if ( node.nodeType === 1 ) return false;
4537 var first = match[2], last = match[3];
4539 if ( first == 1 && last == 0 ) {
4543 var doneName = match[0],
4544 parent = elem.parentNode;
4546 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4548 for ( node = parent.firstChild; node; node = node.nextSibling ) {
4549 if ( node.nodeType === 1 ) {
4550 node.nodeIndex = ++count;
4553 parent.sizcache = doneName;
4556 var diff = elem.nodeIndex - last;
4560 return ( diff % first == 0 && diff / first >= 0 );
4564 ID: function(elem, match){
4565 return elem.nodeType === 1 && elem.getAttribute("id") === match;
4567 TAG: function(elem, match){
4568 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4570 CLASS: function(elem, match){
4571 return (" " + (elem.className || elem.getAttribute("class")) + " ")
4572 .indexOf( match ) > -1;
4574 ATTR: function(elem, match){
4575 var name = match[1],
4576 result = Expr.attrHandle[ name ] ?
4577 Expr.attrHandle[ name ]( elem ) :
4578 elem[ name ] != null ?
4580 elem.getAttribute( name ),
4581 value = result + "",
4585 return result == null ?
4590 value.indexOf(check) >= 0 :
4592 (" " + value + " ").indexOf(check) >= 0 :
4594 value && result !== false :
4598 value.indexOf(check) === 0 :
4600 value.substr(value.length - check.length) === check :
4602 value === check || value.substr(0, check.length + 1) === check + "-" :
4605 POS: function(elem, match, i, array){
4606 var name = match[2], filter = Expr.setFilters[ name ];
4609 return filter( elem, i, match, array );
4615 var origPOS = Expr.match.POS;
4617 for ( var type in Expr.match ) {
4618 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4619 Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4622 var makeArray = function(array, results) {
4623 array = Array.prototype.slice.call( array, 0 );
4626 results.push.apply( results, array );
4634 Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4637 makeArray = function(array, results) {
4638 var ret = results || [];
4640 if ( toString.call(array) === "[object Array]" ) {
4641 Array.prototype.push.apply( ret, array );
4643 if ( typeof array.length === "number" ) {
4644 for ( var i = 0, l = array.length; i < l; i++ ) {
4645 ret.push( array[i] );
4648 for ( var i = 0; array[i]; i++ ) {
4649 ret.push( array[i] );
4660 if ( document.documentElement.compareDocumentPosition ) {
4661 sortOrder = function( a, b ) {
4662 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4664 hasDuplicate = true;
4669 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4671 hasDuplicate = true;
4675 } else if ( "sourceIndex" in document.documentElement ) {
4676 sortOrder = function( a, b ) {
4677 if ( !a.sourceIndex || !b.sourceIndex ) {
4679 hasDuplicate = true;
4684 var ret = a.sourceIndex - b.sourceIndex;
4686 hasDuplicate = true;
4690 } else if ( document.createRange ) {
4691 sortOrder = function( a, b ) {
4692 if ( !a.ownerDocument || !b.ownerDocument ) {
4694 hasDuplicate = true;
4699 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4700 aRange.setStart(a, 0);
4701 aRange.setEnd(a, 0);
4702 bRange.setStart(b, 0);
4703 bRange.setEnd(b, 0);
4704 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4706 hasDuplicate = true;
4713 var form = document.createElement("div"),
4714 id = "script" + (new Date).getTime();
4715 form.innerHTML = "<a name='" + id + "'/>";
4717 var root = document.documentElement;
4718 root.insertBefore( form, root.firstChild );
4720 if ( !!document.getElementById( id ) ) {
4721 Expr.find.ID = function(match, context, isXML){
4722 if ( typeof context.getElementById !== "undefined" && !isXML ) {
4723 var m = context.getElementById(match[1]);
4724 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4728 Expr.filter.ID = function(elem, match){
4729 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4730 return elem.nodeType === 1 && node && node.nodeValue === match;
4734 root.removeChild( form );
4735 root = form = null; // release memory in IE
4740 var div = document.createElement("div");
4741 div.appendChild( document.createComment("") );
4743 if ( div.getElementsByTagName("*").length > 0 ) {
4744 Expr.find.TAG = function(match, context){
4745 var results = context.getElementsByTagName(match[1]);
4747 if ( match[1] === "*" ) {
4750 for ( var i = 0; results[i]; i++ ) {
4751 if ( results[i].nodeType === 1 ) {
4752 tmp.push( results[i] );
4763 div.innerHTML = "<a href='#'></a>";
4764 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4765 div.firstChild.getAttribute("href") !== "#" ) {
4766 Expr.attrHandle.href = function(elem){
4767 return elem.getAttribute("href", 2);
4771 div = null; // release memory in IE
4774 if ( document.querySelectorAll ) (function(){
4775 var oldSizzle = Sizzle, div = document.createElement("div");
4776 div.innerHTML = "<p class='TEST'></p>";
4778 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4782 Sizzle = function(query, context, extra, seed){
4783 context = context || document;
4785 if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4787 return makeArray( context.querySelectorAll(query), extra );
4791 return oldSizzle(query, context, extra, seed);
4794 for ( var prop in oldSizzle ) {
4795 Sizzle[ prop ] = oldSizzle[ prop ];
4798 div = null; // release memory in IE
4801 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4802 var div = document.createElement("div");
4803 div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4805 if ( div.getElementsByClassName("e").length === 0 )
4808 div.lastChild.className = "e";
4810 if ( div.getElementsByClassName("e").length === 1 )
4813 Expr.order.splice(1, 0, "CLASS");
4814 Expr.find.CLASS = function(match, context, isXML) {
4815 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4816 return context.getElementsByClassName(match[1]);
4820 div = null; // release memory in IE
4823 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4824 var sibDir = dir == "previousSibling" && !isXML;
4825 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4826 var elem = checkSet[i];
4828 if ( sibDir && elem.nodeType === 1 ){
4829 elem.sizcache = doneName;
4836 if ( elem.sizcache === doneName ) {
4837 match = checkSet[elem.sizset];
4841 if ( elem.nodeType === 1 && !isXML ){
4842 elem.sizcache = doneName;
4846 if ( elem.nodeName === cur ) {
4854 checkSet[i] = match;
4859 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4860 var sibDir = dir == "previousSibling" && !isXML;
4861 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4862 var elem = checkSet[i];
4864 if ( sibDir && elem.nodeType === 1 ) {
4865 elem.sizcache = doneName;
4872 if ( elem.sizcache === doneName ) {
4873 match = checkSet[elem.sizset];
4877 if ( elem.nodeType === 1 ) {
4879 elem.sizcache = doneName;
4882 if ( typeof cur !== "string" ) {
4883 if ( elem === cur ) {
4888 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4897 checkSet[i] = match;
4902 var contains = document.compareDocumentPosition ? function(a, b){
4903 return a.compareDocumentPosition(b) & 16;
4905 return a !== b && (a.contains ? a.contains(b) : true);
4908 var isXML = function(elem){
4909 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4910 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4913 var posProcess = function(selector, context){
4914 var tmpSet = [], later = "", match,
4915 root = context.nodeType ? [context] : context;
4917 while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4919 selector = selector.replace( Expr.match.PSEUDO, "" );
4922 selector = Expr.relative[selector] ? selector + "*" : selector;
4924 for ( var i = 0, l = root.length; i < l; i++ ) {
4925 Sizzle( selector, root[i], tmpSet );
4928 return Sizzle.filter( later, tmpSet );
4932 window.Sizzle = Sizzle;
4936 ;(function(engine) {
4937 var extendElements = Prototype.Selector.extendElements;
4939 function select(selector, scope) {
4940 return extendElements(engine(selector, scope || document));
4943 function match(element, selector) {
4944 return engine.matches(selector, [element]).length == 1;
4947 Prototype.Selector.engine = engine;
4948 Prototype.Selector.select = select;
4949 Prototype.Selector.match = match;
4952 window.Sizzle = Prototype._original_property;
4953 delete Prototype._original_property;
4956 reset: function(form) {
4962 serializeElements: function(elements, options) {
4963 if (typeof options != 'object') options = { hash: !!options };
4964 else if (Object.isUndefined(options.hash)) options.hash = true;
4965 var key, value, submitted = false, submit = options.submit;
4967 var data = elements.inject({ }, function(result, element) {
4968 if (!element.disabled && element.name) {
4969 key = element.name; value = $(element).getValue();
4970 if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
4971 submit !== false && (!submit || key == submit) && (submitted = true)))) {
4972 if (key in result) {
4973 if (!Object.isArray(result[key])) result[key] = [result[key]];
4974 result[key].push(value);
4976 else result[key] = value;
4982 return options.hash ? data : Object.toQueryString(data);
4987 serialize: function(form, options) {
4988 return Form.serializeElements(Form.getElements(form), options);
4991 getElements: function(form) {
4992 var elements = $(form).getElementsByTagName('*'),
4995 serializers = Form.Element.Serializers;
4996 for (var i = 0; element = elements[i]; i++) {
4999 return arr.inject([], function(elements, child) {
5000 if (serializers[child.tagName.toLowerCase()])
5001 elements.push(Element.extend(child));
5006 getInputs: function(form, typeName, name) {
5008 var inputs = form.getElementsByTagName('input');
5010 if (!typeName && !name) return $A(inputs).map(Element.extend);
5012 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
5013 var input = inputs[i];
5014 if ((typeName && input.type != typeName) || (name && input.name != name))
5016 matchingInputs.push(Element.extend(input));
5019 return matchingInputs;
5022 disable: function(form) {
5024 Form.getElements(form).invoke('disable');
5028 enable: function(form) {
5030 Form.getElements(form).invoke('enable');
5034 findFirstElement: function(form) {
5035 var elements = $(form).getElements().findAll(function(element) {
5036 return 'hidden' != element.type && !element.disabled;
5038 var firstByIndex = elements.findAll(function(element) {
5039 return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
5040 }).sortBy(function(element) { return element.tabIndex }).first();
5042 return firstByIndex ? firstByIndex : elements.find(function(element) {
5043 return /^(?:input|select|textarea)$/i.test(element.tagName);
5047 focusFirstElement: function(form) {
5049 form.findFirstElement().activate();
5053 request: function(form, options) {
5054 form = $(form), options = Object.clone(options || { });
5056 var params = options.parameters, action = form.readAttribute('action') || '';
5057 if (action.blank()) action = window.location.href;
5058 options.parameters = form.serialize(true);
5061 if (Object.isString(params)) params = params.toQueryParams();
5062 Object.extend(options.parameters, params);
5065 if (form.hasAttribute('method') && !options.method)
5066 options.method = form.method;
5068 return new Ajax.Request(action, options);
5072 /*--------------------------------------------------------------------------*/
5076 focus: function(element) {
5081 select: function(element) {
5082 $(element).select();
5087 Form.Element.Methods = {
5089 serialize: function(element) {
5090 element = $(element);
5091 if (!element.disabled && element.name) {
5092 var value = element.getValue();
5093 if (value != undefined) {
5095 pair[element.name] = value;
5096 return Object.toQueryString(pair);
5102 getValue: function(element) {
5103 element = $(element);
5104 var method = element.tagName.toLowerCase();
5105 return Form.Element.Serializers[method](element);
5108 setValue: function(element, value) {
5109 element = $(element);
5110 var method = element.tagName.toLowerCase();
5111 Form.Element.Serializers[method](element, value);
5115 clear: function(element) {
5116 $(element).value = '';
5120 present: function(element) {
5121 return $(element).value != '';
5124 activate: function(element) {
5125 element = $(element);
5128 if (element.select && (element.tagName.toLowerCase() != 'input' ||
5129 !(/^(?:button|reset|submit)$/i.test(element.type))))
5135 disable: function(element) {
5136 element = $(element);
5137 element.disabled = true;
5141 enable: function(element) {
5142 element = $(element);
5143 element.disabled = false;
5148 /*--------------------------------------------------------------------------*/
5150 var Field = Form.Element;
5152 var $F = Form.Element.Methods.getValue;
5154 /*--------------------------------------------------------------------------*/
5156 Form.Element.Serializers = {
5157 input: function(element, value) {
5158 switch (element.type.toLowerCase()) {
5161 return Form.Element.Serializers.inputSelector(element, value);
5163 return Form.Element.Serializers.textarea(element, value);
5167 inputSelector: function(element, value) {
5168 if (Object.isUndefined(value)) return element.checked ? element.value : null;
5169 else element.checked = !!value;
5172 textarea: function(element, value) {
5173 if (Object.isUndefined(value)) return element.value;
5174 else element.value = value;
5177 select: function(element, value) {
5178 if (Object.isUndefined(value))
5179 return this[element.type == 'select-one' ?
5180 'selectOne' : 'selectMany'](element);
5182 var opt, currentValue, single = !Object.isArray(value);
5183 for (var i = 0, length = element.length; i < length; i++) {
5184 opt = element.options[i];
5185 currentValue = this.optionValue(opt);
5187 if (currentValue == value) {
5188 opt.selected = true;
5192 else opt.selected = value.include(currentValue);
5197 selectOne: function(element) {
5198 var index = element.selectedIndex;
5199 return index >= 0 ? this.optionValue(element.options[index]) : null;
5202 selectMany: function(element) {
5203 var values, length = element.length;
5204 if (!length) return null;
5206 for (var i = 0, values = []; i < length; i++) {
5207 var opt = element.options[i];
5208 if (opt.selected) values.push(this.optionValue(opt));
5213 optionValue: function(opt) {
5214 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
5218 /*--------------------------------------------------------------------------*/
5221 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
5222 initialize: function($super, element, frequency, callback) {
5223 $super(callback, frequency);
5224 this.element = $(element);
5225 this.lastValue = this.getValue();
5228 execute: function() {
5229 var value = this.getValue();
5230 if (Object.isString(this.lastValue) && Object.isString(value) ?
5231 this.lastValue != value : String(this.lastValue) != String(value)) {
5232 this.callback(this.element, value);
5233 this.lastValue = value;
5238 Form.Element.Observer = Class.create(Abstract.TimedObserver, {
5239 getValue: function() {
5240 return Form.Element.getValue(this.element);
5244 Form.Observer = Class.create(Abstract.TimedObserver, {
5245 getValue: function() {
5246 return Form.serialize(this.element);
5250 /*--------------------------------------------------------------------------*/
5252 Abstract.EventObserver = Class.create({
5253 initialize: function(element, callback) {
5254 this.element = $(element);
5255 this.callback = callback;
5257 this.lastValue = this.getValue();
5258 if (this.element.tagName.toLowerCase() == 'form')
5259 this.registerFormCallbacks();
5261 this.registerCallback(this.element);
5264 onElementEvent: function() {
5265 var value = this.getValue();
5266 if (this.lastValue != value) {
5267 this.callback(this.element, value);
5268 this.lastValue = value;
5272 registerFormCallbacks: function() {
5273 Form.getElements(this.element).each(this.registerCallback, this);
5276 registerCallback: function(element) {
5278 switch (element.type.toLowerCase()) {
5281 Event.observe(element, 'click', this.onElementEvent.bind(this));
5284 Event.observe(element, 'change', this.onElementEvent.bind(this));
5291 Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
5292 getValue: function() {
5293 return Form.Element.getValue(this.element);
5297 Form.EventObserver = Class.create(Abstract.EventObserver, {
5298 getValue: function() {
5299 return Form.serialize(this.element);
5323 var docEl = document.documentElement;
5324 var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
5325 && 'onmouseleave' in docEl;
5328 if (Prototype.Browser.IE) {
5329 var buttonMap = { 0: 1, 1: 4, 2: 2 };
5330 _isButton = function(event, code) {
5331 return event.button === buttonMap[code];
5333 } else if (Prototype.Browser.WebKit) {
5334 _isButton = function(event, code) {
5336 case 0: return event.which == 1 && !event.metaKey;
5337 case 1: return event.which == 1 && event.metaKey;
5338 default: return false;
5342 _isButton = function(event, code) {
5343 return event.which ? (event.which === code + 1) : (event.button === code);
5347 function isLeftClick(event) { return _isButton(event, 0) }
5349 function isMiddleClick(event) { return _isButton(event, 1) }
5351 function isRightClick(event) { return _isButton(event, 2) }
5353 function element(event) {
5354 event = Event.extend(event);
5356 var node = event.target, type = event.type,
5357 currentTarget = event.currentTarget;
5359 if (currentTarget && currentTarget.tagName) {
5360 if (type === 'load' || type === 'error' ||
5361 (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
5362 && currentTarget.type === 'radio'))
5363 node = currentTarget;
5366 if (node.nodeType == Node.TEXT_NODE)
5367 node = node.parentNode;
5369 return Element.extend(node);
5372 function findElement(event, expression) {
5373 var element = Event.element(event);
5374 if (!expression) return element;
5376 if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
5377 return Element.extend(element);
5379 element = element.parentNode;
5383 function pointer(event) {
5384 return { x: pointerX(event), y: pointerY(event) };
5387 function pointerX(event) {
5388 var docElement = document.documentElement,
5389 body = document.body || { scrollLeft: 0 };
5391 return event.pageX || (event.clientX +
5392 (docElement.scrollLeft || body.scrollLeft) -
5393 (docElement.clientLeft || 0));
5396 function pointerY(event) {
5397 var docElement = document.documentElement,
5398 body = document.body || { scrollTop: 0 };
5400 return event.pageY || (event.clientY +
5401 (docElement.scrollTop || body.scrollTop) -
5402 (docElement.clientTop || 0));
5406 function stop(event) {
5407 Event.extend(event);
5408 event.preventDefault();
5409 event.stopPropagation();
5411 event.stopped = true;
5415 isLeftClick: isLeftClick,
5416 isMiddleClick: isMiddleClick,
5417 isRightClick: isRightClick,
5420 findElement: findElement,
5430 var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
5431 m[name] = Event.Methods[name].methodize();
5435 if (Prototype.Browser.IE) {
5436 function _relatedTarget(event) {
5438 switch (event.type) {
5439 case 'mouseover': element = event.fromElement; break;
5440 case 'mouseout': element = event.toElement; break;
5441 default: return null;
5443 return Element.extend(element);
5446 Object.extend(methods, {
5447 stopPropagation: function() { this.cancelBubble = true },
5448 preventDefault: function() { this.returnValue = false },
5449 inspect: function() { return '[object Event]' }
5452 Event.extend = function(event, element) {
5453 if (!event) return false;
5454 if (event._extendedByPrototype) return event;
5456 event._extendedByPrototype = Prototype.emptyFunction;
5457 var pointer = Event.pointer(event);
5459 Object.extend(event, {
5460 target: event.srcElement || element,
5461 relatedTarget: _relatedTarget(event),
5466 return Object.extend(event, methods);
5469 Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
5470 Object.extend(Event.prototype, methods);
5471 Event.extend = Prototype.K;
5474 function _createResponder(element, eventName, handler) {
5475 var registry = Element.retrieve(element, 'prototype_event_registry');
5477 if (Object.isUndefined(registry)) {
5478 CACHE.push(element);
5479 registry = Element.retrieve(element, 'prototype_event_registry', $H());
5482 var respondersForEvent = registry.get(eventName);
5483 if (Object.isUndefined(respondersForEvent)) {
5484 respondersForEvent = [];
5485 registry.set(eventName, respondersForEvent);
5488 if (respondersForEvent.pluck('handler').include(handler)) return false;
5491 if (eventName.include(":")) {
5492 responder = function(event) {
5493 if (Object.isUndefined(event.eventName))
5496 if (event.eventName !== eventName)
5499 Event.extend(event, element);
5500 handler.call(element, event);
5503 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
5504 (eventName === "mouseenter" || eventName === "mouseleave")) {
5505 if (eventName === "mouseenter" || eventName === "mouseleave") {
5506 responder = function(event) {
5507 Event.extend(event, element);
5509 var parent = event.relatedTarget;
5510 while (parent && parent !== element) {
5511 try { parent = parent.parentNode; }
5512 catch(e) { parent = element; }
5515 if (parent === element) return;
5517 handler.call(element, event);
5521 responder = function(event) {
5522 Event.extend(event, element);
5523 handler.call(element, event);
5528 responder.handler = handler;
5529 respondersForEvent.push(responder);
5533 function _destroyCache() {
5534 for (var i = 0, length = CACHE.length; i < length; i++) {
5535 Event.stopObserving(CACHE[i]);
5542 if (Prototype.Browser.IE)
5543 window.attachEvent('onunload', _destroyCache);
5545 if (Prototype.Browser.WebKit)
5546 window.addEventListener('unload', Prototype.emptyFunction, false);
5549 var _getDOMEventName = Prototype.K,
5550 translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
5552 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
5553 _getDOMEventName = function(eventName) {
5554 return (translations[eventName] || eventName);
5558 function observe(element, eventName, handler) {
5559 element = $(element);
5561 var responder = _createResponder(element, eventName, handler);
5563 if (!responder) return element;
5565 if (eventName.include(':')) {
5566 if (element.addEventListener)
5567 element.addEventListener("dataavailable", responder, false);
5569 element.attachEvent("ondataavailable", responder);
5570 element.attachEvent("onfilterchange", responder);
5573 var actualEventName = _getDOMEventName(eventName);
5575 if (element.addEventListener)
5576 element.addEventListener(actualEventName, responder, false);
5578 element.attachEvent("on" + actualEventName, responder);
5584 function stopObserving(element, eventName, handler) {
5585 element = $(element);
5587 var registry = Element.retrieve(element, 'prototype_event_registry');
5588 if (!registry) return element;
5591 registry.each( function(pair) {
5592 var eventName = pair.key;
5593 stopObserving(element, eventName);
5598 var responders = registry.get(eventName);
5599 if (!responders) return element;
5602 responders.each(function(r) {
5603 stopObserving(element, eventName, r.handler);
5608 var responder = responders.find( function(r) { return r.handler === handler; });
5609 if (!responder) return element;
5611 if (eventName.include(':')) {
5612 if (element.removeEventListener)
5613 element.removeEventListener("dataavailable", responder, false);
5615 element.detachEvent("ondataavailable", responder);
5616 element.detachEvent("onfilterchange", responder);
5619 var actualEventName = _getDOMEventName(eventName);
5620 if (element.removeEventListener)
5621 element.removeEventListener(actualEventName, responder, false);
5623 element.detachEvent('on' + actualEventName, responder);
5626 registry.set(eventName, responders.without(responder));
5631 function fire(element, eventName, memo, bubble) {
5632 element = $(element);
5634 if (Object.isUndefined(bubble))
5637 if (element == document && document.createEvent && !element.dispatchEvent)
5638 element = document.documentElement;
5641 if (document.createEvent) {
5642 event = document.createEvent('HTMLEvents');
5643 event.initEvent('dataavailable', true, true);
5645 event = document.createEventObject();
5646 event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
5649 event.eventName = eventName;
5650 event.memo = memo || { };
5652 if (document.createEvent)
5653 element.dispatchEvent(event);
5655 element.fireEvent(event.eventType, event);
5657 return Event.extend(event);
5660 Event.Handler = Class.create({
5661 initialize: function(element, eventName, selector, callback) {
5662 this.element = $(element);
5663 this.eventName = eventName;
5664 this.selector = selector;
5665 this.callback = callback;
5666 this.handler = this.handleEvent.bind(this);
5670 Event.observe(this.element, this.eventName, this.handler);
5675 Event.stopObserving(this.element, this.eventName, this.handler);
5679 handleEvent: function(event) {
5680 var element = event.findElement(this.selector);
5681 if (element) this.callback.call(this.element, event, element);
5685 function on(element, eventName, selector, callback) {
5686 element = $(element);
5687 if (Object.isFunction(selector) && Object.isUndefined(callback)) {
5688 callback = selector, selector = null;
5691 return new Event.Handler(element, eventName, selector, callback).start();
5694 Object.extend(Event, Event.Methods);
5696 Object.extend(Event, {
5699 stopObserving: stopObserving,
5703 Element.addMethods({
5708 stopObserving: stopObserving,
5713 Object.extend(document, {
5714 fire: fire.methodize(),
5716 observe: observe.methodize(),
5718 stopObserving: stopObserving.methodize(),
5725 if (window.Event) Object.extend(window.Event, Event);
5726 else window.Event = Event;
5730 /* Support for the DOMContentLoaded event is based on work by Dan Webb,
5731 Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
5735 function fireContentLoadedEvent() {
5736 if (document.loaded) return;
5737 if (timer) window.clearTimeout(timer);
5738 document.loaded = true;
5739 document.fire('dom:loaded');
5742 function checkReadyState() {
5743 if (document.readyState === 'complete') {
5744 document.stopObserving('readystatechange', checkReadyState);
5745 fireContentLoadedEvent();
5749 function pollDoScroll() {
5750 try { document.documentElement.doScroll('left'); }
5752 timer = pollDoScroll.defer();
5755 fireContentLoadedEvent();
5758 if (document.addEventListener) {
5759 document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
5761 document.observe('readystatechange', checkReadyState);
5763 timer = pollDoScroll.defer();
5766 Event.observe(window, 'load', fireContentLoadedEvent);
5769 Element.addMethods();
5771 /*------------------------------- DEPRECATED -------------------------------*/
5773 Hash.toQueryString = Object.toQueryString;
5775 var Toggle = { display: Element.toggle };
5777 Element.Methods.childOf = Element.Methods.descendantOf;
5780 Before: function(element, content) {
5781 return Element.insert(element, {before:content});
5784 Top: function(element, content) {
5785 return Element.insert(element, {top:content});
5788 Bottom: function(element, content) {
5789 return Element.insert(element, {bottom:content});
5792 After: function(element, content) {
5793 return Element.insert(element, {after:content});
5797 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
5800 includeScrollOffsets: false,
5802 prepare: function() {
5803 this.deltaX = window.pageXOffset
5804 || document.documentElement.scrollLeft
5805 || document.body.scrollLeft
5807 this.deltaY = window.pageYOffset
5808 || document.documentElement.scrollTop
5809 || document.body.scrollTop
5813 within: function(element, x, y) {
5814 if (this.includeScrollOffsets)
5815 return this.withinIncludingScrolloffsets(element, x, y);
5818 this.offset = Element.cumulativeOffset(element);
5820 return (y >= this.offset[1] &&
5821 y < this.offset[1] + element.offsetHeight &&
5822 x >= this.offset[0] &&
5823 x < this.offset[0] + element.offsetWidth);
5826 withinIncludingScrolloffsets: function(element, x, y) {
5827 var offsetcache = Element.cumulativeScrollOffset(element);
5829 this.xcomp = x + offsetcache[0] - this.deltaX;
5830 this.ycomp = y + offsetcache[1] - this.deltaY;
5831 this.offset = Element.cumulativeOffset(element);
5833 return (this.ycomp >= this.offset[1] &&
5834 this.ycomp < this.offset[1] + element.offsetHeight &&
5835 this.xcomp >= this.offset[0] &&
5836 this.xcomp < this.offset[0] + element.offsetWidth);
5839 overlap: function(mode, element) {
5840 if (!mode) return 0;
5841 if (mode == 'vertical')
5842 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
5843 element.offsetHeight;
5844 if (mode == 'horizontal')
5845 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
5846 element.offsetWidth;
5850 cumulativeOffset: Element.Methods.cumulativeOffset,
5852 positionedOffset: Element.Methods.positionedOffset,
5854 absolutize: function(element) {
5856 return Element.absolutize(element);
5859 relativize: function(element) {
5861 return Element.relativize(element);
5864 realOffset: Element.Methods.cumulativeScrollOffset,
5866 offsetParent: Element.Methods.getOffsetParent,
5868 page: Element.Methods.viewportOffset,
5870 clone: function(source, target, options) {
5871 options = options || { };
5872 return Element.clonePosition(target, source, options);
5876 /*--------------------------------------------------------------------------*/
5878 if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
5879 function iter(name) {
5880 return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
5883 instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
5884 function(element, className) {
5885 className = className.toString().strip();
5886 var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
5887 return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
5888 } : function(element, className) {
5889 className = className.toString().strip();
5890 var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
5891 if (!classNames && !className) return elements;
5893 var nodes = $(element).getElementsByTagName('*');
5894 className = ' ' + className + ' ';
5896 for (var i = 0, child, cn; child = nodes[i]; i++) {
5897 if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
5898 (classNames && classNames.all(function(name) {
5899 return !name.toString().blank() && cn.include(' ' + name + ' ');
5901 elements.push(Element.extend(child));
5906 return function(className, parentElement) {
5907 return $(parentElement || document.body).getElementsByClassName(className);
5911 /*--------------------------------------------------------------------------*/
5913 Element.ClassNames = Class.create();
5914 Element.ClassNames.prototype = {
5915 initialize: function(element) {
5916 this.element = $(element);
5919 _each: function(iterator) {
5920 this.element.className.split(/\s+/).select(function(name) {
5921 return name.length > 0;
5925 set: function(className) {
5926 this.element.className = className;
5929 add: function(classNameToAdd) {
5930 if (this.include(classNameToAdd)) return;
5931 this.set($A(this).concat(classNameToAdd).join(' '));
5934 remove: function(classNameToRemove) {
5935 if (!this.include(classNameToRemove)) return;
5936 this.set($A(this).without(classNameToRemove).join(' '));
5939 toString: function() {
5940 return $A(this).join(' ');
5944 Object.extend(Element.ClassNames.prototype, Enumerable);
5946 /*--------------------------------------------------------------------------*/
5949 window.Selector = Class.create({
5950 initialize: function(expression) {
5951 this.expression = expression.strip();
5954 findElements: function(rootElement) {
5955 return Prototype.Selector.select(this.expression, rootElement);
5958 match: function(element) {
5959 return Prototype.Selector.match(element, this.expression);
5962 toString: function() {
5963 return this.expression;
5966 inspect: function() {
5967 return "#<Selector: " + this.expression + ">";
5971 Object.extend(Selector, {
5972 matchElements: function(elements, expression) {
5973 var match = Prototype.Selector.match,
5976 for (var i = 0, length = elements.length; i < length; i++) {
5977 var element = elements[i];
5978 if (match(element, expression)) {
5979 results.push(Element.extend(element));
5985 findElement: function(elements, expression, index) {
5987 var matchIndex = 0, element;
5988 for (var i = 0, length = elements.length; i < length; i++) {
5989 element = elements[i];
5990 if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
5991 return Element.extend(element);
5996 findChildElements: function(element, expressions) {
5997 var selector = expressions.toArray().join(', ');
5998 return Prototype.Selector.select(selector, element || document);