summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/ajax/prototype
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/ajax/prototype')
-rw-r--r--dom/tests/mochitest/ajax/prototype/dist/prototype.js4681
-rw-r--r--dom/tests/mochitest/ajax/prototype/dist/prototype_update_helper.js417
-rw-r--r--dom/tests/mochitest/ajax/prototype/manifest.json1
-rw-r--r--dom/tests/mochitest/ajax/prototype/mochitest.ini67
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/browser.html229
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/console.html110
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/functional/event.html243
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/lib/unittest.js602
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/test.css50
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/ajax_test.js286
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/array_test.js190
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/base_test.js511
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/dom_test.js1404
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/element_mixins_test.js32
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/enumerable_test.js263
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/event_test.js286
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.html2
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.js42
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/array.html1
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.html6
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.js106
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/content.html1
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/data.json1
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.css85
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.html285
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.js17
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.html4
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.js2
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.html0
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.js1
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.html8
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.js23
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/event.html4
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/form.html112
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hash.js25
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hello.js1
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/logo.gifbin0 -> 3502 bytes
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/position.html9
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/selector.html69
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/string.js8
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/fixtures/unittest.html18
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/form_test.js384
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/hash_test.js178
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/number_test.js44
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/position_test.js44
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/range_test.js58
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/selector_test.js377
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/string_test.js540
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/ajax_test.html42
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/array_test.html39
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/base_test.html45
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/dom_test.html326
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/element_mixins_test.html43
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/enumerable_test.html48
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/event_test.html42
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/form_test.html150
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/hash_test.html40
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/number_test.html38
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/position_test.html47
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/range_test.html38
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/selector_test.html107
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/string_test.html40
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/tmp/unit_test.html56
-rw-r--r--dom/tests/mochitest/ajax/prototype/test/unit/unittest_test.js143
-rw-r--r--dom/tests/mochitest/ajax/prototype/test_Prototype.html15
65 files changed, 13086 insertions, 0 deletions
diff --git a/dom/tests/mochitest/ajax/prototype/dist/prototype.js b/dom/tests/mochitest/ajax/prototype/dist/prototype.js
new file mode 100644
index 0000000000..69f82032a8
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/dist/prototype.js
@@ -0,0 +1,4681 @@
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2007 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ SelectorsAPI: !!document.querySelector,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div')['__proto__'] &&
+ document.createElement('div')['__proto__'] !==
+ document.createElement('form')['__proto__']
+ },
+
+ ScriptFragment: '<script[^>]*>([^\\x00]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = (function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method);
+
+ value.valueOf = method.valueOf.bind(method);
+ value.toString = method.toString.bind(method);
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return !!(object && object.nodeType == 1);
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object && object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function" && typeof object.call == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+ .replace(/\s+/g, '').split(',');
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ defer: function() {
+ var args = [0.01].concat($A(arguments));
+ return this.delay.apply(this, args);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: (function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'ig');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'i');
+ var matchComments = new RegExp('<!--\\s*' + Prototype.ScriptFragment + '\\s*-->', 'i');
+
+ return function() {
+ if (this.indexOf('<script') == -1) return [];
+ return (this.replace(matchComments, '').match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ }
+ })(),
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.container.innerHTML.replace(/"/g, '&quot;');
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ // Safari requires the text nested inside another element to render correctly
+ div.innerHTML = '<pre>' + this.stripTags() + '</pre>';
+ div = div.firstChild;
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo + node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ container: document.createElement('pre'),
+ text: document.createTextNode('')
+});
+
+String.prototype.escapeHTML.container.appendChild(String.prototype.escapeHTML.text);
+
+if (Prototype.Browser.IE) {
+ // IE converts all newlines to carriage returns so we swap them back
+ String.prototype.unescapeHTML = String.prototype.unescapeHTML.wrap(function(proceed) {
+ return proceed().replace(/\r/g, '\n')
+ });
+}
+
+if ('>'.escapeHTML() !== '&gt;') {
+ // Safari 3.x has issues with escaping the ">" character
+ (function() {
+ var escapeHTML = String.prototype.escapeHTML;
+ Object.extend(
+ String.prototype.escapeHTML = escapeHTML.wrap(function(proceed) {
+ return proceed().replace(/>/g, "&gt;")
+ }), {
+ container: escapeHTML.container,
+ text: escapeHTML.text
+ })
+ })();
+}
+
+if ('&'.escapeHTML() !== '&amp;') {
+ // Safari 2.x has issues with escaping html inside a "pre" element so we use the deprecated "xmp" element instead
+ Object.extend(String.prototype.escapeHTML, {
+ container: document.createElement('xmp'),
+ text: document.createTextNode('')
+ });
+ String.prototype.escapeHTML.container.appendChild(String.prototype.escapeHTML.text);
+}
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ iterator.call(context, value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ var index = -number, slices = [], array = this.toArray();
+ if (number < 1) return array;
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator.call(context, value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator.call(context, value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator.call(context, value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator.call(context, value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator.call(context, value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator.call(context, value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while (slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ this.each(function(value, index) {
+ memo = iterator.call(context, memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator.call(context, value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator.call(context, value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator.call(context, value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator.call(context, value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ return this.map(function(value, index) {
+ return {
+ value: value,
+ criteria: iterator.call(context, value, index)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ var length = array.length, i;
+ return this.uniq().findAll(function(item) {
+ i = length;
+ while (i--) if (item === array[i]) return true;
+ return false;
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator, context) {
+ $R(0, this, true).each(iterator, context);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ // simulating poorly supported hasOwnProperty
+ if (this._object[key] !== Object.prototype[key])
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.inject([], function(results, pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return results.concat(values.map(toQueryPair.curry(key)));
+ } else results.push(toQueryPair(key, values));
+ return results;
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ var transport = false;
+ Ajax.getTransport = Try.these(
+ function() {
+ /* fallback on activex xmlhttp to avoid IE7 local file-system read error */
+ if (Prototype.Browser.IE && window.location.href.indexOf('file://') == 0) throw 'skip';
+ transport = new XMLHttpRequest();
+ return function(){ return new XMLHttpRequest()}
+ },
+ function() {
+ transport = new ActiveXObject('Msxml2.XMLHTTP');
+ return function(){ return new ActiveXObject('Msxml2.XMLHTTP')}
+ },
+ function() {
+ transport = new ActiveXObject('Microsoft.XMLHTTP');
+ return function(){ return new ActiveXObject('Microsoft.XMLHTTP')}
+ }
+ ) || false;
+
+ return transport;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.allowStatusZero = false;
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+ _allowStatusZero: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: (function() {
+
+ var absoluteExp = /^[a-z]{3,5}:/, fileExp = /^(file|ftp):/;
+ function isRelative(url) {
+ return !absoluteExp.test(url);
+ }
+
+ function isFileProtocol(url) {
+ return fileExp.test(url);
+ }
+
+ return function(url) {
+ var base;
+ if (Prototype.Browser.Opera && opera.version() < 9.5 &&
+ isRelative(url) && (base = $(document.documentElement).down('base')))
+ url = base.readAttribute('href') + url;
+
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+ this._allowStatusZero = isFileProtocol(this.url) ||
+ (isRelative(url) && isFileProtocol(window.location.protocol));
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ if (this.method == 'post' &&
+ /Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ }
+ })(),
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return (!status && this._allowStatusZero) || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if (readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && (attributes.name || attributes.type)) {
+ tagName = '<' + tagName +
+ (attributes.name ? ' name="' + attributes.name + '"' : '') +
+ (attributes.type ? ' type="' + attributes.type + '"' : '') + '>';
+ delete attributes.name; delete attributes.type;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+ if (element) this.Element.prototype = element.prototype;
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ element = $(element);
+ var originalDisplay = element.style.display;
+ if (originalDisplay && originalDisplay != 'none')
+ element._originalDisplay = originalDisplay;
+ element.style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ element = $(element);
+ if (element._originalDisplay) {
+ element.style.display = element._originalDisplay;
+ element._originalDisplay = null;
+ } else element.style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ Element.select(element, expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ var t = Element._attributeTranslations.read;
+ if (t.names[name]) name = t.names[name];
+
+ if (Prototype.Browser.IE) {
+ // If we're reading from a form, avoid a conflict between an attribute
+ // and a child name.
+ if (element.tagName.toUpperCase() == 'FORM' &&
+ !/^((child|parent)Node|(next|previous)Sibling)$/.test(name) &&
+ element.children[name]){
+ element = $(element.cloneNode(false));
+ }
+ if (t.values[name]) return t.values[name](element, name);
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ } else if (t.values[name]) return t.values[name](element, name);
+
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[name]) name = t.values[name](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (ancestor.contains)
+ return ancestor.contains(element) && ancestor !== element;
+
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value || value == 'auto') {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = element.getStyle('display'),
+ dimensions = { width: element.clientWidth, height: element.clientHeight };
+
+ // All *Width and *Height properties give 0 on elements with display: none,
+ // so enable the element temporarily
+ if (display === "none" || display === null) {
+ var els = element.style,
+ originalVisibility = els.visibility,
+ originalPosition = els.position,
+ originalDisplay = els.display;
+
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+
+ dimensions = { width: element.clientWidth, height: element.clientHeight };
+
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ }
+
+ return dimensions;
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName.toUpperCase() == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return element;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset(),
+ dimensions = element.getDimensions(),
+ top = offsets[1],
+ left = offsets[0],
+ width = dimensions.width,
+ height = dimensions.height;
+
+ Object.extend(element, {
+ _originalLeft: left - parseFloat(element.style.left || 0),
+ _originalTop: top - parseFloat(element.style.top || 0),
+ _originalWidth: element.style.width,
+ _originalHeight: element.style.height
+ });
+
+ element.setStyle({
+ position: 'absolute',
+ top: top + 'px',
+ left: left + 'px',
+ width: width + 'px',
+ height: height + 'px'
+ });
+
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return element;
+
+ if (!element._originalTop){
+ /* fix bizarre IE position issue with empty elements */
+ var isBuggy = element.outerHTML && element.innerHTML.blank();
+ if (isBuggy) element.innerHTML = '\x00';
+
+ Object.extend(element, {
+ _originalTop: element.offsetTop,
+ _originalLeft: element.offsetLeft,
+ _originalWidth: Element.getStyle(element, 'width'),
+ _originalHeight: Element.getStyle(element, 'height'),
+ _originalMarginTop: Element.getStyle(element, 'marginTop'),
+ _originalMarginLeft: Element.getStyle(element, 'marginLeft')
+ });
+
+ if (isBuggy) element.innerHTML = '';
+ }
+
+ Element.setStyle(element, {
+ position: 'relative',
+ width: element._originalWidth,
+ height: element._originalHeight,
+ marginTop: element._originalMarginTop,
+ marginLeft: element._originalMarginLeft
+ });
+
+ var offsets = element.positionedOffset(),
+ top = element._originalTop - offsets.top,
+ left = element._originalLeft - offsets.left;
+
+ var isAuto = /^(auto|)$/;
+ if (!isAuto.test(element.style.top)) top += element._originalTop;
+ if (!isAuto.test(element.style.left)) left += element._originalLeft;
+
+ Element.setStyle(element, {
+ top: top + 'px',
+ left: left + 'px'
+ });
+
+ return element;
+ },
+
+ getOffsetParent: function(element) {
+ element = $(element);
+ var op = element.offsetParent, docElement = document.documentElement;
+ if (op && op != docElement) return $(op);
+
+ while ((element = element.parentNode) && element !== docElement &&
+ element !== document) {
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+ }
+
+ return $(document.body);
+ }
+};
+
+Object.extend(Element.Methods, (function() {
+ function getNumericStyle(element, style) {
+ return parseFloat(Element.getStyle(element, style)) || 0;
+ }
+
+ function getStyleDiff(element, source, style) {
+ return getNumericStyle(source, style) - getNumericStyle(element, style);
+ }
+
+ function cloneDimension(element, source, dimension) {
+ var d = Element.getDimensions(source), style = { };
+ style[dimension] = d[dimension] + 'px';
+
+ var styles = $w('margin padding');
+ var sides = (dimension === 'height') ? $w('top bottom') :
+ $w('left right');
+
+ var property;
+ for (var i = 0; i < 2; i++) {
+ for (var j = 0; j < 2; j++) {
+ property = styles[i] + sides[j].capitalize();
+ style[property] = (getNumericStyle(element, property) +
+ getStyleDiff(element, source, property)) + 'px';
+ }
+ }
+ Element.setStyle(element, style);
+ }
+
+ return {
+ cumulativeScrollOffset: function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0, endElement = document;
+ var B = Prototype.Browser;
+
+ // Safari and Opera need to stop at document.body or else they'll
+ // report inaccurate values.
+ if (B.WebKit || B.Opera && opera.version() < 9.5) {
+ if ([document, document.body, document.documentElement].include(element))
+ return Element._returnOffset(0, 0);
+
+ endElement = document.body;
+ }
+
+ if (Element.getStyle(element, 'position') !== 'fixed') {
+ while ((element = element.parentNode) && element !== endElement) {
+ if (Element.getStyle(element, 'position') === 'fixed') break;
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ }
+ }
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ cumulativeOffset: function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += (element.offsetTop || 0);
+ valueL += (element.offsetLeft || 0);
+ } while ((element = Element.getOffsetParent(element)) != document.body);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += (element.offsetTop || 0);
+ valueL += (element.offsetLeft || 0);
+ element = Element.getOffsetParent(element);
+ } while (element != document.body &&
+ Element.getStyle(element, 'position') == 'static');
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ viewportOffset: function(forElement) {
+ forElement = $(forElement);
+ var op, element = forElement, valueT = 0, valueL = 0;
+
+ do {
+ valueT += (element.offsetTop || 0);
+ valueL += (element.offsetLeft || 0);
+
+ // Safari fix
+ op = Element.getOffsetParent(element);
+ if (op == document.body && Element.getStyle(element,
+ 'position') == 'absolute') break;
+ } while ((element = op) != document.body);
+
+ var scrollOffset = Element.cumulativeScrollOffset(forElement);
+ valueT -= scrollOffset.top;
+ valueL -= scrollOffset.left;
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ element = $(element);
+ source = $(source);
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find coordinate system to use
+ // delta [0,0] will do fine with position: fixed elements;
+ // position: absolute needs offsetParent deltas
+ var parent, delta = [0, 0];
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = Element.getOffsetParent(element);
+ delta = Element.viewportOffset(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set dimensions
+ if (options.setWidth) cloneDimension(element, source, 'width');
+ if (options.setHeight) cloneDimension(element, source, 'height');
+
+ // find page position of source
+ var p = Element.viewportOffset(source),
+ borderOffset = ['borderLeftWidth', 'borderTopWidth'].map(
+ function(style) { return getStyleDiff(element, source, style); });
+
+ if (options.setLeft) {
+ var left = p[0] - delta[0] + borderOffset[0];
+ if (options.offsetLeft)
+ left += options.offsetLeft + getNumericStyle(element, 'paddingLeft');
+
+ element.style.left = left + 'px';
+ }
+ if (options.setTop) {
+ var top = p[1] - delta[1] + borderOffset[1];
+ if (options.offsetTop)
+ top += options.offsetTop + getNumericStyle(element, 'paddingTop');
+
+ element.style.top = top + 'px';
+ }
+ return element;
+ }
+ };
+})());
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ },
+
+ read: {
+ names: { },
+ values: {
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ }
+ }
+ }
+};
+
+(function(v) {
+ Object.extend(v, {
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag
+ });
+})(Element._attributeTranslations.read.values);
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return $(element).title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ // IE throws an error if element is not in document
+ try { element.offsetParent; }
+ catch(e) { return $(document.body); }
+
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+
+ var position = Element.getStyle(element, 'position');
+ if (position !== 'static') return proceed(element);
+
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = Element.getOffsetParent(element),
+ style = { position: 'relative' };
+ if (Element.getOffsetParent(offsetParent, 'position') === 'fixed')
+ style.zoom = '1';
+
+ Element.setStyle(element, style);
+ var value = proceed(element);
+ Element.setStyle(element, { position: position});
+ return value;
+ }
+ );
+ });
+
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ (function(t) {
+ t.has = { };
+ t.write.names = { };
+
+ $w('cellPadding cellSpacing colSpan rowSpan vAlign dateTime accessKey ' +
+ 'tabIndex encType maxLength readOnly longDesc frameBorder').each(function(attr) {
+ var lower = attr.toLowerCase();
+ t.has[lower] = attr;
+ t.read.names[lower] = attr;
+ t.write.names[lower] = attr;
+ });
+
+ [t.write.names, t.read.names].each(function(n) {
+ Object.extend(n, {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ });
+ });
+ })(Element._attributeTranslations);
+
+ Object.extend(Element._attributeTranslations.read.values, {
+
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+
+ title: function(element) {
+ return element.title;
+ }
+ });
+
+ Object.extend(Element._attributeTranslations.write.values, {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ encType: function(element, value) {
+ element.getAttributeNode('encType').value = value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ });
+
+ (function(v) {
+ delete v.readonly;
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv,
+ readOnly: v._flag.wrap(function(proceed, element, attribute) {
+ attribute = proceed(element, attribute);
+ return attribute? 'readonly' : null;
+ })
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if (element.tagName.toUpperCase() == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ element = $(element);
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if (Prototype.Browser.IE) {
+ // Wrap Element#update to clean up event handlers on
+ // newly-removed elements. Prevents memory leaks in IE.
+ Element.Methods.update = Element.Methods.update.wrap(
+ function(proceed, element, contents) {
+ Element.select(element, '*').each(Event.stopObserving);
+ return proceed(element, contents);
+ }
+ );
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ // Avoid outerHTML in IE because it incorrectly removes the replaced
+ // elements' child nodes.
+ if (Element._insertionTranslations.tags[tagName] || Prototype.Browser.IE) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName,
+ content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ // No use of $ in this function in order to keep things fast.
+ // Used by the Selector class.
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = element.getAttributeNode(attribute);
+ return !!(node && node.specified);
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div')['__proto__']) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div')['__proto__'];
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ // Filter out XML nodes in IE.
+ if (!(element.ownerDocument || element).body) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName.toUpperCase(), property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+
+// No use of $ in this function in order to keep things fast.
+// Used by the Selector class.
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "BUTTON": Object.clone(Form.Element.Methods),
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName)['__proto__'];
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { }, B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+ (B.Opera && opera.version() < 9.5) ? document.body['client' + D] : document.documentElement['client' + D];
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+
+ if (this.shouldUseSelectorsAPI()) {
+ this.mode = 'selectorsAPI';
+ } else if (this.shouldUseXPath()) {
+ this.mode = 'xpath';
+ this.compileXPathMatcher();
+ } else {
+ this.mode = "normal";
+ this.compileMatcher();
+ }
+
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(e))
+ return false;
+
+ return true;
+ },
+
+ shouldUseSelectorsAPI: function() {
+ if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+ if (!Selector._div) Selector._div = new Element('div');
+
+ // Make sure the browser treats the selector as valid. Test on an
+ // isolated element to minimize cost of this check.
+ try {
+ Selector._div.querySelector(this.expression);
+ } catch(e) {
+ return false;
+ }
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ var e = this.expression, results;
+
+ switch (this.mode) {
+ case 'selectorsAPI':
+ // querySelectorAll queries document-wide, then filters to descendants
+ // of the context element. That's not what we want.
+ // Add an explicit context to the selector if necessary.
+ if (root !== document) {
+ var oldId = root.id, id = $(root).identify();
+ e = "#" + id + " " + e;
+ }
+
+ results = $A(root.querySelectorAll(e)).map(Element.extend);
+ root.id = oldId;
+
+ return results;
+ case 'xpath':
+ return document._getElementsByXPath(this.xpath, root);
+ default:
+ return this.matcher(root);
+ }
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0)]",
+ 'checked': "[@checked]",
+ 'disabled': "[(@disabled) and (@type!='hidden')]",
+ 'enabled': "[not(@disabled) and (@type!='hidden')]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) {
+ // IE doesn't find elements by ID if they're not attached to the
+ // document.
+ if (Prototype.Browser.IE && (root.sourceIndex < 1 || root === document)) {
+ var nodes = root.getElementsByTagName('*');
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (node[id] = id) {
+ targetNode = node; break;
+ }
+ } if (!targetNode) return [];
+ } else return [];
+ }
+
+ if (!nodes && root === document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || node.firstChild) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled && (!node.type || node.type !== 'hidden'))
+ results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+ '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+ '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+ '-').include('-' + (v || "").toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options !== 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+
+ var key, value, type, isImageType, isSubmitButton, submitSerialized;
+ var submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ element = $(element);
+ key = element.name;
+ value = element.getValue();
+ type = element.type;
+
+ isImageType = type === 'image';
+ isSubmitButton = (type === 'submit' || isImageType);
+
+ // Null values don't get serialized
+ if (value === null) return result;
+ // Disabled elements don't get serialized
+ if (element.disabled) return result;
+ // <input type="file|reset" /> doesn't get serialized
+ if (type === 'file' || type === 'reset') return result;
+ // Non-active submit buttons don't get serialized
+ if (isSubmitButton &&
+ (submit === false || submitSerialized ||
+ (submit && !(key === submit || element === submit))))
+ return result;
+
+ if (isSubmitButton) {
+ submitSerialized = true;
+ if (isImageType) {
+ var prefix = key ? key + '.' : '',
+ x = options.x || 0, y = options.y || 0;
+
+ result[prefix + 'x'] = x;
+ result[prefix + 'y'] = y;
+ return result;
+ }
+ }
+
+ else if (!key) return result;
+
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ } else result[key] = value;
+
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['button', 'input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ if (!(element = $(element))) return null;
+ var method = element.tagName.toLowerCase(), s = Form.Element.Serializers;
+ return s[method]? s[method](element) : null;
+ },
+
+ setValue: function(element, value) {
+ if (!(element = $(element))) return null;
+ var method = element.tagName.toLowerCase(), s = Form.Element.Serializers;
+ if (s[method]) s[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'image', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ button: function(element, value){
+ if (Object.isUndefined(value)) return element.innerHTML;
+ else element.innerHTML = value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (Object.isUndefined(index))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ event = Event.extend(event);
+ var node = event.target, currentTarget = event.currentTarget, type = event.type;
+
+ if (currentTarget && currentTarget.tagName) {
+ // Firefox screws up the "click" event when moving between radio buttons
+ // via arrow keys. It also screws up the "load" and "error" events on images,
+ // reporting the document as the target instead of the original image.
+ if (['load', 'error'].include(type) ||
+ (currentTarget.tagName.toUpperCase() === "INPUT" && currentTarget.type === "radio" && type === "click"))
+ node = currentTarget;
+ }
+
+ return Element.extend(node && node.nodeType == Node.TEXT_NODE ?
+ node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollLeft: 0, scrollTop: 0 };
+ return {
+ x: event.pageX || (event.clientX +
+ (docElement.scrollLeft || body.scrollLeft) -
+ (docElement.clientLeft || 0)),
+ y: event.pageY || (event.clientY +
+ (docElement.scrollTop || body.scrollTop) -
+ (docElement.clientTop || 0))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ _extendedByPrototype: Prototype.emptyFunction,
+ target: Element.extend(event.srcElement),
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ // Event ID is stored as the 0th index in a one-item array so that it
+ // won't get copied to a new node when cloneNode is called.
+ if (element === window) return 1;
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ return element._prototypeEventID = [arguments.callee.id++];
+ }
+ getEventID.id = 2;
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function addEventDispatcher(element, eventName, dispatchWrapper) {
+ var id = getEventID(element), wrappers = getWrappersForEventName(id, eventName);
+ if (wrappers.dispatcher) return;
+
+ wrappers.dispatcher = function(event) {
+ var w = getWrappersForEventName(id, eventName);
+ for(var i = 0, l = w.length; i < l; i++) w[i](event); // execute wrappers
+ };
+
+ if (dispatchWrapper) wrappers.dispatcher = wrappers.dispatcher.wrap(dispatchWrapper);
+ element.attachEvent("on" + getDOMEventName(eventName), wrappers.dispatcher);
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element), c = getCacheForID(id);
+
+ // Attach the element itself onto its cache entry so we can retrieve it for
+ // cleanup on page unload.
+ if (!c.element) c.element = element;
+
+ var w = getWrappersForEventName(id, eventName);
+ if (w.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ handler.call(element, Event.extend(event));
+ };
+
+ wrapper.handler = handler;
+ w.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var w = getWrappersForEventName(id, eventName);
+ return w.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ var d = c[eventName].dispatcher;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ c[eventName].dispatcher = d;
+ }
+
+ // Loop through all elements and remove all handlers on page unload. IE
+ // needs this in order to prevent memory leaks.
+ function purgeListeners() {
+ var element, entry;
+ for (var i in Event.cache) {
+ entry = Event.cache[i];
+ Event.stopObserving(entry.element);
+ entry.element = null;
+ }
+ }
+
+ function onStop() {
+ document.detachEvent("onstop", onStop);
+ purgeListeners();
+ }
+
+ function onBeforeUnload() {
+ if (document.readyState === "interactive") {
+ document.attachEvent("onstop", onStop);
+ (function() { document.detachEvent("onstop", onStop); }).defer();
+ }
+ }
+
+ if (window.attachEvent && !window.addEventListener) {
+ // Internet Explorer needs to remove event handlers on page unload
+ // in order to avoid memory leaks.
+ window.attachEvent("onunload", purgeListeners);
+
+ // IE also doesn't fire the unload event if the page is navigated away
+ // from before it's done loading. Workaround adapted from
+ // http://blog.moxiecode.com/2008/04/08/unload-event-never-fires-in-ie/.
+ window.attachEvent("onbeforeunload", onBeforeUnload);
+
+ // Ensure window onload is fired after "dom:loaded"
+ addEventDispatcher(window, 'load', function(proceed, event) {
+ if (document.loaded) {
+ proceed(event);
+ } else {
+ arguments.callee.defer(proceed, event);
+ }
+ });
+
+ // Ensure window onresize is fired only once per resize
+ addEventDispatcher(window, 'resize', function(proceed, event) {
+ var callee = arguments.callee, dimensions = document.viewport.getDimensions();
+ if (dimensions.width != callee.prevWidth || dimensions.height != callee.prevHeight) {
+ callee.prevWidth = dimensions.width;
+ callee.prevHeight = dimensions.height;
+ proceed(event);
+ }
+ });
+ }
+
+ // Safari has a dummy event handler on page unload so that it won't
+ // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+ // object when page is returned to via the back button using its bfcache.
+ else if (Prototype.Browser.WebKit) {
+ window.addEventListener("unload", Prototype.emptyFunction, false);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ addEventDispatcher(element, eventName);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ eventName = Object.isString(eventName) ? eventName : null;
+ var id = getEventID(element), c = cache[id];
+
+ if (!c) {
+ return element;
+ }
+ else if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ Event.stopObserving(element, eventName, wrapper.handler);
+ });
+ return element;
+ }
+ else if (!eventName) {
+ Object.keys(c).without("element").each(function(eventName) {
+ Event.stopObserving(element, eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ var name = getDOMEventName(eventName);
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ destroyWrapper(id, eventName, handler);
+ } else {
+ destroyWrapper(id, eventName, handler);
+ var wrappers = getWrappersForEventName(id, eventName);
+ if (!wrappers.length) {
+ element.detachEvent("on" + name, wrappers.dispatcher);
+ wrappers.dispatcher = null;
+ }
+ }
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards, John Resig and Diego Perini. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.loaded = true;
+ document.fire("dom:loaded");
+ }
+
+ if (document.addEventListener) {
+ document.addEventListener("DOMContentLoaded", fireContentLoadedEvent, false);
+ } else {
+ document.attachEvent("onreadystatechange", function() {
+ if (document.readyState == "complete") {
+ document.detachEvent("onreadystatechange", arguments.callee);
+ fireContentLoadedEvent();
+ }
+ });
+
+ if (window == top) {
+ timer = setInterval(function() {
+ try {
+ document.documentElement.doScroll("left");
+ } catch(e) { return }
+ fireContentLoadedEvent();
+ }, 10);
+ }
+ }
+
+
+ // Safari <3.1 doesn't support DOMContentLoaded
+ if (Prototype.Browser.WebKit && (navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1] < 525)) {
+ timer = setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 10);
+ }
+
+ // Worst case fallback...
+ Event.observe(window, "load", fireContentLoadedEvent);
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+
+ var dimensions = Element.getDimensions(element);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + dimensions.height &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + dimensions.width);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element),
+ dimensions = Element.getDimensions(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + dimensions.height &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + dimensions.width);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ var dimensions = Element.getDimensions(element);
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + dimensions.height) - this.ycomp) /
+ dimensions.height;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + dimensions.width) - this.xcomp) /
+ dimensions.width;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods(); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/dist/prototype_update_helper.js b/dom/tests/mochitest/ajax/prototype/dist/prototype_update_helper.js
new file mode 100644
index 0000000000..0f4b949381
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/dist/prototype_update_helper.js
@@ -0,0 +1,417 @@
+/* Update Helper (c) 2008 Tobie Langel
+ *
+ * Requires Prototype >= 1.6.0
+ *
+ * Update Helper is distributable under the same terms as Prototype
+ * (MIT-style license). For details, see the Prototype web site:
+ * http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var UpdateHelper = Class.create({
+ logLevel: 0,
+ MessageTemplate: new Template('Update Helper: #{message}\n#{stack}'),
+ Regexp: new RegExp("@" + window.location.protocol + ".*?\\d+\\n", "g"),
+
+ initialize: function(deprecatedMethods) {
+ var notify = function(message, type) {
+ this.notify(message, type);
+ }.bind(this); // Late binding to simplify testing.
+
+ deprecatedMethods.each(function(d) {
+ var condition = d.condition,
+ type = d.type || 'info',
+ message = d.message,
+ namespace = d.namespace,
+ method = d.methodName;
+
+ namespace[method] = (namespace[method] || function() {}).wrap(function(proceed) {
+ var arr = $A(arguments);
+ var args = arr.splice(1, arr.length);
+ if (!condition || condition.apply(this, args)) notify(message, type);
+ return proceed.apply(proceed, args);
+ });
+ });
+ Element.addMethods();
+ },
+
+ notify: function(message, type) {
+ switch(type) {
+ case 'info':
+ if (this.logLevel > UpdateHelper.Info) return false;
+ case 'warn':
+ if (this.logLevel > UpdateHelper.Warn) return false;
+ default:
+ if (this.logLevel > UpdateHelper.Error) return false;
+ }
+ this.log(this.MessageTemplate.evaluate({
+ message: message,
+ stack: this.getStack()
+ }), type);
+ return true;
+ },
+
+ getStack: function() {
+ try {
+ throw new Error("stack");
+ } catch(e) {
+ return (e.stack || '').match(this.Regexp).reject(function(path) {
+ return /(prototype|unittest|update_helper)\.js/.test(path);
+ }).join("\n");
+ }
+ },
+
+ log: function(message, type) {
+ if (type == 'error') console.error(message);
+ else if (type == 'warn') console.warn(message);
+ else console.log(message);
+ }
+});
+
+Object.extend(UpdateHelper, {
+ Info: 0,
+ Warn: 1,
+ Error: 2
+});
+
+/* UpdateHelper for Prototype 1.6.0.2 (c) 2008 Tobie Langel
+ *
+ * UpdateHelper for Prototype is freely distributable under the same
+ * terms as Prototype (MIT-style license).
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ * Include this file right below prototype.js. All messages
+ * will be logged to the console.
+ *
+ * Note: You can tune the level of warning by redefining
+ * prototypeUpdateHelper.logLevel with one of the appropriate constansts
+ * (UpdateHelper.Info, UpdateHelper.Warn or UpdateHelper.Error), e.g.:
+ *
+ * prototypeUpdateHelper.logLevel = UpdateHelper.Warn;
+ *
+ * This, for example, will prevent deprecation messages from being logged.
+ *
+ * THIS SCRIPT WORKS IN FIREFOX ONLY
+ *--------------------------------------------------------------------------*/
+
+var prototypeUpdateHelper = new UpdateHelper([
+ {
+ methodName: 'display',
+ namespace: Toggle,
+ message: 'Toggle.display has been deprecated, please use Element.toggle instead.'
+ },
+
+ {
+ methodName: 'show',
+ namespace: Element.Methods,
+ message: 'Passing an arbitrary number of elements to Element.show is no longer supported.\n' +
+ 'Use [id_1, id_2, ...].each(Element.show) or $(id_1, id_2, ...).invoke("show") instead.',
+ type: 'error',
+ condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
+ },
+
+ {
+ methodName: 'hide',
+ namespace: Element.Methods,
+ message: 'Passing an arbitrary number of elements to Element.hide is no longer supported.\n' +
+ 'Use [id_1, id_2, ...].each(Element.hide) or $(id_1, id_2, ...).invoke("hide") instead.',
+ type: 'error',
+ condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
+ },
+
+ {
+ methodName: 'toggle',
+ namespace: Element.Methods,
+ message: 'Passing an arbitrary number of elements to Element.toggle is no longer supported.\n' +
+ 'Use [id_1, id_2, ...].each(Element.toggle) or $(id_1, id_2, ...).invoke("toggle") instead.',
+ type: 'error',
+ condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
+ },
+
+ {
+ methodName: 'clear',
+ namespace: Form.Element.Methods,
+ message: 'Passing an arbitrary number of elements to Field.clear is no longer supported.\n' +
+ 'Use [id_1, id_2, ...].each(Form.Element.clear) or $(id_1, id_2, ...).invoke("clear") instead.',
+ type: 'error',
+ condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
+ },
+
+ {
+ methodName: 'present',
+ namespace: Form.Element.Methods,
+ message: 'Passing an arbitrary number of elements to Field.present is no longer supported.\n' +
+ 'Use [id_1, id_2, ...].each(Form.Element.present) or $(id_1, id_2, ...).invoke("present") instead.',
+ type: 'error',
+ condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
+ },
+
+ {
+ methodName: 'childOf',
+ namespace: Element.Methods,
+ message: 'Element#childOf has been deprecated, please use Element#descendantOf instead.'
+ },
+
+ {
+ methodName: 'Before',
+ namespace: Insertion,
+ message: 'Insertion.Before has been deprecated, please use Element#insert instead.'
+ },
+
+ {
+ methodName: 'Top',
+ namespace: Insertion,
+ message: 'Insertion.Top has been deprecated, please use Element#insert instead.'
+ },
+
+ {
+ methodName: 'Bottom',
+ namespace: Insertion,
+ message: 'Insertion.Bottom has been deprecated, please use Element#insert instead.'
+ },
+
+ {
+ methodName: 'After',
+ namespace: Insertion,
+ message: 'Insertion.After has been deprecated, please use Element#insert instead.'
+ },
+
+ {
+ methodName: 'prepare',
+ namespace: Position,
+ message: 'Position.prepare has been deprecated.'
+ },
+
+ {
+ methodName: 'within',
+ namespace: Position,
+ message: 'Position.within has been deprecated.'
+ },
+
+ {
+ methodName: 'withinIncludingScrolloffsets',
+ namespace: Position,
+ message: 'Position.withinIncludingScrolloffsets has been deprecated.'
+ },
+
+ {
+ methodName: 'overlap',
+ namespace: Position,
+ message: 'Position.overlap has been deprecated.'
+ },
+
+ {
+ methodName: 'cumulativeOffset',
+ namespace: Position,
+ message: 'Position.cumulativeOffset has been deprecated, please use Element#cumulativeOffset instead.'
+ },
+
+ {
+ methodName: 'positionedOffset',
+ namespace: Position,
+ message: 'Position.positionedOffset has been deprecated, please use Element#positionedOffset instead.'
+ },
+
+ {
+ methodName: 'absolutize',
+ namespace: Position,
+ message: 'Position.absolutize has been deprecated, please use Element#absolutize instead.'
+ },
+
+ {
+ methodName: 'relativize',
+ namespace: Position,
+ message: 'Position.relativize has been deprecated, please use Element#relativize instead.'
+ },
+
+ {
+ methodName: 'realOffset',
+ namespace: Position,
+ message: 'Position.realOffset has been deprecated, please use Element#cumulativeScrollOffset instead.'
+ },
+
+ {
+ methodName: 'offsetParent',
+ namespace: Position,
+ message: 'Position.offsetParent has been deprecated, please use Element#getOffsetParent instead.'
+ },
+
+ {
+ methodName: 'page',
+ namespace: Position,
+ message: 'Position.page has been deprecated, please use Element#viewportOffset instead.'
+ },
+
+ {
+ methodName: 'clone',
+ namespace: Position,
+ message: 'Position.clone has been deprecated, please use Element#clonePosition instead.'
+ },
+
+ {
+ methodName: 'initialize',
+ namespace: Element.ClassNames.prototype,
+ message: 'Element.ClassNames has been deprecated.'
+ },
+
+ {
+ methodName: 'classNames',
+ namespace: Element.Methods,
+ message: 'Element#classNames has been deprecated.\n' +
+ 'If you need to access CSS class names as an array, try: $w(element.classname).'
+ },
+
+ {
+ methodName: 'setStyle',
+ namespace: Element.Methods,
+ message: 'Use of uncamelized style-property names is no longer supported.\n' +
+ 'Use either camelized style-property names or a regular CSS string instead (see online documentation).',
+ type: 'error',
+ condition: function(element, style) {
+ return !Object.isString(style) && Object.keys(style).join('').include('-');
+ }
+ },
+
+ {
+ methodName: 'getElementsByClassName',
+ namespace: document,
+ message: 'document.getElementsByClassName has been deprecated, please use $$ instead.'
+ },
+
+ {
+ methodName: 'getElementsByClassName',
+ namespace: Element.Methods,
+ message: 'Element#getElementsByClassName has been deprecated, please use Element#select instead.'
+ },
+
+ {
+ methodName: 'immediateDescendants',
+ namespace: Element.Methods,
+ message: 'Element#immediateDescendants has been deprecated, please use Element#childElements instead.'
+ },
+
+ {
+ methodName: 'getElementsBySelector',
+ namespace: Element.Methods,
+ message: 'Element#getElementsBySelector has been deprecated, please use Element#select instead.'
+ },
+
+ {
+ methodName: 'toQueryString',
+ namespace: Hash,
+ message: 'Hash.toQueryString has been deprecated.\n' +
+ 'Use the instance method Hash#toQueryString or Object.toQueryString instead.'
+ },
+
+ {
+ methodName: 'toJSON',
+ namespace: Hash,
+ message: 'Hash.toJSON has been removed.\n' +
+ 'Use the instance method Hash#toJSON or Object.toJSON instead.',
+ type: 'error'
+ },
+
+ {
+ methodName: 'remove',
+ namespace: Hash.prototype,
+ message: 'Hash#remove is no longer supported, use Hash#unset instead.\n' +
+ 'Please note that Hash#unset only accepts one argument.',
+ type: 'error'
+ },
+
+ {
+ methodName: 'merge',
+ namespace: Hash.prototype,
+ message: 'Hash#merge is no longer destructive and now operates on a clone of the Hash instance.\n' +
+ 'If you need a destructive merge, use Hash#update instead.',
+ type: 'warn'
+ },
+
+ {
+ methodName: 'unloadCache',
+ namespace: Event,
+ message: 'Event.unloadCache has been deprecated.',
+ type: 'error'
+ },
+
+ {
+ methodName: 'create',
+ namespace: Class,
+ message: 'The class API has been fully revised and now allows for mixins and inheritance.\n' +
+ 'You can find more about it here: http://prototypejs.org/learn/class-inheritance',
+ condition: function() { return !arguments.length }
+ }
+]);
+
+// Special casing for Hash.
+
+(function() {
+ var __properties = Object.keys(Hash.prototype).concat(['_object', '__properties']);
+
+ var messages = {
+ setting: new Template("Directly setting a property on an instance of Hash is no longer supported.\n" +
+ "Please use Hash#set('#{property}', #{value}) instead."),
+ getting: new Template("Directly accessing a property of an instance of Hash is no longer supported.\n" +
+ "Please use Hash#get('#{property}') instead.")
+ };
+
+ function notify(property, value) {
+ var message = messages[arguments.length == 1 ? 'getting' : 'setting'].evaluate({
+ property: property,
+ value: Object.inspect(value)
+ });
+ prototypeUpdateHelper.notify(message, 'error');
+ }
+
+ function defineSetters(obj, prop) {
+ if (obj.__properties.include(prop)) return;
+ obj.__properties.push(prop);
+ obj.__defineGetter__(prop, function() {
+ checkProperties(this);
+ notify(prop);
+ });
+ obj.__defineSetter__(prop, function(value) {
+ checkProperties(this);
+ notify(prop, value);
+ });
+ }
+
+ function checkProperties(hash) {
+ var current = Object.keys(hash);
+ if (current.length == hash.__properties.length)
+ return;
+ current.each(function(prop) {
+ if (hash.__properties.include(prop)) return;
+ notify(prop, hash[prop]);
+ defineSetters(hash, prop);
+ });
+ }
+
+ Hash.prototype.set = Hash.prototype.set.wrap(function(proceed, property, value) {
+ defineSetters(this, property);
+ return proceed(property, value);
+ });
+
+ $w('merge update').each(function(name) {
+ Hash.prototype[name] = Hash.prototype[name].wrap(function(proceed, object) {
+ for (var prop in object) defineSetters(this, prop);
+ return proceed(object);
+ });
+ });
+
+ $H(Hash.prototype).each(function(method) {
+ var key = method.key;
+ if (!Object.isFunction(method.value) || key == 'initialize') return;
+ Hash.prototype[key] = Hash.prototype[key].wrap(function(proceed) {
+ checkProperties(this);
+ var arr = $A(arguments);
+ return proceed.apply(proceed, arr.splice(1, arr.length));
+ });
+ });
+
+ Hash.prototype.initialize = Hash.prototype.initialize.wrap(function(proceed, object) {
+ this.__properties = __properties.clone();
+ for (var prop in object) defineSetters(this, prop);
+ proceed(object);
+ });
+})();
diff --git a/dom/tests/mochitest/ajax/prototype/manifest.json b/dom/tests/mochitest/ajax/prototype/manifest.json
new file mode 100644
index 0000000000..1beb8a7079
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/manifest.json
@@ -0,0 +1 @@
+{testcases:["test/unit/tmp/ajax_test.html","test/unit/tmp/array_test.html","test/unit/tmp/base_test.html","test/unit/tmp/dom_test.html","test/unit/tmp/element_mixins_test.html","test/unit/tmp/enumerable_test.html","test/unit/tmp/event_test.html","test/unit/tmp/form_test.html","test/unit/tmp/hash_test.html","test/unit/tmp/number_test.html","test/unit/tmp/position_test.html","test/unit/tmp/range_test.html","test/unit/tmp/selector_test.html","test/unit/tmp/string_test.html", "test/unit/tmp/unit_test.html"]} \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/mochitest.ini b/dom/tests/mochitest/ajax/prototype/mochitest.ini
new file mode 100644
index 0000000000..6af573fad6
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/mochitest.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+support-files =
+ manifest.json
+ dist/prototype.js
+ test/browser.html
+ test/console.html
+ test/test.css
+ test/functional/event.html
+ test/lib/unittest.js
+ test/unit/ajax_test.js
+ test/unit/array_test.js
+ test/unit/base_test.js
+ test/unit/dom_test.js
+ test/unit/element_mixins_test.js
+ test/unit/enumerable_test.js
+ test/unit/event_test.js
+ test/unit/form_test.js
+ test/unit/hash_test.js
+ test/unit/number_test.js
+ test/unit/position_test.js
+ test/unit/range_test.js
+ test/unit/selector_test.js
+ test/unit/string_test.js
+ test/unit/unittest_test.js
+ test/unit/fixtures/ajax.html
+ test/unit/fixtures/ajax.js
+ test/unit/fixtures/array.html
+ test/unit/fixtures/base.html
+ test/unit/fixtures/base.js
+ test/unit/fixtures/content.html
+ test/unit/fixtures/data.json
+ test/unit/fixtures/dom.css
+ test/unit/fixtures/dom.html
+ test/unit/fixtures/dom.js
+ test/unit/fixtures/element_mixins.html
+ test/unit/fixtures/element_mixins.js
+ test/unit/fixtures/empty.html
+ test/unit/fixtures/empty.js
+ test/unit/fixtures/enumerable.html
+ test/unit/fixtures/enumerable.js
+ test/unit/fixtures/event.html
+ test/unit/fixtures/form.html
+ test/unit/fixtures/hash.js
+ test/unit/fixtures/hello.js
+ test/unit/fixtures/logo.gif
+ test/unit/fixtures/position.html
+ test/unit/fixtures/selector.html
+ test/unit/fixtures/string.js
+ test/unit/fixtures/unittest.html
+ test/unit/tmp/ajax_test.html
+ test/unit/tmp/array_test.html
+ test/unit/tmp/base_test.html
+ test/unit/tmp/dom_test.html
+ test/unit/tmp/element_mixins_test.html
+ test/unit/tmp/enumerable_test.html
+ test/unit/tmp/event_test.html
+ test/unit/tmp/form_test.html
+ test/unit/tmp/hash_test.html
+ test/unit/tmp/number_test.html
+ test/unit/tmp/position_test.html
+ test/unit/tmp/range_test.html
+ test/unit/tmp/selector_test.html
+ test/unit/tmp/string_test.html
+ test/unit/tmp/unit_test.html
+
+[test_Prototype.html]
+skip-if = toolkit == 'android'
diff --git a/dom/tests/mochitest/ajax/prototype/test/browser.html b/dom/tests/mochitest/ajax/prototype/test/browser.html
new file mode 100644
index 0000000000..ec699f9d45
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/browser.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title>Prototype object browser</title>
+ <style type="text/css" media="screen">
+ body {
+ font-family: Lucida Grande, Verdana, sans-serif;
+ font-size: 13px;
+ }
+
+ .inspector {
+ margin: 1%;
+ float: left;
+ width: 31%;
+ border: 1px solid #ccc;
+ background-color: white;
+ }
+
+ .inspector h2 {
+ font-size: 13px;
+ margin: 0;
+ text-align: center;
+ padding: 5px;
+ background-color: #e6e6e6;
+ border-bottom: 1px solid #999;
+ }
+
+ .inspector ul {
+ height: 200px;
+ overflow: auto;
+ margin: 0;
+ padding-left: 0;
+ }
+
+ .inspector li {
+ cursor: pointer;
+ list-style-type: none;
+ padding: 2px 5px 2px 30px;
+ color: #333;
+ }
+
+ .inspector li.selected {
+ background-color: #888;
+ color: #fff;
+ }
+
+ .inspector.active li.selected {
+ background-color: #1a76fd;
+ color: #fff;
+ }
+
+ #path, #value {
+ width: 97%;
+ margin: 1%;
+ }
+
+ #path {
+ margin-bottom: 0;
+ border: 1px solid #ccc;
+ border-bottom: 1px solid #999;
+ background-color: #e6e6e6;
+ }
+
+ #value {
+ margin-top: 0;
+ border: 1px solid #ccc;
+ border-top: none;
+ overflow: auto;
+ }
+
+ #path_content, #value_content {
+ display: block;
+ padding: 15px 30px 15px 30px;
+ }
+
+ </style>
+ <script type="text/javascript" src="../dist/prototype.js"></script>
+ <script type="text/javascript">
+ var Browser = Class.create();
+ Browser.prototype = {
+ initialize: function(element, name, value, options) {
+ this.element = $(element);
+ this.name = name;
+ this.value = value;
+ this.history = [];
+ Object.extend(this, options || {});
+ this.reset();
+ },
+
+ reset: function() {
+ this.go(this.name, this.value);
+ },
+
+ refresh: function() {
+ var children = $A(this.element.childNodes),
+ history = this.history.toArray(),
+ elements = history.slice(-3).pluck('element');
+
+ children.each(function(element) {
+ if (element && !elements.include(element))
+ this.element.removeChild(element);
+ }.bind(this));
+
+ children = $A(this.element.childNodes);
+
+ elements.each(function(element, index) {
+ Element.removeClassName(element, 'active');
+ var child = children[index];
+ if (!child)
+ this.element.appendChild(element);
+ else if (!element.parentNode)
+ this.element.insertBefore(element, child);
+ }.bind(this));
+
+ this.setTitle();
+ this.setValue();
+ },
+
+ setTitle: function() {
+ if (this.titleElement)
+ this.titleElement.innerHTML =
+ this.history.pluck('name').invoke('escapeHTML').join('.');
+ },
+
+ setValue: function() {
+ if (this.valueElement)
+ this.valueElement.innerHTML =
+ this.currentValue().escapeHTML() + '&nbsp;';
+ },
+
+ currentValue: function() {
+ try {
+ return Object.inspect(this.current());
+ } catch (e) {
+ return '(Internal Function)';
+ }
+ },
+
+ current: function() {
+ return this.history.last().value;
+ },
+
+ go: function(name, value) {
+ var from = this.history.last();
+ this.history.push(new Inspector(this, name, value));
+ this.refresh();
+ if (from)
+ Element.addClassName(from.element, 'active');
+ }
+ }
+
+ var Inspector = Class.create();
+ Inspector.prototype = {
+ initialize: function(browser, name, value) {
+ this.browser = browser;
+ this.name = name;
+ this.value = value;
+ this.id = 'inspector_' + new Date().getTime();
+ this.history = this.browser.history.toArray();
+ this.history.push(this);
+ this.createElement();
+ this.populate();
+ },
+
+ properties: function() {
+ var properties = [];
+ for (var property in this.value)
+ properties.push(property);
+ properties.sort();
+ return properties;
+ },
+
+ createElement: function() {
+ var element = document.createElement('div');
+ element.className = 'inspector';
+ element.id = this.id;
+ this.element = element;
+
+ var title = document.createElement('h2');
+ title.innerHTML = this.name.toString().escapeHTML();
+ this.titleElement = title;
+
+ var list = document.createElement('ul');
+ this.listElement = list;
+
+ element.appendChild(title);
+ element.appendChild(list);
+ },
+
+ populate: function() {
+ this.properties().each(function(property) {
+ var li = document.createElement('li');
+ li.innerHTML = property.toString().escapeHTML();
+ li.onclick = this.select.bind(this, li);
+ li._property = property;
+ this.listElement.appendChild(li);
+ }.bind(this));
+ },
+
+ select: function(element) {
+ this.unselect();
+ Element.addClassName(element, 'selected');
+ this.selectedProperty = element;
+ this.browser.history = this.history.toArray();
+ this.browser.go(element._property, this.value[element._property]);
+ },
+
+ unselect: function() {
+ if (this.selectedProperty)
+ Element.removeClassName(this.selectedProperty, 'selected');
+ this.selectedProperty = null;
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <div id="browser_wrapper">
+ <div id="browser"></div>
+ <div style="clear: left"></div>
+ </div>
+ <h1 id="path"><span id="path_content"></span></h1>
+ <pre id="value"><div id="value_content"></div></pre>
+ <script type="text/javascript">
+ new Browser('browser', 'window', window, {titleElement: $('path_content'), valueElement: $('value_content')})
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/console.html b/dom/tests/mochitest/ajax/prototype/test/console.html
new file mode 100644
index 0000000000..2b586ee426
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/console.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <title>Prototype Console</title>
+ <script src="../dist/prototype.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ Prototype.Console = Class.create();
+ Prototype.Console.prototype = {
+ initialize: function(element, form, input) {
+ this.element = $(element);
+ this.form = $(form);
+ this.input = $(input);
+ this.context = window.eval.bind(window);
+ this.registerCallbacks();
+ document.title = 'Prototype Console ' + Prototype.Version;
+ Field.activate(this.input);
+ },
+
+ registerCallbacks: function() {
+ Event.observe(this.form, 'submit', function(event) {
+ this.eval($F(this.input));
+ this.input.value = '';
+ Field.activate(this.input);
+ Event.stop(event);
+ }.bind(this));
+ },
+
+ log: function(type, message) {
+ new Insertion.Bottom(this.element,
+ '<tr class="' + type + '"><td>' +
+ message.escapeHTML() + '</td></tr>');
+ Element.scrollTo(this.form);
+ },
+
+ eval: function(expression) {
+ if (expression.match(/^\s*$/)) return;
+ try {
+ this.log('input', expression);
+ window.$_ = this.context.call(window, expression);
+ this.log('output', Object.inspect($_));
+ } catch (e) {
+ this.log('error', e.toString());
+ }
+ },
+
+ clear: function() {
+ this.element.innerHTML = '';
+ }
+ }
+ </script>
+ <style type="text/css">
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ .console {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 50px;
+ }
+
+ .console td {
+ padding: 5px;
+ font-family: monospace;
+ font-size: 14px;
+ }
+
+ .console tr.input td {
+ background-color: #eee;
+ font-weight: bold;
+ }
+
+ .console tr.error td,
+ .console tr.output td {
+ color: #333;
+ border-bottom: 1px solid #ccc;
+ }
+
+ .console tr.error td {
+ color: #f00;
+ }
+
+ #input-form {
+ width: 100%;
+ background-color: #f0f5b8;
+ border-top: 1px solid #333;
+ padding: 10px;
+ position: fixed;
+ height: 25px;
+ bottom: 0;
+ margin: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <table class="console">
+ <tbody id="console">
+ </tbody>
+ </table>
+ <form id="input-form">
+ <input type="text" size="60" id="input" />
+ <input type="submit" value="Evaluate" />
+ </form>
+ <script type="text/javascript">
+ window.console = new Prototype.Console('console', 'input-form', 'input');
+ </script>
+ </body>
+</html>
diff --git a/dom/tests/mochitest/ajax/prototype/test/functional/event.html b/dom/tests/mochitest/ajax/prototype/test/functional/event.html
new file mode 100644
index 0000000000..526e711f9a
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/functional/event.html
@@ -0,0 +1,243 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype functional test file</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script src="../../dist/prototype.js" type="text/javascript"></script>
+
+ <style type="text/css" media="screen">
+ /* <![CDATA[ */
+ body { margin:1em 2em; padding:0; font-size:0.8em }
+ hr { width:31.2em; margin:1em 0; text-align:left }
+ p { width:30em; margin:0.5em 0; padding:0.3em 0.6em; color:#222; background:#eee; border:1px solid silver; }
+ .subtest { margin-top:-0.5em }
+ .passed { color:green; border-color:olive }
+ .failed { color:firebrick; border-color:firebrick }
+ .button { padding:0.2em 0.4em; background:#ccc; border:1px solid #aaa }
+ #log { position:absolute; left:35em; top:5em; width:20em; font-size:13px !important }
+ h2 { font:normal 1.1em Verdana,Arial,sans-serif; font-style:italic; color:gray; margin-top:-1.2em }
+ h2 *, h2 a:visited { color:#444 }
+ h2 a:hover { color:blue }
+ a:visited { color:blue }
+ a:hover { color:red }
+ /* ]]> */
+ </style>
+
+ <script type="text/javascript">
+ Element.addMethods({
+ passed: function(el, message) {
+ el = $(el);
+ el.className = 'passed';
+ (el.down('span') || el).update(message || 'Test passed!');
+ },
+
+ failed: function(el, message) {
+ el = $(el);
+ el.className = 'failed';
+ (el.down('span') || el).update(message || 'Test failed');
+ }
+ });
+
+ function log(obj) {
+ var line, all = [];
+ for (prop in obj) {
+ if (typeof obj[prop] == 'function' || /^[A-Z]|[XY]$/.test(prop)) continue;
+ line = prop + ": " + Object.inspect(obj[prop]);
+ all.push(line.escapeHTML());
+ }
+ $('log').update(all.join('<br />'));
+ }
+ </script>
+</head>
+<body>
+ <h1>Prototype functional tests for the Event module</h1>
+
+ <div id="log">log empty</div>
+
+ <p id="basic">A basic event test - <strong>click here</strong></p>
+ <p id="basic_remove" class="subtest"><strong>click</strong> to stop observing the first test</p>
+
+ <p id="inline_test" onclick="Event.stop(event); $(this).passed();"><strong>click</strong> to ensure generic Event methods work on inline handlers</p>
+
+ <script type="text/javascript">
+ var basic_callback = function(e){
+ $('basic').passed();
+ if ($('basic_remove')) $('basic_remove').show()
+ else $('basic').failed()
+ log(e);
+ }
+ $('basic').observe('click', basic_callback)
+ $('basic_remove').observe('click', function(e){
+ el = $('basic')
+ el.passed('This test should now be inactive (try clicking)')
+ el.stopObserving('click')
+ $('basic_remove').remove()
+ log(e);
+ }).hide()
+ </script>
+
+ <p id="basic2"><strong>Scope</strong> test - scope of the handler should be this element</p>
+
+ <script type="text/javascript">
+ $('basic2').observe('click', function(e) {
+ if (this === window) $('basic2').failed('Window scope! (needs scope correction)');
+ else this.passed();
+ log(e);
+ });
+ </script>
+
+ <p id="basic3"><strong>Event object</strong> test - should be present as a first argument</p>
+
+ <script type="text/javascript">
+ $('basic3').observe('click', function(evt) {
+ el = $('basic3');
+ if (typeof evt != 'object') this.failed('Expected event object for first argument');
+ else this.passed('Good first argument');
+ log(evt);
+ });
+ </script>
+
+ <p><a href="#wrong" id="hijack">Hijack link test</a> (preventDefault)</p>
+
+ <script type="text/javascript">
+ $('hijack').observe('click', function(e){
+ el = $(this.parentNode);
+ log(e); // this makes it fail?!?
+ e.preventDefault();
+
+ setTimeout(function() {
+ if (window.location.hash == '#wrong') el.failed('Hijack failed (<a href="' +
+ window.location.toString().replace(/#.+$/, '') + '">remove the fragment</a>)')
+ else el.passed();
+ }, 50)
+ })
+ </script>
+
+ <hr />
+
+
+ <p>Mouse click:
+ <span class="button" id="left">left</span> <span class="button" id="middle">middle</span> <span class="button" id="right">right</span></p>
+
+ <script type="text/javascript">
+ $w('left middle right').each(function(button) {
+ Event.observe(button, 'mousedown', function(e) {
+ if (Event['is' + this.id.capitalize() + 'Click'](e)) this.passed('Squeak!')
+ else this.failed('OH NO!');
+ log(e);
+ });
+ });
+ </script>
+
+ <p id="context">Context menu event (tries to prevent default)</p>
+
+ <script type="text/javascript">
+ $('context').observe('contextmenu', function(e){
+ this.passed();
+ Event.stop(e);
+ log(e);
+ })
+ </script>
+
+ <p id="target">Event.element() test</p>
+
+ <script type="text/javascript">
+ $('target').observe('click', function(e) {
+ if (e.element() == this && e.target == this) this.passed();
+ else this.failed();
+ log(e);
+ });
+ </script>
+
+ <p id="currentTarget"><span>Event.currentTarget test</span></p>
+
+ <script type="text/javascript">
+ $('currentTarget').observe('click', function(e){
+ if (e.currentTarget !== this) this.failed();
+ else this.passed();
+ log(e);
+ })
+ </script>
+
+ <p id="findElement"><span>Event.findElement() test</span></p>
+
+ <script type="text/javascript">
+ $('findElement').observe('click', function(e){
+ if (e.findElement('p') == this && e.findElement('body') == document.body &&
+ e.findElement('foo') == null) this.passed();
+ else this.failed();
+ log(e);
+ })
+ </script>
+
+ <div id="container"><p id="stop"><strong>Stop propagation</strong> test (bubbling)</p></div>
+
+ <script type="text/javascript">
+ $('stop').observe('click', function(e){
+ e.stop();
+ this.passed();
+ log(e);
+ })
+ $('container').observe('click', function(e){
+ $('stop').failed();
+ log(e);
+ })
+ </script>
+
+ <div>
+ <p id="keyup_log"><strong>Keyup</strong> test - focus on the textarea and type</p>
+ <textarea id="keyup" class="subtest"></textarea>
+ </div>
+
+ <script type="text/javascript">
+ $('keyup').observe('keyup', function(e){
+ el = $('keyup_log');
+ el.passed('Key captured: the length is ' + $('keyup').value.length);
+ log(e);
+ })
+ </script>
+
+ <p id="bind"><code>bindAsEventListener()</code> test</p>
+
+ <script type="text/javascript">
+ $('bind').observe('click', function(e, str, arr){
+ el = $('bind')
+ try {
+ if (arguments.length != 3) throw arguments.length + ' arguments: ' + $A(arguments).inspect()
+ if (str != 'foo') throw 'wrong string: ' + str
+ if (arr.constructor != Array) throw '3rd parameter is not an array'
+ el.passed();
+ }
+ catch (err) { el.failed(err.toString()) }
+ log(e);
+ }.bindAsEventListener(document.body, 'foo', [1,2,3]))
+ </script>
+
+ <p id="obj_inspect"><code>Object.inspect(event)</code> test</p>
+
+ <script type="text/javascript">
+ $('obj_inspect').observe('click', function(e){
+ el = $('obj_inspect')
+ try { el.passed(Object.inspect(e)) }
+ catch (err) { el.failed('Failed! Error thrown') }
+ log(e);
+ })
+ </script>
+
+ <p id="addunload">Add unload events</p>
+
+ <script type="text/javascript">
+ $('addunload').observe('click', function(e){
+ if (this._done) return
+
+ window.onunload = function(){ alert('inline unload fired!') }
+ Event.observe(window, 'unload', function(){ alert('observed unload fired!') })
+
+ this.update('Registered two unload events, one inline ("onunload") and one regular - try to refresh, both should fire')
+ this._done = true
+ log(e);
+ })
+ </script>
+</body>
+</html>
diff --git a/dom/tests/mochitest/ajax/prototype/test/lib/unittest.js b/dom/tests/mochitest/ajax/prototype/test/lib/unittest.js
new file mode 100644
index 0000000000..4f92d13dc0
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/lib/unittest.js
@@ -0,0 +1,602 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+ var options = Object.extend({
+ pointerX: 0,
+ pointerY: 0,
+ buttons: 0
+ }, arguments[2] || {});
+ var oEvent = document.createEvent("MouseEvents");
+ oEvent.initMouseEvent(eventName, true, true, document.defaultView,
+ options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
+ false, false, false, false, 0, $(element));
+
+ if (this.mark) Element.remove(this.mark);
+
+ var style = 'position: absolute; width: 5px; height: 5px;' +
+ 'top: #{pointerY}px; left: #{pointerX}px;'.interpolate(options) +
+ 'border-top: 1px solid red; border-left: 1px solid red;'
+
+ this.mark = new Element('div', { style: style });
+ this.mark.appendChild(document.createTextNode(" "));
+ document.body.appendChild(this.mark);
+
+ if (this.step)
+ alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+
+ $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+ var options = Object.extend({
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ keyCode: 0,
+ charCode: 0
+ }, arguments[2] || {});
+
+ var oEvent = document.createEvent("KeyboardEvent");
+ oEvent.initKeyEvent(eventName, true, true, window,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode );
+ $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+ for (var i=0; i<command.length; i++) {
+ Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+ }
+};
+
+var Test = {
+ Unit: {
+ inspect: Object.inspect // security exception workaround
+ }
+};
+
+Test.Unit.Logger = Class.create({
+ initialize: function(element) {
+ this.element = $(element);
+ if (this.element) this._createLogTable();
+ this.tbody = $(this.element.getElementsByTagName('tbody')[0]);
+ },
+
+ start: function(testName) {
+ this.testName = testName;
+ if (!this.element) return;
+ this.tbody.insert('<tr><td>' + testName + '</td><td></td><td></td></tr>');
+ },
+
+ setStatus: function(status) {
+ this.getLastLogLine().addClassName(status);
+ $(this.getLastLogLine().getElementsByTagName('td')[1]).update(status);
+ },
+
+ finish: function(status, summary) {
+ if (!this.element) return;
+ this.setStatus(status);
+ this.message(summary);
+ },
+
+ message: function(message) {
+ if (!this.element) return;
+ this.getMessageCell().update(this._toHTML(message));
+ },
+
+ summary: function(summary) {
+ if (!this.element) return;
+ var div = $(this.element.getElementsByTagName('div')[0]);
+ div.update(this._toHTML(summary));
+ },
+
+ getLastLogLine: function() {
+ //return this.element.descendants('tr').last();
+ var trs = this.element.getElementsByTagName('tr');
+ return $(trs[trs.length - 1]);
+ },
+
+ getMessageCell: function() {
+ return this.getLastLogLine().down('td', 2);
+ var tds = this.getLastLogLine().getElementsByTagName('td');
+ return $(tds[2]);
+ },
+
+ _createLogTable: function() {
+ var html = '<div class="logsummary">running...</div>' +
+ '<table class="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody class="loglines"></tbody>' +
+ '</table>';
+ this.element.update(html);
+
+ },
+
+ appendActionButtons: function(actions) {
+ actions = $H(actions);
+ if (!actions.any()) return;
+ var div = new Element("div", {className: 'action_buttons'});
+ actions.inject(div, function(container, action) {
+ var button = new Element("input").setValue(action.key).observe("click", action.value);
+ button.type = "button";
+ return container.insert(button);
+ });
+ this.getMessageCell().insert(div);
+ },
+
+ _toHTML: function(txt) {
+ return txt.escapeHTML().replace(/\n/g,"<br />");
+ }
+});
+
+Test.Unit.Runner = Class.create({
+ initialize: function(testcases) {
+ var options = this.options = Object.extend({
+ testLog: 'testlog'
+ }, arguments[1] || {});
+
+ options.resultsURL = this.queryParams.resultsURL;
+
+ this.tests = this.getTests(testcases);
+ this.currentTest = 0;
+
+ Event.observe(window, "load", function() {
+ this.logger = new Test.Unit.Logger($(options.testLog));
+ this.runTests.bind(this).delay(0.1);
+ }.bind(this));
+ },
+
+ queryParams: window.location.search.parseQuery(),
+
+ getTests: function(testcases) {
+ var tests, options = this.options;
+ if (this.queryParams.tests) tests = this.queryParams.tests.split(',');
+ else if (options.tests) tests = options.tests;
+ else if (options.test) tests = [option.test];
+ else tests = Object.keys(testcases).grep(/^test/);
+
+ return tests.map(function(test) {
+ if (testcases[test])
+ return new Test.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown);
+ }).compact();
+ },
+
+ getResult: function() {
+ var results = {
+ tests: this.tests.length,
+ assertions: 0,
+ failures: 0,
+ errors: 0
+ };
+
+ return this.tests.inject(results, function(results, test) {
+ results.assertions += test.assertions;
+ results.failures += test.failures;
+ results.errors += test.errors;
+ return results;
+ });
+ },
+
+ postResults: function() {
+ if (this.options.resultsURL) {
+ new Ajax.Request(this.options.resultsURL,
+ { method: 'get', parameters: this.getResult(), asynchronous: false });
+ }
+ },
+
+ runTests: function() {
+ var test = this.tests[this.currentTest], actions;
+
+ if (!test) return this.finish();
+ if (test.timerID > 0) test.timerID = -1;
+ if (!test.isWaiting) this.logger.start(test.name);
+ test.run();
+ if (test.isWaiting) {
+ if (test.timeToWait) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ test.timerID = setTimeout(this.runTests.bind(this), test.timeToWait);
+ } else {
+ this.logger.message("Waiting for finish");
+ }
+ test.runner = this;
+ return;
+ }
+
+ this.logger.finish(test.status(), test.summary());
+ if (actions = test.actions) this.logger.appendActionButtons(actions);
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+ },
+
+ finish: function() {
+ this.postResults();
+ this.logger.summary(this.summary());
+ },
+
+ summary: function() {
+ return '#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors'
+ .interpolate(this.getResult());
+ }
+});
+
+Test.Unit.MessageTemplate = Class.create({
+ initialize: function(string) {
+ var parts = [];
+ (string || '').scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) {
+ parts.push(part[0]);
+ });
+ this.parts = parts;
+ },
+
+ evaluate: function(params) {
+ return this.parts.map(function(part) {
+ return part == '?' ? Test.Unit.inspect(params.shift()) : part.replace(/\\\?/, '?');
+ }).join('');
+ }
+});
+
+Test.Unit.Assertions = {
+ buildMessage: function(message, template) {
+ var args = $A(arguments).slice(2);
+ return (message ? message + '\n' : '') + new Test.Unit.MessageTemplate(template).evaluate(args);
+ },
+
+ flunk: function(message) {
+ this.assertBlock(message || 'Flunked', function() { return false });
+ },
+
+ assertBlock: function(message, block) {
+ try {
+ block.call(this) ? this.pass() : this.fail(message);
+ } catch(e) { this.error(e) }
+ },
+
+ assert: function(expression, message) {
+ message = this.buildMessage(message || 'assert', 'got <?>', expression);
+ this.assertBlock(message, function() { return expression });
+ },
+
+ assertEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected == actual });
+ },
+
+ assertNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected != actual });
+ },
+
+ assertEnumEqual: function(expected, actual, message) {
+ expected = $A(expected);
+ actual = $A(actual);
+ message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() {
+ return expected.length == actual.length && expected.zip(actual).all(function(pair) { return pair[0] == pair[1] });
+ });
+ },
+
+ assertEnumNotEqual: function(expected, actual, message) {
+ expected = $A(expected);
+ actual = $A(actual);
+ message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual);
+ this.assertBlock(message, function() {
+ return expected.length != actual.length || expected.zip(actual).any(function(pair) { return pair[0] != pair[1] });
+ });
+ },
+
+ assertHashEqual: function(expected, actual, message) {
+ expected = $H(expected);
+ actual = $H(actual);
+ var expected_array = expected.toArray().sort(), actual_array = actual.toArray().sort();
+ message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual);
+ // from now we recursively zip & compare nested arrays
+ var block = function() {
+ return expected_array.length == actual_array.length &&
+ expected_array.zip(actual_array).all(function(pair) {
+ return pair.all(Object.isArray) ?
+ pair[0].zip(pair[1]).all(arguments.callee) : pair[0] == pair[1];
+ });
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertHashNotEqual: function(expected, actual, message) {
+ expected = $H(expected);
+ actual = $H(actual);
+ var expected_array = expected.toArray().sort(), actual_array = actual.toArray().sort();
+ message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual);
+ // from now we recursively zip & compare nested arrays
+ var block = function() {
+ return !(expected_array.length == actual_array.length &&
+ expected_array.zip(actual_array).all(function(pair) {
+ return pair.all(Object.isArray) ?
+ pair[0].zip(pair[1]).all(arguments.callee) : pair[0] == pair[1];
+ }));
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected === actual });
+ },
+
+ assertNotIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected !== actual });
+ },
+
+ assertNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj === null });
+ },
+
+ assertNotNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj !== null });
+ },
+
+ assertUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj == "undefined" });
+ },
+
+ assertNotUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj != "undefined" });
+ },
+
+ assertNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj == null });
+ },
+
+ assertNotNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj != null });
+ },
+
+ assertMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual);
+ this.assertBlock(message, function() { return new RegExp(expected).exec(actual) });
+ },
+
+ assertNoMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual);
+ this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) });
+ },
+
+ assertHidden: function(element, message) {
+ message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element);
+ this.assertBlock(message, function() { return element.style.display == 'none' });
+ },
+
+ assertInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual);
+ this.assertBlock(message, function() { return actual instanceof expected });
+ },
+
+ assertNotInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual);
+ this.assertBlock(message, function() { return !(actual instanceof expected) });
+ },
+
+ assertRespondsTo: function(method, obj, message) {
+ message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method);
+ this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') });
+ },
+
+ assertRaise: function(exceptionName, method, message) {
+ message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName);
+ var block = function() {
+ try {
+ method();
+ return false;
+ } catch(e) {
+ if (e.name == exceptionName) return true;
+ else throw e;
+ }
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertNothingRaised: function(method, message) {
+ try {
+ method();
+ this.assert(true, "Expected nothing to be thrown");
+ } catch(e) {
+ message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e);
+ this.flunk(message);
+ }
+ },
+
+ _isVisible: function(element) {
+ element = $(element);
+ if (!element.parentNode) return true;
+ this.assertNotNull(element);
+ if (element.style && Element.getStyle(element, 'display') == 'none')
+ return false;
+
+ return arguments.callee.call(this, element.parentNode);
+ },
+
+ assertVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not visible.', element);
+ this.assertBlock(message, function() { return this._isVisible(element) });
+ },
+
+ assertNotVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element);
+ this.assertBlock(message, function() { return !this._isVisible(element) });
+ },
+
+ assertElementsMatch: function() {
+ var pass = true, expressions = $A(arguments), elements = $A(expressions.shift());
+ if (elements.length != expressions.length) {
+ message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions);
+ this.flunk(message);
+ pass = false;
+ }
+ elements.zip(expressions).all(function(pair, index) {
+ var element = $(pair.first()), expression = pair.last();
+ if (element.match(expression)) return true;
+ message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element);
+ this.flunk(message);
+ pass = false;
+ }.bind(this))
+
+ if (pass) this.assert(true, "Expected all elements to match.");
+ },
+
+ assertElementMatches: function(element, expression, message) {
+ this.assertElementsMatch([element], expression);
+ }
+};
+
+Test.Unit.Testcase = Class.create(Test.Unit.Assertions, {
+ initialize: function(name, test, setup, teardown) {
+ this.name = name;
+ this.test = test || Prototype.emptyFunction;
+ this.setup = setup || Prototype.emptyFunction;
+ this.teardown = teardown || Prototype.emptyFunction;
+ this.messages = [];
+ this.actions = {};
+ },
+
+ isWaiting: false,
+ timeToWait: null,
+ timerID: -1,
+ runner: null,
+ assertions: 0,
+ failures: 0,
+ errors: 0,
+ isRunningFromRake: window.location.port == 4711,
+
+ wait: function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+ },
+
+ waitForFinish: function() {
+ this.isWaiting = true;
+ },
+
+ finish: function() {
+ if (this.timerID > 0) {
+ clearTimeout(this.timerID);
+ this.timerID = -1;
+ this.timeToWait = null;
+ }
+ this.test = function(){};
+ // continue test
+ if (this.runner)
+ this.runner.runTests();
+ },
+
+ run: function(rethrow) {
+ try {
+ try {
+ if (!this.isWaiting) this.setup();
+ this.isWaiting = false;
+ this.test();
+ } finally {
+ if (!this.isWaiting) {
+ this.teardown();
+ }
+ }
+ }
+ catch(e) {
+ if (rethrow) throw e;
+ this.error(e, this);
+ }
+ },
+
+ summary: function() {
+ var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors\n';
+ return msg.interpolate(this) + this.messages.join("\n");
+ },
+
+ pass: function() {
+ this.assertions++;
+ },
+
+ fail: function(message) {
+ this.failures++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ match = /.*\/(.*_test\.js):(\d+)/.exec(e.stack || '') || ['','',''];
+ file = match[1];
+ line = match[2];
+ }
+ this.messages.push("Failure: " + message + (line ? " " + file + ": Line #" + line : ""));
+ },
+
+ info: function(message) {
+ this.messages.push("Info: " + message);
+ },
+
+ error: function(error, test) {
+ this.errors++;
+ this.actions['retry with throw'] = function() { test.run(true) };
+ this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) + ")");
+ },
+
+ status: function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ return 'passed';
+ },
+
+ benchmark: function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+ }
+});
+
+if ( parent.SimpleTest && parent.runAJAXTest ) (function(){
+ var finish = Test.Unit.Logger.prototype.finish;
+ Test.Unit.Logger.prototype.finish = function(status,summary){
+ parent.SimpleTest.ok( status == "passed", `${this.testName}: ${summary}` );
+ return finish.apply( this, arguments );
+ };
+
+ // Intentionally overwrite (to stop the Ajax request)
+ Test.Unit.Runner.prototype.postResults = parent.runAJAXTest;
+})();
+
+
diff --git a/dom/tests/mochitest/ajax/prototype/test/test.css b/dom/tests/mochitest/ajax/prototype/test/test.css
new file mode 100644
index 0000000000..6fe8f51c9b
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/test.css
@@ -0,0 +1,50 @@
+body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li {
+ font-family: sans-serif;
+}
+
+body {
+ font-size:0.8em;
+}
+
+#log {
+ padding-bottom: 1em;
+ border-bottom: 2px solid #000;
+ margin-bottom: 2em;
+}
+
+.logsummary {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ padding: 1ex;
+ border: 1px solid #000;
+ font-weight: bold;
+}
+
+.logtable {
+ width:100%;
+ border-collapse: collapse;
+ border: 1px dotted #666;
+}
+
+.logtable td, .logtable th {
+ text-align: left;
+ padding: 3px 8px;
+ border: 1px dotted #666;
+}
+
+.logtable .passed {
+ background-color: #cfc;
+}
+
+.logtable .failed, .logtable .error {
+ background-color: #fcc;
+}
+
+.logtable td div.action_buttons {
+ display: inline;
+}
+
+.logtable td div.action_buttons input {
+ margin: 0 5px;
+ font-size: 10px;
+} \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/ajax_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/ajax_test.js
new file mode 100644
index 0000000000..9ceb985186
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/ajax_test.js
@@ -0,0 +1,286 @@
+var extendDefault = function(options) {
+ return Object.extend({
+ asynchronous: false,
+ method: 'get',
+ onException: function(request, e) { throw e }
+ }, options);
+};
+
+new Test.Unit.Runner({
+ setup: function() {
+ $('content').update('');
+ $('content2').update('');
+ },
+
+ teardown: function() {
+ // hack to cleanup responders
+ Ajax.Responders.responders = [Ajax.Responders.responders[0]];
+ },
+
+ testSynchronousRequest: function() {
+ this.assertEqual("", $("content").innerHTML);
+
+ this.assertEqual(0, Ajax.activeRequestCount);
+ new Ajax.Request("../fixtures/hello.js", extendDefault({
+ asynchronous: false,
+ method: 'GET',
+ evalJS: 'force'
+ }));
+ this.assertEqual(0, Ajax.activeRequestCount);
+
+ var h2 = $("content").firstChild;
+ this.assertEqual("Hello world!", h2.innerHTML);
+ },
+
+ testUpdaterOptions: function() {
+ var options = {
+ method: 'get',
+ asynchronous: false,
+ evalJS: 'force',
+ onComplete: Prototype.emptyFunction
+ }
+ var request = new Ajax.Updater("content", "../fixtures/hello.js", options);
+ request.options.onComplete = function() {};
+ this.assertIdentical(Prototype.emptyFunction, options.onComplete);
+ },
+
+ testEvalResponseShouldBeCalledBeforeOnComplete: function() {
+ if (this.isRunningFromRake) {
+ this.assertEqual("", $("content").innerHTML);
+
+ this.assertEqual(0, Ajax.activeRequestCount);
+ new Ajax.Request("../fixtures/hello.js", extendDefault({
+ onComplete: function(response) { this.assertNotEqual("", $("content").innerHTML) }.bind(this)
+ }));
+ this.assertEqual(0, Ajax.activeRequestCount);
+
+ var h2 = $("content").firstChild;
+ this.assertEqual("Hello world!", h2.innerHTML);
+ } else {
+ this.info(message);
+ }
+ },
+
+ testContentTypeSetForSimulatedVerbs: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request('/inspect', extendDefault({
+ method: 'put',
+ contentType: 'application/bogus',
+ onComplete: function(response) {
+ this.assertEqual('application/bogus; charset=UTF-8', response.responseJSON.headers['content-type']);
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+ },
+
+ testOnCreateCallback: function() {
+ new Ajax.Request("../fixtures/content.html", extendDefault({
+ onCreate: function(transport) { this.assertEqual(0, transport.readyState) }.bind(this),
+ onComplete: function(transport) { this.assertNotEqual(0, transport.readyState) }.bind(this)
+ }));
+ },
+
+ testEvalJS: function() {
+ if (this.isRunningFromRake) {
+
+ $('content').update();
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.js,
+ onComplete: function(transport) {
+ var h2 = $("content").firstChild;
+ this.assertEqual("Hello world!", h2.innerHTML);
+ }.bind(this)
+ }));
+
+ $('content').update();
+ new Ajax.Request("/response", extendDefault({
+ evalJS: false,
+ parameters: Fixtures.js,
+ onComplete: function(transport) {
+ this.assertEqual("", $("content").innerHTML);
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+
+ $('content').update();
+ new Ajax.Request("../fixtures/hello.js", extendDefault({
+ evalJS: 'force',
+ onComplete: function(transport) {
+ var h2 = $("content").firstChild;
+ this.assertEqual("Hello world!", h2.innerHTML);
+ }.bind(this)
+ }));
+ },
+
+ testCallbacks: function() {
+ var options = extendDefault({
+ onCreate: function(transport) { this.assertInstanceOf(Ajax.Response, transport) }.bind(this)
+ });
+
+ Ajax.Request.Events.each(function(state){
+ options['on' + state] = options.onCreate;
+ });
+
+ new Ajax.Request("../fixtures/content.html", options);
+ },
+
+ testResponseText: function() {
+ new Ajax.Request("../fixtures/empty.html", extendDefault({
+ onComplete: function(transport) { this.assertEqual('', transport.responseText) }.bind(this)
+ }));
+
+ new Ajax.Request("../fixtures/content.html", extendDefault({
+ onComplete: function(transport) { this.assertEqual(sentence, transport.responseText.toLowerCase()) }.bind(this)
+ }));
+ },
+
+ testResponseXML: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.xml,
+ onComplete: function(transport) {
+ this.assertEqual('foo', transport.responseXML.getElementsByTagName('name')[0].getAttribute('attr'))
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+ },
+
+ testResponseJSON: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.json,
+ onComplete: function(transport) { this.assertEqual(123, transport.responseJSON.test) }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ parameters: {
+ 'Content-Length': 0,
+ 'Content-Type': 'application/json'
+ },
+ onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ evalJSON: false,
+ parameters: Fixtures.json,
+ onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.jsonWithoutContentType,
+ onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ sanitizeJSON: true,
+ parameters: Fixtures.invalidJson,
+ onException: function(request, error) {
+ this.assert(error.message.include('Badly formed JSON string'));
+ this.assertInstanceOf(Ajax.Request, request);
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+
+ new Ajax.Request("../fixtures/data.json", extendDefault({
+ evalJSON: 'force',
+ onComplete: function(transport) { this.assertEqual(123, transport.responseJSON.test) }.bind(this)
+ }));
+ },
+
+ testHeaderJSON: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.headerJson,
+ onComplete: function(transport, json) {
+ this.assertEqual('hello #éà', transport.headerJSON.test);
+ this.assertEqual('hello #éà', json.test);
+ }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ onComplete: function(transport, json) {
+ this.assertNull(transport.headerJSON)
+ this.assertNull(json)
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+ },
+
+ testGetHeader: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request("/response", extendDefault({
+ parameters: { 'X-TEST': 'some value' },
+ onComplete: function(transport) {
+ this.assertEqual('some value', transport.getHeader('X-Test'));
+ this.assertNull(transport.getHeader('X-Inexistant'));
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+ },
+
+ testParametersCanBeHash: function() {
+ if (this.isRunningFromRake) {
+ new Ajax.Request("/response", extendDefault({
+ parameters: $H({ "one": "two", "three": "four" }),
+ onComplete: function(transport) {
+ this.assertEqual("two", transport.getHeader("one"));
+ this.assertEqual("four", transport.getHeader("three"));
+ this.assertNull(transport.getHeader("toObject"));
+ }.bind(this)
+ }));
+ } else {
+ this.info(message);
+ }
+ },
+
+ testIsSameOriginMethod: function() {
+ var isSameOrigin = Ajax.Request.prototype.isSameOrigin;
+ this.assert(isSameOrigin.call({ url: '/foo/bar.html' }), '/foo/bar.html');
+ this.assert(isSameOrigin.call({ url: window.location.toString() }), window.location);
+ this.assert(!isSameOrigin.call({ url: 'http://example.com' }), 'http://example.com');
+
+ if (this.isRunningFromRake) {
+ Ajax.Request.prototype.isSameOrigin = function() {
+ return false
+ };
+
+ $("content").update('same origin policy');
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.js,
+ onComplete: function(transport) {
+ this.assertEqual("same origin policy", $("content").innerHTML);
+ }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ parameters: Fixtures.invalidJson,
+ onException: function(request, error) {
+ this.assert(error.message.include('Badly formed JSON string'));
+ }.bind(this)
+ }));
+
+ new Ajax.Request("/response", extendDefault({
+ parameters: { 'X-JSON': '{});window.attacked = true;({}' },
+ onException: function(request, error) {
+ this.assert(error.message.include('Badly formed JSON string'));
+ }.bind(this)
+ }));
+
+ Ajax.Request.prototype.isSameOrigin = isSameOrigin;
+ } else {
+ this.info(message);
+ }
+ }
+});
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/array_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/array_test.js
new file mode 100644
index 0000000000..c3f9d41147
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/array_test.js
@@ -0,0 +1,190 @@
+var globalArgsTest = 'nothing to see here';
+
+new Test.Unit.Runner({
+ test$A: function(){
+ this.assertEnumEqual([], $A({}));
+ },
+
+ testToArrayOnArguments: function(){
+ function toArrayOnArguments(){
+ globalArgsTest = $A(arguments);
+ }
+ toArrayOnArguments();
+ this.assertEnumEqual([], globalArgsTest);
+ toArrayOnArguments('foo');
+ this.assertEnumEqual(['foo'], globalArgsTest);
+ toArrayOnArguments('foo','bar');
+ this.assertEnumEqual(['foo','bar'], globalArgsTest);
+ },
+
+ testToArrayOnNodeList: function(){
+ // direct HTML
+ this.assertEqual(3, $A($('test_node').childNodes).length);
+
+ // DOM
+ var element = document.createElement('div');
+ element.appendChild(document.createTextNode('22'));
+ (2).times(function(){ element.appendChild(document.createElement('span')) });
+ this.assertEqual(3, $A(element.childNodes).length);
+
+ // HTML String
+ element = document.createElement('div');
+ $(element).update('22<span></span><span></span');
+ this.assertEqual(3, $A(element.childNodes).length);
+ },
+
+ testClear: function(){
+ this.assertEnumEqual([], [].clear());
+ this.assertEnumEqual([], [1].clear());
+ this.assertEnumEqual([], [1,2].clear());
+ },
+
+ testClone: function(){
+ this.assertEnumEqual([], [].clone());
+ this.assertEnumEqual([1], [1].clone());
+ this.assertEnumEqual([1,2], [1,2].clone());
+ this.assertEnumEqual([0,1,2], [0,1,2].clone());
+ var a = [0,1,2];
+ var b = a;
+ this.assertIdentical(a, b);
+ b = a.clone();
+ this.assertNotIdentical(a, b);
+ },
+
+ testFirst: function(){
+ this.assertUndefined([].first());
+ this.assertEqual(1, [1].first());
+ this.assertEqual(1, [1,2].first());
+ },
+
+ testLast: function(){
+ this.assertUndefined([].last());
+ this.assertEqual(1, [1].last());
+ this.assertEqual(2, [1,2].last());
+ },
+
+ testCompact: function(){
+ this.assertEnumEqual([], [].compact());
+ this.assertEnumEqual([1,2,3], [1,2,3].compact());
+ this.assertEnumEqual([0,1,2,3], [0,null,1,2,undefined,3].compact());
+ this.assertEnumEqual([1,2,3], [null,1,2,3,null].compact());
+ },
+
+ testFlatten: function(){
+ this.assertEnumEqual([], [].flatten());
+ this.assertEnumEqual([1,2,3], [1,2,3].flatten());
+ this.assertEnumEqual([1,2,3], [1,[[[2,3]]]].flatten());
+ this.assertEnumEqual([1,2,3], [[1],[2],[3]].flatten());
+ this.assertEnumEqual([1,2,3], [[[[[[[1]]]]]],2,3].flatten());
+ },
+
+ testIndexOf: function(){
+ this.assertEqual(-1, [].indexOf(1));
+ this.assertEqual(-1, [0].indexOf(1));
+ this.assertEqual(0, [1].indexOf(1));
+ this.assertEqual(1, [0,1,2].indexOf(1));
+ this.assertEqual(0, [1,2,1].indexOf(1));
+ this.assertEqual(2, [1,2,1].indexOf(1, -1));
+ this.assertEqual(1, [undefined,null].indexOf(null));
+ },
+
+ testLastIndexOf: function(){
+ this.assertEqual(-1,[].lastIndexOf(1));
+ this.assertEqual(-1, [0].lastIndexOf(1));
+ this.assertEqual(0, [1].lastIndexOf(1));
+ this.assertEqual(2, [0,2,4,6].lastIndexOf(4));
+ this.assertEqual(3, [4,4,2,4,6].lastIndexOf(4));
+ this.assertEqual(3, [0,2,4,6].lastIndexOf(6,3));
+ this.assertEqual(-1, [0,2,4,6].lastIndexOf(6,2));
+ this.assertEqual(0, [6,2,4,6].lastIndexOf(6,2));
+
+ var fixture = [1,2,3,4,3];
+ this.assertEqual(4, fixture.lastIndexOf(3));
+ this.assertEnumEqual([1,2,3,4,3],fixture);
+
+ //tests from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
+ var array = [2, 5, 9, 2];
+ this.assertEqual(3,array.lastIndexOf(2));
+ this.assertEqual(-1,array.lastIndexOf(7));
+ this.assertEqual(3,array.lastIndexOf(2,3));
+ this.assertEqual(0,array.lastIndexOf(2,2));
+ this.assertEqual(0,array.lastIndexOf(2,-2));
+ this.assertEqual(3,array.lastIndexOf(2,-1));
+ },
+
+ testInspect: function(){
+ this.assertEqual('[]',[].inspect());
+ this.assertEqual('[1]',[1].inspect());
+ this.assertEqual('[\'a\']',['a'].inspect());
+ this.assertEqual('[\'a\', 1]',['a',1].inspect());
+ },
+
+ testIntersect: function(){
+ this.assertEnumEqual([1,3], [1,1,3,5].intersect([1,2,3]));
+ this.assertEnumEqual([1], [1,1].intersect([1,1]));
+ this.assertEnumEqual([0], [0,2].intersect([1,0]));
+ this.assertEnumEqual([], [1,1,3,5].intersect([4]));
+ this.assertEnumEqual([], [1].intersect(['1']));
+
+ this.assertEnumEqual(
+ ['B','C','D'],
+ $R('A','Z').toArray().intersect($R('B','D').toArray())
+ );
+ },
+
+ testToJSON: function(){
+ this.assertEqual('[]', [].toJSON());
+ this.assertEqual('[\"a\"]', ['a'].toJSON());
+ this.assertEqual('[\"a\", 1]', ['a', 1].toJSON());
+ this.assertEqual('[\"a\", {\"b\": null}]', ['a', {'b': null}].toJSON());
+ },
+
+ testReduce: function(){
+ this.assertUndefined([].reduce());
+ this.assertNull([null].reduce());
+ this.assertEqual(1, [1].reduce());
+ this.assertEnumEqual([1,2,3], [1,2,3].reduce());
+ this.assertEnumEqual([1,null,3], [1,null,3].reduce());
+ },
+
+ testReverse: function(){
+ this.assertEnumEqual([], [].reverse());
+ this.assertEnumEqual([1], [1].reverse());
+ this.assertEnumEqual([2,1], [1,2].reverse());
+ this.assertEnumEqual([3,2,1], [1,2,3].reverse());
+ },
+
+ testSize: function(){
+ this.assertEqual(4, [0, 1, 2, 3].size());
+ this.assertEqual(0, [].size());
+ },
+
+ testUniq: function(){
+ this.assertEnumEqual([1], [1, 1, 1].uniq());
+ this.assertEnumEqual([1], [1].uniq());
+ this.assertEnumEqual([], [].uniq());
+ this.assertEnumEqual([0, 1, 2, 3], [0, 1, 2, 2, 3, 0, 2].uniq());
+ this.assertEnumEqual([0, 1, 2, 3], [0, 0, 1, 1, 2, 3, 3, 3].uniq(true));
+ },
+
+ testWithout: function(){
+ this.assertEnumEqual([], [].without(0));
+ this.assertEnumEqual([], [0].without(0));
+ this.assertEnumEqual([1], [0,1].without(0));
+ this.assertEnumEqual([1,2], [0,1,2].without(0));
+ },
+
+ test$w: function(){
+ this.assertEnumEqual(['a', 'b', 'c', 'd'], $w('a b c d'));
+ this.assertEnumEqual([], $w(' '));
+ this.assertEnumEqual([], $w(''));
+ this.assertEnumEqual([], $w(null));
+ this.assertEnumEqual([], $w(undefined));
+ this.assertEnumEqual([], $w());
+ this.assertEnumEqual([], $w(10));
+ this.assertEnumEqual(['a'], $w('a'));
+ this.assertEnumEqual(['a'], $w('a '));
+ this.assertEnumEqual(['a'], $w(' a'));
+ this.assertEnumEqual(['a', 'b', 'c', 'd'], $w(' a b\nc\t\nd\n'));
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/base_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/base_test.js
new file mode 100644
index 0000000000..f2843128a0
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/base_test.js
@@ -0,0 +1,511 @@
+new Test.Unit.Runner({
+ testFunctionArgumentNames: function() {
+ this.assertEnumEqual([], (function() {}).argumentNames());
+ this.assertEnumEqual(["one"], (function(one) {}).argumentNames());
+ this.assertEnumEqual(["one", "two", "three"], (function(one, two, three) {}).argumentNames());
+ this.assertEnumEqual(["one", "two", "three"], (function( one , two
+ , three ) {}).argumentNames());
+ this.assertEqual("$super", (function($super) {}).argumentNames().first());
+
+ function named1() {};
+ this.assertEnumEqual([], named1.argumentNames());
+ function named2(one) {};
+ this.assertEnumEqual(["one"], named2.argumentNames());
+ function named3(one, two, three) {};
+ this.assertEnumEqual(["one", "two", "three"], named3.argumentNames());
+ },
+
+ testFunctionBind: function() {
+ function methodWithoutArguments() { return this.hi };
+ function methodWithArguments() { return this.hi + ',' + $A(arguments).join(',') };
+ var func = Prototype.emptyFunction;
+
+ this.assertIdentical(func, func.bind());
+ this.assertIdentical(func, func.bind(undefined));
+ this.assertNotIdentical(func, func.bind(null));
+
+ this.assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })());
+ this.assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2'));
+ this.assertEqual('withBindArgs,arg1,arg2',
+ methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')());
+ this.assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4',
+ methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4'));
+ },
+
+ testFunctionCurry: function() {
+ var split = function(delimiter, string) { return string.split(delimiter); };
+ var splitOnColons = split.curry(":");
+ this.assertNotIdentical(split, splitOnColons);
+ this.assertEnumEqual(split(":", "0:1:2:3:4:5"), splitOnColons("0:1:2:3:4:5"));
+ this.assertIdentical(split, split.curry());
+ },
+
+ testFunctionDelay: function() {
+ window.delayed = undefined;
+ var delayedFunction = function() { window.delayed = true; };
+ var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); };
+ delayedFunction.delay(0.8);
+ delayedFunctionWithArgs.delay(0.8, 'hello', 'world');
+ this.assertUndefined(window.delayed);
+ this.wait(1000, function() {
+ this.assert(window.delayed);
+ this.assertEqual('hello world', window.delayedWithArgs);
+ });
+ },
+
+ testFunctionWrap: function() {
+ function sayHello(){
+ return 'hello world';
+ };
+
+ this.assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) {
+ return proceed().toUpperCase();
+ })());
+
+ var temp = String.prototype.capitalize;
+ String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) {
+ if (eachWord && this.include(' ')) return this.split(' ').map(function(str){
+ return str.capitalize();
+ }).join(' ');
+ return proceed();
+ });
+ this.assertEqual('Hello world', 'hello world'.capitalize());
+ this.assertEqual('Hello World', 'hello world'.capitalize(true));
+ this.assertEqual('Hello', 'hello'.capitalize());
+ String.prototype.capitalize = temp;
+ },
+
+ testFunctionDefer: function() {
+ window.deferred = undefined;
+ var deferredFunction = function() { window.deferred = true; };
+ deferredFunction.defer();
+ this.assertUndefined(window.deferred);
+ this.wait(50, function() {
+ this.assert(window.deferred);
+
+ window.deferredValue = 0;
+ var deferredFunction2 = function(arg) { window.deferredValue = arg; };
+ deferredFunction2.defer('test');
+ this.wait(50, function() {
+ this.assertEqual('test', window.deferredValue);
+ });
+ });
+ },
+
+ testFunctionMethodize: function() {
+ var Foo = { bar: function(baz) { return baz } };
+ var baz = { quux: Foo.bar.methodize() };
+
+ this.assertEqual(Foo.bar.methodize(), baz.quux);
+ this.assertEqual(baz, Foo.bar(baz));
+ this.assertEqual(baz, baz.quux());
+ },
+
+ testObjectExtend: function() {
+ var object = {foo: 'foo', bar: [1, 2, 3]};
+ this.assertIdentical(object, Object.extend(object));
+ this.assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object);
+ this.assertIdentical(object, Object.extend(object, {bla: 123}));
+ this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object);
+ this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null},
+ Object.extend(object, {bla: null}));
+ },
+
+ testObjectToQueryString: function() {
+ this.assertEqual('a=A&b=B&c=C&d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'}));
+ },
+
+ testObjectClone: function() {
+ var object = {foo: 'foo', bar: [1, 2, 3]};
+ this.assertNotIdentical(object, Object.clone(object));
+ this.assertHashEqual(object, Object.clone(object));
+ this.assertHashEqual({}, Object.clone());
+ var clone = Object.clone(object);
+ delete clone.bar;
+ this.assertHashEqual({foo: 'foo'}, clone,
+ "Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted.");
+ },
+
+ testObjectInspect: function() {
+ this.assertEqual('undefined', Object.inspect());
+ this.assertEqual('undefined', Object.inspect(undefined));
+ this.assertEqual('null', Object.inspect(null));
+ this.assertEqual("'foo\\\\b\\\'ar'", Object.inspect('foo\\b\'ar'));
+ this.assertEqual('[]', Object.inspect([]));
+ this.assertNothingRaised(function() { Object.inspect(window.Node) });
+ },
+
+ testObjectToJSON: function() {
+ this.assertUndefined(Object.toJSON(undefined));
+ this.assertUndefined(Object.toJSON(Prototype.K));
+ this.assertEqual('\"\"', Object.toJSON(''));
+ this.assertEqual('[]', Object.toJSON([]));
+ this.assertEqual('[\"a\"]', Object.toJSON(['a']));
+ this.assertEqual('[\"a\", 1]', Object.toJSON(['a', 1]));
+ this.assertEqual('[\"a\", {\"b\": null}]', Object.toJSON(['a', {'b': null}]));
+ this.assertEqual('{\"a\": \"hello!\"}', Object.toJSON({a: 'hello!'}));
+ this.assertEqual('{}', Object.toJSON({}));
+ this.assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K}));
+ this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
+ Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}));
+ this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
+ Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})));
+ this.assertEqual('true', Object.toJSON(true));
+ this.assertEqual('false', Object.toJSON(false));
+ this.assertEqual('null', Object.toJSON(null));
+ var sam = new Person('sam');
+ this.assertEqual('-sam', Object.toJSON(sam));
+ this.assertEqual('-sam', sam.toJSON());
+ var element = $('test');
+ this.assertUndefined(Object.toJSON(element));
+ element.toJSON = function(){return 'I\'m a div with id test'};
+ this.assertEqual('I\'m a div with id test', Object.toJSON(element));
+ },
+
+ testObjectToHTML: function() {
+ this.assertIdentical('', Object.toHTML());
+ this.assertIdentical('', Object.toHTML(''));
+ this.assertIdentical('', Object.toHTML(null));
+ this.assertIdentical('0', Object.toHTML(0));
+ this.assertIdentical('123', Object.toHTML(123));
+ this.assertEqual('hello world', Object.toHTML('hello world'));
+ this.assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }}));
+ },
+
+ testObjectIsArray: function() {
+ this.assert(Object.isArray([]));
+ this.assert(Object.isArray([0]));
+ this.assert(Object.isArray([0, 1]));
+ this.assert(!Object.isArray({}));
+ this.assert(!Object.isArray($('list').childNodes));
+ this.assert(!Object.isArray());
+ this.assert(!Object.isArray(''));
+ this.assert(!Object.isArray('foo'));
+ this.assert(!Object.isArray(0));
+ this.assert(!Object.isArray(1));
+ this.assert(!Object.isArray(null));
+ this.assert(!Object.isArray(true));
+ this.assert(!Object.isArray(false));
+ this.assert(!Object.isArray(undefined));
+ },
+
+ testObjectIsHash: function() {
+ this.assert(Object.isHash($H()));
+ this.assert(Object.isHash(new Hash()));
+ this.assert(!Object.isHash({}));
+ this.assert(!Object.isHash(null));
+ this.assert(!Object.isHash());
+ this.assert(!Object.isHash(''));
+ this.assert(!Object.isHash(2));
+ this.assert(!Object.isHash(false));
+ this.assert(!Object.isHash(true));
+ this.assert(!Object.isHash([]));
+ },
+
+ testObjectIsElement: function() {
+ this.assert(Object.isElement(document.createElement('div')));
+ this.assert(Object.isElement(new Element('div')));
+ this.assert(Object.isElement($('testlog')));
+ this.assert(!Object.isElement(document.createTextNode('bla')));
+
+ // falsy variables should not mess up return value type
+ this.assertIdentical(false, Object.isElement(0));
+ this.assertIdentical(false, Object.isElement(''));
+ this.assertIdentical(false, Object.isElement(NaN));
+ this.assertIdentical(false, Object.isElement(null));
+ this.assertIdentical(false, Object.isElement(undefined));
+ },
+
+ testObjectIsFunction: function() {
+ this.assert(Object.isFunction(function() { }));
+ this.assert(Object.isFunction(Class.create()));
+ this.assert(!Object.isFunction("a string"));
+ this.assert(!Object.isFunction($("testlog")));
+ this.assert(!Object.isFunction([]));
+ this.assert(!Object.isFunction({}));
+ this.assert(!Object.isFunction(0));
+ this.assert(!Object.isFunction(false));
+ this.assert(!Object.isFunction(undefined));
+ this.assert(!Object.isFunction(/foo/));
+ this.assert(!Object.isFunction(document.getElementsByTagName('div')));
+ },
+
+ testObjectIsString: function() {
+ this.assert(!Object.isString(function() { }));
+ this.assert(Object.isString("a string"));
+ this.assert(!Object.isString(0));
+ this.assert(!Object.isString([]));
+ this.assert(!Object.isString({}));
+ this.assert(!Object.isString(false));
+ this.assert(!Object.isString(undefined));
+ },
+
+ testObjectIsNumber: function() {
+ this.assert(Object.isNumber(0));
+ this.assert(Object.isNumber(1.0));
+ this.assert(!Object.isNumber(function() { }));
+ this.assert(!Object.isNumber("a string"));
+ this.assert(!Object.isNumber([]));
+ this.assert(!Object.isNumber({}));
+ this.assert(!Object.isNumber(false));
+ this.assert(!Object.isNumber(undefined));
+ },
+
+ testObjectIsUndefined: function() {
+ this.assert(Object.isUndefined(undefined));
+ this.assert(!Object.isUndefined(null));
+ this.assert(!Object.isUndefined(false));
+ this.assert(!Object.isUndefined(0));
+ this.assert(!Object.isUndefined(""));
+ this.assert(!Object.isUndefined(function() { }));
+ this.assert(!Object.isUndefined([]));
+ this.assert(!Object.isUndefined({}));
+ },
+
+ // sanity check
+ testDoesntExtendObjectPrototype: function() {
+ // for-in is supported with objects
+ var iterations = 0, obj = { a: 1, b: 2, c: 3 };
+ for (property in obj) iterations++;
+ this.assertEqual(3, iterations);
+
+ // for-in is not supported with arrays
+ iterations = 0;
+ var arr = [1,2,3];
+ for (property in arr) iterations++;
+ this.assert(iterations > 3);
+ },
+
+
+ testBindAsEventListener: function() {
+ for ( var i = 0; i < 10; ++i ) {
+ var div = document.createElement('div');
+ div.setAttribute('id','test-'+i);
+ document.body.appendChild(div);
+ var tobj = new TestObj();
+ var eventTest = { test: true };
+ var call = tobj.assertingEventHandler.bindAsEventListener(tobj,
+ this.assertEqual.bind(this, eventTest),
+ this.assertEqual.bind(this, arg1),
+ this.assertEqual.bind(this, arg2),
+ this.assertEqual.bind(this, arg3), arg1, arg2, arg3 );
+ call(eventTest);
+ }
+ },
+
+ testDateToJSON: function() {
+ this.assertEqual('\"1970-01-01T00:00:00Z\"', new Date(Date.UTC(1970, 0, 1)).toJSON());
+ },
+
+ testRegExpEscape: function() {
+ this.assertEqual('word', RegExp.escape('word'));
+ this.assertEqual('\\/slashes\\/', RegExp.escape('/slashes/'));
+ this.assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\'));
+ this.assertEqual('\\\\border of word', RegExp.escape('\\border of word'));
+
+ this.assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)'));
+ this.assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]);
+
+ this.assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)'));
+ this.assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]);
+
+ this.assertEqual('\\(\\?<\\=positive-lookbehind\\)', RegExp.escape('(?<=positive-lookbehind)'));
+ this.assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?<=') + '([^)]+)').exec('(?<=positive-lookbehind)')[1]);
+
+ this.assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)'));
+ this.assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]);
+
+ this.assertEqual('\\(\\?<\\!negative-lookbehind\\)', RegExp.escape('(?<!negative-lookbehind)'));
+ this.assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?<!') + '([^)]+)').exec('(?<!negative-lookbehind)')[1]);
+
+ this.assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+'));
+ this.assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]);
+
+ this.assertEqual('<div>', new RegExp(RegExp.escape('<div>')).exec('<td><div></td>')[0]);
+
+ this.assertEqual('false', RegExp.escape(false));
+ this.assertEqual('undefined', RegExp.escape());
+ this.assertEqual('null', RegExp.escape(null));
+ this.assertEqual('42', RegExp.escape(42));
+
+ this.assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t'));
+ this.assertEqual('\n\r\t', RegExp.escape('\n\r\t'));
+ this.assertEqual('\\{5,2\\}', RegExp.escape('{5,2}'));
+
+ this.assertEqual(
+ '\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g',
+ RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g')
+ );
+ },
+
+ testBrowserDetection: function() {
+ var results = $H(Prototype.Browser).map(function(engine){
+ return engine;
+ }).partition(function(engine){
+ return engine[1] === true
+ });
+ var trues = results[0], falses = results[1];
+
+ this.info('User agent string is: ' + navigator.userAgent);
+
+ this.assert(trues.size() == 0 || trues.size() == 1,
+ 'There should be only one or no browser detected.');
+
+ // we should have definite trues or falses here
+ trues.each(function(result) {
+ this.assert(result[1] === true);
+ }, this);
+ falses.each(function(result) {
+ this.assert(result[1] === false);
+ }, this);
+
+ if (navigator.userAgent.indexOf('AppleWebKit/') > -1) {
+ this.info('Running on WebKit');
+ this.assert(Prototype.Browser.WebKit);
+ }
+
+ if (!!window.opera) {
+ this.info('Running on Opera');
+ this.assert(Prototype.Browser.Opera);
+ }
+
+ if (!!(window.attachEvent && !window.opera)) {
+ this.info('Running on IE');
+ this.assert(Prototype.Browser.IE);
+ }
+
+ if (navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1) {
+ this.info('Running on Gecko');
+ this.assert(Prototype.Browser.Gecko);
+ }
+ },
+
+ testClassCreate: function() {
+ this.assert(Object.isFunction(Animal), 'Animal is not a constructor');
+ this.assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
+ Animal.subclasses.each(function(subclass) {
+ this.assertEqual(Animal, subclass.superclass);
+ }, this);
+
+ var Bird = Class.create(Animal);
+ this.assertEqual(Bird, Animal.subclasses.last());
+ // for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes
+ this.assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort());
+ },
+
+ testClassInstantiation: function() {
+ var pet = new Animal("Nibbles");
+ this.assertEqual("Nibbles", pet.name, "property not initialized");
+ this.assertEqual('Nibbles: Hi!', pet.say('Hi!'));
+ this.assertEqual(Animal, pet.constructor, "bad constructor reference");
+ this.assertUndefined(pet.superclass);
+
+ var Empty = Class.create();
+ this.assert('object', typeof new Empty);
+ },
+
+ testInheritance: function() {
+ var tom = new Cat('Tom');
+ this.assertEqual(Cat, tom.constructor, "bad constructor reference");
+ this.assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference');
+ this.assertEqual('Tom', tom.name);
+ this.assertEqual('Tom: meow', tom.say('meow'));
+ this.assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal));
+ },
+
+ testSuperclassMethodCall: function() {
+ var tom = new Cat('Tom');
+ this.assertEqual('Tom: Yum!', tom.eat(new Mouse));
+
+ // augment the constructor and test
+ var Dodo = Class.create(Animal, {
+ initialize: function($super, name) {
+ $super(name);
+ this.extinct = true;
+ },
+
+ say: function($super, message) {
+ return $super(message) + " honk honk";
+ }
+ });
+
+ var gonzo = new Dodo('Gonzo');
+ this.assertEqual('Gonzo', gonzo.name);
+ this.assert(gonzo.extinct, 'Dodo birds should be extinct');
+ this.assertEqual("Gonzo: hello honk honk", gonzo.say("hello"));
+ },
+
+ testClassAddMethods: function() {
+ var tom = new Cat('Tom');
+ var jerry = new Mouse('Jerry');
+
+ Animal.addMethods({
+ sleep: function() {
+ return this.say('ZZZ');
+ }
+ });
+
+ Mouse.addMethods({
+ sleep: function($super) {
+ return $super() + " ... no, can't sleep! Gotta steal cheese!";
+ },
+ escape: function(cat) {
+ return this.say('(from a mousehole) Take that, ' + cat.name + '!');
+ }
+ });
+
+ this.assertEqual('Tom: ZZZ', tom.sleep(), "added instance method not available to subclass");
+ this.assertEqual("Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
+ this.assertEqual("Jerry: (from a mousehole) Take that, Tom!", jerry.escape(tom));
+ // insure that a method has not propagated *up* the prototype chain:
+ this.assertUndefined(tom.escape);
+ this.assertUndefined(new Animal().escape);
+
+ Animal.addMethods({
+ sleep: function() {
+ return this.say('zZzZ');
+ }
+ });
+
+ this.assertEqual("Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
+ },
+
+ testBaseClassWithMixin: function() {
+ var grass = new Plant('grass', 3);
+ this.assertRespondsTo('getValue', grass);
+ this.assertEqual('#<Plant: grass>', grass.inspect());
+ },
+
+ testSubclassWithMixin: function() {
+ var snoopy = new Dog('Snoopy', 12, 'male');
+ this.assertRespondsTo('reproduce', snoopy);
+ },
+
+ testSubclassWithMixins: function() {
+ var cow = new Ox('cow', 400, 'female');
+ this.assertEqual('#<Ox: cow>', cow.inspect());
+ this.assertRespondsTo('reproduce', cow);
+ this.assertRespondsTo('getValue', cow);
+ },
+
+ testClassWithToStringAndValueOfMethods: function() {
+ var Foo = Class.create({
+ toString: function() { return "toString" },
+ valueOf: function() { return "valueOf" }
+ });
+
+ var Parent = Class.create({
+ m1: function(){ return 'm1' },
+ m2: function(){ return 'm2' }
+ });
+ var Child = Class.create(Parent, {
+ m1: function($super) { return 'm1 child' },
+ m2: function($super) { return 'm2 child' }
+ });
+
+ this.assert(new Child().m1.toString().indexOf('m1 child') > -1);
+
+ this.assertEqual("toString", new Foo().toString());
+ this.assertEqual("valueOf", new Foo().valueOf());
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/dom_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/dom_test.js
new file mode 100644
index 0000000000..ebb3b383ec
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/dom_test.js
@@ -0,0 +1,1404 @@
+var getInnerHTML = function(id) {
+ return $(id).innerHTML.toString().toLowerCase().gsub(/[\r\n\t]/, '');
+};
+var createParagraph = function(text) {
+ var p = document.createElement('p');
+ p.appendChild(document.createTextNode(text));
+ return p;
+}
+
+new Test.Unit.Runner({
+ setup: function() {
+ if (documentViewportProperties) return;
+ // Based on properties check from http://www.quirksmode.org/viewport/compatibility.html
+ documentViewportProperties = {
+ properties : [
+ 'self.pageXOffset', 'self.pageYOffset',
+ 'self.screenX', 'self.screenY',
+ 'self.innerHeight', 'self.innerWidth',
+ 'self.outerHeight', 'self.outerWidth',
+ 'self.screen.height', 'self.screen.width',
+ 'self.screen.availHeight', 'self.screen.availWidth',
+ 'self.screen.availTop', 'self.screen.availLeft',
+ 'self.screen.Top', 'self.screen.Left',
+ 'self.screenTop', 'self.screenLeft',
+ 'document.body.clientHeight', 'document.body.clientWidth',
+ 'document.body.scrollHeight', 'document.body.scrollWidth',
+ 'document.body.scrollLeft', 'document.body.scrollTop',
+ 'document.body.offsetHeight', 'document.body.offsetWidth',
+ 'document.body.offsetTop', 'document.body.offsetLeft'
+ ].inject([], function(properties, prop) {
+ if (!self.screen && prop.include('self.screen')) return;
+ if (!document.body && prop.include('document.body')) return;
+ properties.push(prop);
+ if (prop.include('.body') && document.documentElement)
+ properties.push(prop.sub('.body', '.documentElement'));
+ return properties;
+ }),
+
+ inspect : function() {
+ var props = [];
+ this.properties.each(function(prop) {
+ if (eval(prop)) props[prop] = eval(prop);
+ }, this);
+ return props;
+ }
+ };
+ },
+
+ testDollarFunction: function() {
+ this.assertUndefined($());
+
+ this.assertNull(document.getElementById('noWayThisIDExists'));
+ this.assertNull($('noWayThisIDExists'));
+
+ this.assertIdentical(document.getElementById('testdiv'), $('testdiv'));
+ this.assertEnumEqual([ $('testdiv'), $('container') ], $('testdiv', 'container'));
+ this.assertEnumEqual([ $('testdiv'), undefined, $('container') ],
+ $('testdiv', 'noWayThisIDExists', 'container'));
+ var elt = $('testdiv');
+ this.assertIdentical(elt, $(elt));
+ this.assertRespondsTo('hide', elt);
+ this.assertRespondsTo('childOf', elt);
+ },
+
+ testGetElementsByClassName: function() {
+ if (document.getElementsByClassName.toString().include('[native code]')) {
+ this.info("browser uses native getElementsByClassName; skipping tests");
+ return;
+ }
+
+
+ var div = $('class_names'), list = $('class_names_ul');
+
+ this.assertElementsMatch(document.getElementsByClassName('A'), 'p.A', 'ul#class_names_ul.A', 'li.A.C');
+
+ if (Prototype.Browser.IE)
+ this.assertUndefined(document.getElementById('unextended').show);
+
+ this.assertElementsMatch(div.getElementsByClassName('B'), 'ul#class_names_ul.A.B', 'div.B.C.D');
+ this.assertElementsMatch(div.getElementsByClassName('D C B'), 'div.B.C.D');
+ this.assertElementsMatch(div.getElementsByClassName(' D\nC\tB '), 'div.B.C.D');
+ this.assertElementsMatch(div.getElementsByClassName($w('D C B')));
+ this.assertElementsMatch(list.getElementsByClassName('A'), 'li.A.C');
+ this.assertElementsMatch(list.getElementsByClassName(' A '), 'li.A.C');
+ this.assertElementsMatch(list.getElementsByClassName('C A'), 'li.A.C');
+ this.assertElementsMatch(list.getElementsByClassName("C\nA "), 'li.A.C');
+ this.assertElementsMatch(list.getElementsByClassName('B'));
+ this.assertElementsMatch(list.getElementsByClassName('1'), 'li.1');
+ this.assertElementsMatch(list.getElementsByClassName([1]), 'li.1');
+ this.assertElementsMatch(list.getElementsByClassName(['1 junk']));
+ this.assertElementsMatch(list.getElementsByClassName(''));
+ this.assertElementsMatch(list.getElementsByClassName(' '));
+ this.assertElementsMatch(list.getElementsByClassName(['']));
+ this.assertElementsMatch(list.getElementsByClassName([' ', '']));
+ this.assertElementsMatch(list.getElementsByClassName({}));
+
+ // those lookups shouldn't have extended all nodes in document
+ if (Prototype.Browser.IE) this.assertUndefined(document.getElementById('unextended')['show']);
+ },
+
+ testElementInsertWithHTML: function() {
+ Element.insert('insertions-main', {before:'<p><em>before</em> text</p><p>more testing</p>'});
+ this.assert(getInnerHTML('insertions-container').startsWith('<p><em>before</em> text</p><p>more testing</p>'));
+ Element.insert('insertions-main', {after:'<p><em>after</em> text</p><p>more testing</p>'});
+ this.assert(getInnerHTML('insertions-container').endsWith('<p><em>after</em> text</p><p>more testing</p>'));
+ Element.insert('insertions-main', {top:'<p><em>top</em> text.</p><p>more testing</p>'});
+ this.assert(getInnerHTML('insertions-main').startsWith('<p><em>top</em> text.</p><p>more testing</p>'));
+ Element.insert('insertions-main', {bottom:'<p><em>bottom</em> text.</p><p>more testing</p>'});
+ this.assert(getInnerHTML('insertions-main').endsWith('<p><em>bottom</em> text.</p><p>more testing</p>'));
+ },
+
+ testElementInsertWithDOMNode: function() {
+ Element.insert('insertions-node-main', {before: createParagraph('node before')});
+ this.assert(getInnerHTML('insertions-node-container').startsWith('<p>node before</p>'));
+ Element.insert('insertions-node-main', {after: createParagraph('node after')});
+ this.assert(getInnerHTML('insertions-node-container').endsWith('<p>node after</p>'));
+ Element.insert('insertions-node-main', {top:createParagraph('node top')});
+ this.assert(getInnerHTML('insertions-node-main').startsWith('<p>node top</p>'));
+ Element.insert('insertions-node-main', {bottom:createParagraph('node bottom')});
+ this.assert(getInnerHTML('insertions-node-main').endsWith('<p>node bottom</p>'));
+ this.assertEqual($('insertions-node-main'), $('insertions-node-main').insert(document.createElement('p')));
+ },
+
+ testElementInsertWithToElementMethod: function() {
+ Element.insert('insertions-node-main', {toElement: createParagraph.curry('toElement') });
+ this.assert(getInnerHTML('insertions-node-main').endsWith('<p>toelement</p>'));
+ Element.insert('insertions-node-main', {bottom: {toElement: createParagraph.curry('bottom toElement') }});
+ this.assert(getInnerHTML('insertions-node-main').endsWith('<p>bottom toelement</p>'));
+ },
+
+ testElementInsertWithToHTMLMethod: function() {
+ Element.insert('insertions-node-main', {toHTML: function() { return '<p>toHTML</p>'} });
+ this.assert(getInnerHTML('insertions-node-main').endsWith('<p>tohtml</p>'));
+ Element.insert('insertions-node-main', {bottom: {toHTML: function() { return '<p>bottom toHTML</p>'} }});
+ this.assert(getInnerHTML('insertions-node-main').endsWith('<p>bottom tohtml</p>'));
+ },
+
+ testElementInsertWithNonString: function() {
+ Element.insert('insertions-main', {bottom:3});
+ this.assert(getInnerHTML('insertions-main').endsWith('3'));
+ },
+
+ testElementInsertInTables: function() {
+ Element.insert('second_row', {after:'<tr id="third_row"><td>Third Row</td></tr>'});
+ this.assert($('second_row').descendantOf('table'));
+
+ $('a_cell').insert({top:'hello world'});
+ this.assert($('a_cell').innerHTML.startsWith('hello world'));
+ $('a_cell').insert({after:'<td>hi planet</td>'});
+ this.assertEqual('hi planet', $('a_cell').next().innerHTML);
+ $('table_for_insertions').insert('<tr><td>a cell!</td></tr>');
+ this.assert($('table_for_insertions').innerHTML.gsub('\r\n', '').toLowerCase().include('<tr><td>a cell!</td></tr>'));
+ $('row_1').insert({after:'<tr></tr><tr></tr><tr><td>last</td></tr>'});
+ this.assertEqual('last', $A($('table_for_row_insertions').getElementsByTagName('tr')).last().lastChild.innerHTML);
+ },
+
+ testElementInsertInSelect: function() {
+ var selectTop = $('select_for_insert_top'), selectBottom = $('select_for_insert_bottom');
+ selectBottom.insert('<option value="33">option 33</option><option selected="selected">option 45</option>');
+ this.assertEqual('option 45', selectBottom.getValue());
+ selectTop.insert({top:'<option value="A">option A</option><option value="B" selected="selected">option B</option>'});
+ this.assertEqual(4, selectTop.options.length);
+ },
+
+ testElementMethodInsert: function() {
+ $('element-insertions-main').insert({before:'some text before'});
+ this.assert(getInnerHTML('element-insertions-container').startsWith('some text before'));
+ $('element-insertions-main').insert({after:'some text after'});
+ this.assert(getInnerHTML('element-insertions-container').endsWith('some text after'));
+ $('element-insertions-main').insert({top:'some text top'});
+ this.assert(getInnerHTML('element-insertions-main').startsWith('some text top'));
+ $('element-insertions-main').insert({bottom:'some text bottom'});
+ this.assert(getInnerHTML('element-insertions-main').endsWith('some text bottom'));
+
+ $('element-insertions-main').insert('some more text at the bottom');
+ this.assert(getInnerHTML('element-insertions-main').endsWith('some more text at the bottom'));
+
+ $('element-insertions-main').insert({TOP:'some text uppercase top'});
+ this.assert(getInnerHTML('element-insertions-main').startsWith('some text uppercase top'));
+
+ $('element-insertions-multiple-main').insert({
+ top:'1', bottom:2, before: new Element('p').update('3'), after:'4'
+ });
+ this.assert(getInnerHTML('element-insertions-multiple-main').startsWith('1'));
+ this.assert(getInnerHTML('element-insertions-multiple-main').endsWith('2'));
+ this.assert(getInnerHTML('element-insertions-multiple-container').startsWith('<p>3</p>'));
+ this.assert(getInnerHTML('element-insertions-multiple-container').endsWith('4'));
+
+ $('element-insertions-main').update('test');
+ $('element-insertions-main').insert(null);
+ $('element-insertions-main').insert({bottom:null});
+ this.assertEqual('test', getInnerHTML('element-insertions-main'));
+ $('element-insertions-main').insert(1337);
+ this.assertEqual('test1337', getInnerHTML('element-insertions-main'));
+ },
+
+ testNewElementInsert: function() {
+ var container = new Element('div');
+ element = new Element('div');
+ container.insert(element);
+
+ element.insert({ before: '<p>a paragraph</p>' });
+ this.assertEqual('<p>a paragraph</p><div></div>', getInnerHTML(container));
+ element.insert({ after: 'some text' });
+ this.assertEqual('<p>a paragraph</p><div></div>some text', getInnerHTML(container));
+
+ element.insert({ top: '<p>a paragraph</p>' });
+ this.assertEqual('<p>a paragraph</p>', getInnerHTML(element));
+ element.insert('some text');
+ this.assertEqual('<p>a paragraph</p>some text', getInnerHTML(element));
+ },
+
+ testInsertionBackwardsCompatibility: function() {
+ new Insertion.Before('element-insertions-main', 'some backward-compatibility testing before');
+ this.assert(getInnerHTML('element-insertions-container').include('some backward-compatibility testing before'));
+ new Insertion.After('element-insertions-main', 'some backward-compatibility testing after');
+ this.assert(getInnerHTML('element-insertions-container').include('some backward-compatibility testing after'));
+ new Insertion.Top('element-insertions-main', 'some backward-compatibility testing top');
+ this.assert(getInnerHTML('element-insertions-main').startsWith('some backward-compatibility testing top'));
+ new Insertion.Bottom('element-insertions-main', 'some backward-compatibility testing bottom');
+ this.assert(getInnerHTML('element-insertions-main').endsWith('some backward-compatibility testing bottom'));
+ },
+
+ testElementWrap: function() {
+ var element = $('wrap'), parent = document.createElement('div');
+ element.wrap();
+ this.assert(getInnerHTML('wrap-container').startsWith('<div><p'));
+ element.wrap('div');
+ this.assert(getInnerHTML('wrap-container').startsWith('<div><div><p'));
+
+ element.wrap(parent);
+ this.assert(Object.isFunction(parent.setStyle));
+ this.assert(getInnerHTML('wrap-container').startsWith('<div><div><div><p'));
+
+ element.wrap('div', {className: 'wrapper'});
+ this.assert(element.up().hasClassName('wrapper'));
+ element.wrap({className: 'other-wrapper'});
+ this.assert(element.up().hasClassName('other-wrapper'));
+ element.wrap(new Element('div'), {className: 'yet-other-wrapper'});
+ this.assert(element.up().hasClassName('yet-other-wrapper'));
+
+ var orphan = new Element('p'), div = new Element('div');
+ orphan.wrap(div);
+ this.assertEqual(orphan.parentNode, div);
+ },
+
+ testElementWrapReturnsWrapper: function() {
+ var element = new Element("div");
+ var wrapper = element.wrap("div");
+ this.assertNotEqual(element, wrapper);
+ this.assertEqual(element.up(), wrapper);
+ },
+
+ testElementVisible: function(){
+ this.assertNotEqual('none', $('test-visible').style.display);
+ this.assertEqual('none', $('test-hidden').style.display);
+ },
+
+ testElementToggle: function(){
+ $('test-toggle-visible').toggle();
+ this.assert(!$('test-toggle-visible').visible());
+ $('test-toggle-visible').toggle();
+ this.assert($('test-toggle-visible').visible());
+ $('test-toggle-hidden').toggle();
+ this.assert($('test-toggle-hidden').visible());
+ $('test-toggle-hidden').toggle();
+ this.assert(!$('test-toggle-hidden').visible());
+ },
+
+ testElementShow: function(){
+ $('test-show-visible').show();
+ this.assert($('test-show-visible').visible());
+ $('test-show-hidden').show();
+ this.assert($('test-show-hidden').visible());
+ },
+
+ testElementHide: function(){
+ $('test-hide-visible').hide();
+ this.assert(!$('test-hide-visible').visible());
+ $('test-hide-hidden').hide();
+ this.assert(!$('test-hide-hidden').visible());
+ },
+
+ testElementRemove: function(){
+ $('removable').remove();
+ this.assert($('removable-container').empty());
+ },
+
+ testElementUpdate: function() {
+ $('testdiv').update('hello from div!');
+ this.assertEqual('hello from div!', $('testdiv').innerHTML);
+
+ Element.update('testdiv', 'another hello from div!');
+ this.assertEqual('another hello from div!', $('testdiv').innerHTML);
+
+ Element.update('testdiv', 123);
+ this.assertEqual('123', $('testdiv').innerHTML);
+
+ Element.update('testdiv');
+ this.assertEqual('', $('testdiv').innerHTML);
+
+ Element.update('testdiv', '&nbsp;');
+ this.assert(!$('testdiv').innerHTML.empty());
+ },
+
+ testElementUpdateWithScript: function() {
+ $('testdiv').update('hello from div!<script>\ntestVar="hello!";\n</'+'script>');
+ this.assertEqual('hello from div!',$('testdiv').innerHTML);
+ this.wait(100,function(){
+ this.assertEqual('hello!',testVar);
+
+ Element.update('testdiv','another hello from div!\n<script>testVar="another hello!"</'+'script>\nhere it goes');
+
+ // note: IE normalizes whitespace (like line breaks) to single spaces, thus the match test
+ this.assertMatch(/^another hello from div!\s+here it goes$/,$('testdiv').innerHTML);
+ this.wait(100,function(){
+ this.assertEqual('another hello!',testVar);
+
+ Element.update('testdiv','a\n<script>testVar="a"\ntestVar="b"</'+'script>');
+ this.wait(100,function(){
+ this.assertEqual('b', testVar);
+
+ Element.update('testdiv',
+ 'x<script>testVar2="a"</'+'script>\nblah\n'+
+ 'x<script>testVar2="b"</'+'script>');
+ this.wait(100,function(){
+ this.assertEqual('b', testVar2);
+ });
+ });
+ });
+ });
+ },
+
+ testElementUpdateInTableRow: function() {
+ $('second_row').update('<td id="i_am_a_td">test</td>');
+ this.assertEqual('test',$('i_am_a_td').innerHTML);
+
+ Element.update('second_row','<td id="i_am_a_td">another <span>test</span></td>');
+ this.assertEqual('another <span>test</span>',$('i_am_a_td').innerHTML.toLowerCase());
+ },
+
+ testElementUpdateInTableCell: function() {
+ Element.update('a_cell','another <span>test</span>');
+ this.assertEqual('another <span>test</span>',$('a_cell').innerHTML.toLowerCase());
+ },
+
+ testElementUpdateInTable: function() {
+ Element.update('table','<tr><td>boo!</td></tr>');
+ this.assertMatch(/^<tr>\s*<td>boo!<\/td><\/tr>$/,$('table').innerHTML.toLowerCase());
+ },
+
+ testElementUpdateInSelect: function() {
+ var select = $('select_for_update');
+ select.update('<option value="3">option 3</option><option selected="selected">option 4</option>');
+ this.assertEqual('option 4', select.getValue());
+ },
+
+ testElementUpdateWithDOMNode: function() {
+ $('testdiv').update(new Element('div').insert('bla'));
+ this.assertEqual('<div>bla</div>', getInnerHTML('testdiv'));
+ },
+
+ testElementUpdateWithToElementMethod: function() {
+ $('testdiv').update({toElement: createParagraph.curry('foo')});
+ this.assertEqual('<p>foo</p>', getInnerHTML('testdiv'));
+ },
+
+ testElementUpdateWithToHTMLMethod: function() {
+ $('testdiv').update({toHTML: function() { return 'hello world' }});
+ this.assertEqual('hello world', getInnerHTML('testdiv'));
+ },
+
+ testElementReplace: function() {
+ $('testdiv-replace-1').replace('hello from div!');
+ this.assertEqual('hello from div!', $('testdiv-replace-container-1').innerHTML);
+
+ $('testdiv-replace-2').replace(123);
+ this.assertEqual('123', $('testdiv-replace-container-2').innerHTML);
+
+ $('testdiv-replace-3').replace();
+ this.assertEqual('', $('testdiv-replace-container-3').innerHTML);
+
+ $('testrow-replace').replace('<tr><td>hello</td></tr>');
+ this.assert(getInnerHTML('testrow-replace-container').include('<tr><td>hello</td></tr>'));
+
+ $('testoption-replace').replace('<option>hello</option>');
+ this.assert($('testoption-replace-container').innerHTML.include('hello'));
+
+ Element.replace('testinput-replace', '<p>hello world</p>');
+ this.assertEqual('<p>hello world</p>', getInnerHTML('testform-replace'));
+
+ Element.replace('testform-replace', '<form></form>');
+ this.assertEqual('<p>some text</p><form></form><p>some text</p>', getInnerHTML('testform-replace-container'));
+ },
+
+ testElementReplaceWithScript: function() {
+ $('testdiv-replace-4').replace('hello from div!<script>testVarReplace="hello!"</'+'script>');
+ this.assertEqual('hello from div!', $('testdiv-replace-container-4').innerHTML);
+ this.wait(100,function(){
+ this.assertEqual('hello!',testVarReplace);
+
+ $('testdiv-replace-5').replace('another hello from div!\n<script>testVarReplace="another hello!"</'+'script>\nhere it goes');
+
+ // note: IE normalizes whitespace (like line breaks) to single spaces, thus the match test
+ this.assertMatch(/^another hello from div!\s+here it goes$/,$('testdiv-replace-container-5').innerHTML);
+ this.wait(100,function(){
+ this.assertEqual('another hello!',testVarReplace);
+ });
+ });
+ },
+
+ testElementReplaceWithDOMNode: function() {
+ $('testdiv-replace-element').replace(createParagraph('hello'));
+ this.assertEqual('<p>hello</p>', getInnerHTML('testdiv-replace-container-element'));
+ },
+
+ testElementReplaceWithToElementMethod: function() {
+ $('testdiv-replace-toelement').replace({toElement: createParagraph.curry('hello')});
+ this.assertEqual('<p>hello</p>', getInnerHTML('testdiv-replace-container-toelement'));
+ },
+
+ testElementReplaceWithToHTMLMethod: function() {
+ $('testdiv-replace-tohtml').replace({toHTML: function() { return 'hello' }});
+ this.assertEqual('hello', getInnerHTML('testdiv-replace-container-tohtml'));
+ },
+
+ testElementSelectorMethod: function() {
+ ['getElementsBySelector','select'].each(function(method) {
+ var testSelector = $('container')[method]('p.test');
+ this.assertEqual(testSelector.length, 4);
+ this.assertEqual(testSelector[0], $('intended'));
+ this.assertEqual(testSelector[0], $$('#container p.test')[0]);
+ }, this);
+ },
+
+ testElementAdjacent: function() {
+ var elements = $('intended').adjacent('p');
+ this.assertEqual(elements.length, 3);
+ elements.each(function(element){
+ this.assert(element != $('intended'));
+ }, this);
+ },
+
+ testElementIdentify: function() {
+ var parent = $('identification');
+ this.assertEqual(parent.down().identify(), 'predefined_id');
+ this.assert(parent.down(1).identify().startsWith('anonymous_element_'));
+ this.assert(parent.down(2).identify().startsWith('anonymous_element_'));
+ this.assert(parent.down(3).identify().startsWith('anonymous_element_'));
+
+ this.assert(parent.down(3).id !== parent.down(2).id);
+ },
+
+ testElementClassNameMethod: function() {
+ var testClassNames = $('container').getElementsByClassName('test');
+ var testSelector = $('container').getElementsBySelector('p.test');
+ this.assertEqual(testClassNames[0], $('intended'));
+ this.assertEqual(testClassNames.length, 4);
+ this.assertEqual(testSelector[3], testClassNames[3]);
+ this.assertEqual(testClassNames.length, testSelector.length);
+ },
+
+ testElementAncestors: function() {
+ var ancestors = $('navigation_test_f').ancestors();
+ this.assertElementsMatch(ancestors, 'ul', 'li', 'ul#navigation_test',
+ 'div#nav_tests_isolator', 'body', 'html');
+ this.assertElementsMatch(ancestors.last().ancestors());
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[0]).ancestors()[0]['setStyle'] == 'function');
+ },
+
+ testElementDescendants: function() {
+ this.assertElementsMatch($('navigation_test').descendants(),
+ 'li', 'em', 'li', 'em.dim', 'li', 'em', 'ul', 'li',
+ 'em.dim', 'li#navigation_test_f', 'em', 'li', 'em');
+ this.assertElementsMatch($('navigation_test_f').descendants(), 'em');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof dummy.descendants()[0].setStyle == 'function');
+ },
+
+ testElementFirstDescendant: function() {
+ this.assertElementMatches($('navigation_test').firstDescendant(), 'li.first');
+ this.assertNull($('navigation_test_next_sibling').firstDescendant());
+ },
+
+ testElementChildElements: function() {
+ this.assertElementsMatch($('navigation_test').childElements(),
+ 'li.first', 'li', 'li#navigation_test_c', 'li.last');
+ this.assertNotEqual(0, $('navigation_test_next_sibling').childNodes.length);
+ this.assertEnumEqual([], $('navigation_test_next_sibling').childElements());
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof dummy.childElements()[0].setStyle == 'function');
+ },
+
+ testElementImmediateDescendants: function() {
+ this.assertIdentical(Element.Methods.childElements, Element.Methods.immediateDescendants);
+ },
+
+ testElementPreviousSiblings: function() {
+ this.assertElementsMatch($('navigation_test').previousSiblings(),
+ 'span#nav_test_prev_sibling', 'p.test', 'div', 'div#nav_test_first_sibling');
+ this.assertElementsMatch($('navigation_test_f').previousSiblings(), 'li');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[1]).previousSiblings()[0].setStyle == 'function');
+ },
+
+ testElementNextSiblings: function() {
+ this.assertElementsMatch($('navigation_test').nextSiblings(),
+ 'div#navigation_test_next_sibling', 'p');
+ this.assertElementsMatch($('navigation_test_f').nextSiblings());
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[0]).nextSiblings()[0].setStyle == 'function');
+ },
+
+ testElementSiblings: function() {
+ this.assertElementsMatch($('navigation_test').siblings(),
+ 'div#nav_test_first_sibling', 'div', 'p.test',
+ 'span#nav_test_prev_sibling', 'div#navigation_test_next_sibling', 'p');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[0]).siblings()[0].setStyle == 'function');
+ },
+
+ testElementUp: function() {
+ var element = $('navigation_test_f');
+ this.assertElementMatches(element.up(), 'ul');
+ this.assertElementMatches(element.up(0), 'ul');
+ this.assertElementMatches(element.up(1), 'li');
+ this.assertElementMatches(element.up(2), 'ul#navigation_test');
+ this.assertElementsMatch(element.up('li').siblings(), 'li.first', 'li', 'li.last');
+ this.assertElementMatches(element.up('ul', 1), 'ul#navigation_test');
+ this.assertEqual(undefined, element.up('garbage'));
+ this.assertEqual(undefined, element.up(6));
+ this.assertElementMatches(element.up('.non-existant, ul'), 'ul');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[0]).up().setStyle == 'function');
+ },
+
+ testElementDown: function() {
+ var element = $('navigation_test');
+ this.assertElementMatches(element.down(), 'li.first');
+ this.assertElementMatches(element.down(0), 'li.first');
+ this.assertElementMatches(element.down(1), 'em');
+ this.assertElementMatches(element.down('li', 5), 'li.last');
+ this.assertElementMatches(element.down('ul').down('li', 1), 'li#navigation_test_f');
+ this.assertElementMatches(element.down('.non-existant, .first'), 'li.first');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof dummy.down().setStyle == 'function');
+
+ var input = $$('input')[0];
+ this.assertNothingRaised(function(){ input.down('span') });
+ this.assertUndefined(input.down('span'));
+ },
+
+ testElementPrevious: function() {
+ var element = $('navigation_test').down('li.last');
+ this.assertElementMatches(element.previous(), 'li#navigation_test_c');
+ this.assertElementMatches(element.previous(1), 'li');
+ this.assertElementMatches(element.previous('.first'), 'li.first');
+ this.assertEqual(undefined, element.previous(3));
+ this.assertEqual(undefined, $('navigation_test').down().previous());
+ this.assertElementMatches(element.previous('.non-existant, .first'), 'li.first');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[1]).previous().setStyle == 'function');
+ },
+
+ testElementNext: function() {
+ var element = $('navigation_test').down('li.first');
+ this.assertElementMatches(element.next(), 'li');
+ this.assertElementMatches(element.next(1), 'li#navigation_test_c');
+ this.assertElementMatches(element.next(2), 'li.last');
+ this.assertElementMatches(element.next('.last'), 'li.last');
+ this.assertEqual(undefined, element.next(3));
+ this.assertEqual(undefined, element.next(2).next());
+ this.assertElementMatches(element.next('.non-existant, .last'), 'li.last');
+
+ var dummy = $(document.createElement('DIV'));
+ dummy.innerHTML = '<div></div>'.times(3);
+ this.assert(typeof $(dummy.childNodes[0]).next().setStyle == 'function');
+ },
+
+ testElementInspect: function() {
+ this.assertEqual('<ul id="navigation_test">', $('navigation_test').inspect());
+ this.assertEqual('<li class="first">', $('navigation_test').down().inspect());
+ this.assertEqual('<em>', $('navigation_test').down(1).inspect());
+ },
+
+ testElementMakeClipping: function() {
+ var chained = Element.extend(document.createElement('DIV'));
+ this.assertEqual(chained, chained.makeClipping());
+ this.assertEqual(chained, chained.makeClipping());
+ this.assertEqual(chained, chained.makeClipping().makeClipping());
+
+ this.assertEqual(chained, chained.undoClipping());
+ this.assertEqual(chained, chained.undoClipping());
+ this.assertEqual(chained, chained.undoClipping().makeClipping());
+
+ ['hidden','visible','scroll'].each( function(overflowValue) {
+ var element = $('element_with_'+overflowValue+'_overflow');
+
+ this.assertEqual(overflowValue, element.getStyle('overflow'));
+ element.makeClipping();
+ this.assertEqual('hidden', element.getStyle('overflow'));
+ element.undoClipping();
+ this.assertEqual(overflowValue, element.getStyle('overflow'));
+ }, this);
+ },
+
+ testElementExtend: function() {
+ var element = $('element_extend_test');
+ this.assertRespondsTo('show', element);
+
+ var XHTML_TAGS = $w(
+ 'a abbr acronym address applet area '+
+ 'b bdo big blockquote br button caption '+
+ 'cite code col colgroup dd del dfn div dl dt '+
+ 'em fieldset form h1 h2 h3 h4 h5 h6 hr '+
+ 'i iframe img input ins kbd label legend li '+
+ 'map object ol optgroup option p param pre q samp '+
+ 'script select small span strong style sub sup '+
+ 'table tbody td textarea tfoot th thead tr tt ul var');
+
+ XHTML_TAGS.each(function(tag) {
+ var element = document.createElement(tag);
+ this.assertEqual(element, Element.extend(element));
+ this.assertRespondsTo('show', element);
+ }, this);
+
+ [null,'','a','aa'].each(function(content) {
+ var textnode = document.createTextNode(content);
+ this.assertEqual(textnode, Element.extend(textnode));
+ this.assert(typeof textnode['show'] == 'undefined');
+ }, this);
+ },
+
+ testElementExtendReextendsDiscardedNodes: function() {
+ this.assertRespondsTo('show', $('discard_1'));
+ $('element_reextend_test').innerHTML += '<div id="discard_2"></div>';
+ this.assertRespondsTo('show', $('discard_1'));
+ },
+
+ testElementCleanWhitespace: function() {
+ Element.cleanWhitespace("test_whitespace");
+ this.assertEqual(3, $("test_whitespace").childNodes.length);
+
+ this.assertEqual(1, $("test_whitespace").firstChild.nodeType);
+ this.assertEqual('SPAN', $("test_whitespace").firstChild.tagName);
+
+ this.assertEqual(1, $("test_whitespace").firstChild.nextSibling.nodeType);
+ this.assertEqual('DIV', $("test_whitespace").firstChild.nextSibling.tagName);
+
+ this.assertEqual(1, $("test_whitespace").firstChild.nextSibling.nextSibling.nodeType);
+ this.assertEqual('SPAN', $("test_whitespace").firstChild.nextSibling.nextSibling.tagName);
+
+ var element = document.createElement('DIV');
+ element.appendChild(document.createTextNode(''));
+ element.appendChild(document.createTextNode(''));
+ this.assertEqual(2, element.childNodes.length);
+ Element.cleanWhitespace(element);
+ this.assertEqual(0, element.childNodes.length);
+ },
+
+ testElementEmpty: function() {
+ this.assert($('test-empty').empty());
+ this.assert($('test-empty-but-contains-whitespace').empty());
+ this.assert(!$('test-full').empty());
+ },
+
+ testDescendantOf: function() {
+ this.assert($('child').descendantOf('ancestor'));
+ this.assert($('child').descendantOf($('ancestor')));
+
+ this.assert(!$('ancestor').descendantOf($('child')));
+
+ this.assert($('great-grand-child').descendantOf('ancestor'), 'great-grand-child < ancestor');
+ this.assert($('grand-child').descendantOf('ancestor'), 'grand-child < ancestor');
+ this.assert($('great-grand-child').descendantOf('grand-child'), 'great-grand-child < grand-child');
+ this.assert($('grand-child').descendantOf('child'), 'grand-child < child');
+ this.assert($('great-grand-child').descendantOf('child'), 'great-grand-child < child');
+
+ this.assert($('sibling').descendantOf('ancestor'), 'sibling < ancestor');
+ this.assert($('grand-sibling').descendantOf('sibling'), 'grand-sibling < sibling');
+ this.assert($('grand-sibling').descendantOf('ancestor'), 'grand-sibling < ancestor');
+
+ this.assert($('grand-sibling').descendantOf(document.body), 'grand-sibling < body');
+
+ this.assert(!$('great-grand-child').descendantOf('great-grand-child'), 'great-grand-child < great-grand-child');
+ this.assert(!$('great-grand-child').descendantOf('sibling'), 'great-grand-child < sibling');
+ this.assert(!$('sibling').descendantOf('child'), 'sibling < child');
+ this.assert(!$('great-grand-child').descendantOf('not-in-the-family'), 'great-grand-child < not-in-the-family');
+ this.assert(!$('child').descendantOf('not-in-the-family'), 'child < not-in-the-family');
+
+ this.assert(!$(document.body).descendantOf('great-grand-child'));
+
+ // dynamically-created elements
+ $('ancestor').insert(new Element('div', { id: 'weird-uncle' }));
+ this.assert($('weird-uncle').descendantOf('ancestor'));
+
+ $(document.body).insert(new Element('div', { id: 'impostor' }));
+ this.assert(!$('impostor').descendantOf('ancestor'));
+
+ // test descendantOf document
+ this.assert($(document.body).descendantOf(document));
+ this.assert($(document.documentElement).descendantOf(document));
+ },
+
+ testChildOf: function() {
+ this.assert($('child').childOf('ancestor'));
+ this.assert($('child').childOf($('ancestor')));
+ this.assert($('great-grand-child').childOf('ancestor'));
+ this.assert(!$('great-grand-child').childOf('not-in-the-family'));
+ this.assertIdentical(Element.Methods.childOf, Element.Methods.descendantOf);
+ },
+
+ testElementSetStyle: function() {
+ Element.setStyle('style_test_3',{ 'left': '2px' });
+ this.assertEqual('2px', $('style_test_3').style.left);
+
+ Element.setStyle('style_test_3',{ marginTop: '1px' });
+ this.assertEqual('1px', $('style_test_3').style.marginTop);
+
+ $('style_test_3').setStyle({ marginTop: '2px', left: '-1px' });
+ this.assertEqual('-1px', $('style_test_3').style.left);
+ this.assertEqual('2px', $('style_test_3').style.marginTop);
+
+ this.assertEqual('none', $('style_test_3').getStyle('float'));
+ $('style_test_3').setStyle({ 'float': 'left' });
+ this.assertEqual('left', $('style_test_3').getStyle('float'));
+
+ $('style_test_3').setStyle({ cssFloat: 'none' });
+ this.assertEqual('none', $('style_test_3').getStyle('float'));
+
+ this.assertEqual(1, $('style_test_3').getStyle('opacity'));
+
+ $('style_test_3').setStyle({ opacity: 0.5 });
+ this.assertEqual(0.5, $('style_test_3').getStyle('opacity'));
+
+ $('style_test_3').setStyle({ opacity: '' });
+ this.assertEqual(1, $('style_test_3').getStyle('opacity'));
+
+ $('style_test_3').setStyle({ opacity: 0 });
+ this.assertEqual(0, $('style_test_3').getStyle('opacity'));
+
+ $('test_csstext_1').setStyle('font-size: 15px');
+ this.assertEqual('15px', $('test_csstext_1').getStyle('font-size'));
+
+ $('test_csstext_2').setStyle({height: '40px'});
+ $('test_csstext_2').setStyle('font-size: 15px');
+ this.assertEqual('15px', $('test_csstext_2').getStyle('font-size'));
+ this.assertEqual('40px', $('test_csstext_2').getStyle('height'));
+
+ $('test_csstext_3').setStyle('font-size: 15px');
+ this.assertEqual('15px', $('test_csstext_3').getStyle('font-size'));
+ this.assertEqual('1px', $('test_csstext_3').getStyle('border-top-width'));
+
+ $('test_csstext_4').setStyle('font-size: 15px');
+ this.assertEqual('15px', $('test_csstext_4').getStyle('font-size'));
+
+ $('test_csstext_4').setStyle('float: right; font-size: 10px');
+ this.assertEqual('right', $('test_csstext_4').getStyle('float'));
+ this.assertEqual('10px', $('test_csstext_4').getStyle('font-size'));
+
+ $('test_csstext_5').setStyle('float: left; opacity: .5; font-size: 10px');
+ this.assertEqual(parseFloat('0.5'), parseFloat($('test_csstext_5').getStyle('opacity')));
+ },
+
+ testElementSetStyleCamelized: function() {
+ this.assertNotEqual('30px', $('style_test_3').style.marginTop);
+ $('style_test_3').setStyle({ marginTop: '30px'}, true);
+ this.assertEqual('30px', $('style_test_3').style.marginTop);
+ },
+
+ testElementSetOpacity: function() {
+ [0,0.1,0.5,0.999].each(function(opacity){
+ $('style_test_3').setOpacity(opacity);
+
+ // b/c of rounding issues on IE special case
+ var realOpacity = $('style_test_3').getStyle('opacity');
+
+ // opera rounds off to two significant digits, so we check for a
+ // ballpark figure
+ this.assert((Number(realOpacity) - opacity) <= 0.002, 'setting opacity to ' + opacity);
+ }, this);
+
+ this.assertEqual(0,
+ $('style_test_3').setOpacity(0.0000001).getStyle('opacity'));
+
+ // for Firefox, we don't set to 1, because of flickering
+ this.assert(
+ $('style_test_3').setOpacity(0.9999999).getStyle('opacity') > 0.999
+ );
+ if (Prototype.Browser.IE) {
+ this.assert($('style_test_4').setOpacity(0.5).currentStyle.hasLayout);
+ this.assert(2, $('style_test_5').setOpacity(0.5).getStyle('zoom'));
+ this.assert(0.5, new Element('div').setOpacity(0.5).getOpacity());
+ this.assert(2, new Element('div').setOpacity(0.5).setStyle('zoom: 2;').getStyle('zoom'));
+ this.assert(2, new Element('div').setStyle('zoom: 2;').setOpacity(0.5).getStyle('zoom'));
+ }
+ },
+
+ testElementGetStyle: function() {
+ this.assertEqual("none",
+ $('style_test_1').getStyle('display'));
+
+ // not displayed, so "null" ("auto" is tranlated to "null")
+ this.assertNull(Element.getStyle('style_test_1', 'width'), 'elements that are hidden should return null on getStyle("width")');
+
+ $('style_test_1').show();
+
+ // from id rule
+ this.assertEqual("pointer",
+ Element.getStyle('style_test_1','cursor'));
+
+ this.assertEqual("block",
+ Element.getStyle('style_test_2','display'));
+
+ // we should always get something for width (if displayed)
+ // firefox and safari automatically send the correct value,
+ // IE is special-cased to do the same
+ this.assertEqual($('style_test_2').offsetWidth+'px', Element.getStyle('style_test_2','width'));
+
+ this.assertEqual("static",Element.getStyle('style_test_1','position'));
+ // from style
+ this.assertEqual("11px",
+ Element.getStyle('style_test_2','font-size'));
+ // from class
+ this.assertEqual("1px",
+ Element.getStyle('style_test_2','margin-left'));
+
+ ['not_floating_none','not_floating_style','not_floating_inline'].each(function(element) {
+ this.assertEqual('none', $(element).getStyle('float'));
+ this.assertEqual('none', $(element).getStyle('cssFloat'));
+ }, this);
+
+ ['floating_style','floating_inline'].each(function(element) {
+ this.assertEqual('left', $(element).getStyle('float'));
+ this.assertEqual('left', $(element).getStyle('cssFloat'));
+ }, this);
+
+ this.assertEqual(0.5, $('op1').getStyle('opacity'));
+ this.assertEqual(0.5, $('op2').getStyle('opacity'));
+ this.assertEqual(1.0, $('op3').getStyle('opacity'));
+
+ $('op1').setStyle({opacity: '0.3'});
+ $('op2').setStyle({opacity: '0.3'});
+ $('op3').setStyle({opacity: '0.3'});
+
+ this.assertEqual(0.3, $('op1').getStyle('opacity'));
+ this.assertEqual(0.3, $('op2').getStyle('opacity'));
+ this.assertEqual(0.3, $('op3').getStyle('opacity'));
+
+ $('op3').setStyle({opacity: 0});
+ this.assertEqual(0, $('op3').getStyle('opacity'));
+
+ if (navigator.appVersion.match(/MSIE/)) {
+ this.assertEqual('alpha(opacity=30)', $('op1').getStyle('filter'));
+ this.assertEqual('progid:DXImageTransform.Microsoft.Blur(strength=10)alpha(opacity=30)', $('op2').getStyle('filter'));
+ $('op2').setStyle({opacity:''});
+ this.assertEqual('progid:DXImageTransform.Microsoft.Blur(strength=10)', $('op2').getStyle('filter'));
+ this.assertEqual('alpha(opacity=0)', $('op3').getStyle('filter'));
+ this.assertEqual(0.3, $('op4-ie').getStyle('opacity'));
+ }
+ // verify that value is still found when using camelized
+ // strings (function previously used getPropertyValue()
+ // which expected non-camelized strings)
+ this.assertEqual("12px", $('style_test_1').getStyle('fontSize'));
+
+ // getStyle on width/height should return values according to
+ // the CSS box-model, which doesn't include
+ // margin, padding, or borders
+ // TODO: This test fails on IE because there seems to be no way
+ // to calculate this properly (clientWidth/Height returns 0)
+ if (!navigator.appVersion.match(/MSIE/)) {
+ this.assertEqual("14px", $('style_test_dimensions').getStyle('width'));
+ this.assertEqual("17px", $('style_test_dimensions').getStyle('height'));
+ }
+
+ // height/width could always be calculated if it's set to "auto" (Firefox)
+ this.assertNotNull($('auto_dimensions').getStyle('height'));
+ this.assertNotNull($('auto_dimensions').getStyle('width'));
+ },
+
+ testElementGetOpacity: function() {
+ this.assertEqual(0.45, $('op1').setOpacity(0.45).getOpacity());
+ },
+
+ testElementReadAttribute: function() {
+ var attribFormIssues = $('attributes_with_issues_form');
+ this.assertEqual('blah-class', attribFormIssues.readAttribute('class'));
+ this.assertEqual('post', attribFormIssues.readAttribute('method'));
+ this.assertEqual('string', typeof(attribFormIssues.readAttribute('action')));
+ this.assertEqual('string', typeof(attribFormIssues.readAttribute('id')));
+
+ $(document.body).insert('<div id="ie_href_test_div"></div>');
+ $('ie_href_test_div').insert('<p>blah blah</p><a id="ie_href_test" href="test.html">blah</a>');
+ this.assertEqual('test.html', $('ie_href_test').readAttribute('href'));
+
+ this.assertEqual('test.html' , $('attributes_with_issues_1').readAttribute('href'));
+ this.assertEqual('L' , $('attributes_with_issues_1').readAttribute('accesskey'));
+ this.assertEqual('50' , $('attributes_with_issues_1').readAttribute('tabindex'));
+ this.assertEqual('a link' , $('attributes_with_issues_1').readAttribute('title'));
+
+ $('cloned_element_attributes_issue').readAttribute('foo')
+ var clone = $('cloned_element_attributes_issue').cloneNode(true);
+ clone.writeAttribute('foo', 'cloned');
+ this.assertEqual('cloned', clone.readAttribute('foo'));
+ this.assertEqual('original', $('cloned_element_attributes_issue').readAttribute('foo'));
+
+ ['href', 'accesskey', 'accesskey', 'title'].each(function(attr) {
+ this.assertEqual('' , $('attributes_with_issues_2').readAttribute(attr));
+ }, this);
+
+ ['checked','disabled','readonly','multiple'].each(function(attr) {
+ this.assertEqual(attr, $('attributes_with_issues_'+attr).readAttribute(attr));
+ }, this);
+
+ this.assertEqual("alert('hello world');", $('attributes_with_issues_1').readAttribute('onclick'));
+ this.assertNull($('attributes_with_issues_1').readAttribute('onmouseover'));
+
+ this.assertEqual('date', $('attributes_with_issues_type').readAttribute('type'));
+ this.assertEqual('text', $('attributes_with_issues_readonly').readAttribute('type'));
+
+ var elements = $('custom_attributes').immediateDescendants();
+ this.assertEnumEqual(['1', '2'], elements.invoke('readAttribute', 'foo'));
+ this.assertEnumEqual(['2', null], elements.invoke('readAttribute', 'bar'));
+
+ var table = $('write_attribute_table');
+ this.assertEqual('4', table.readAttribute('cellspacing'));
+ this.assertEqual('6', table.readAttribute('cellpadding'));
+
+ // test for consistent flag value across browsers
+ ["true", true, " ", 'rEadOnLy'].each(function(value) {
+ $('attributes_with_issues_readonly').writeAttribute('readonly', value);
+ this.assertEqual('readonly', $('attributes_with_issues_readonly').readAttribute('readonly'));
+ }, this);
+ },
+
+ testElementWriteAttribute: function() {
+ var element = Element.extend(document.body.appendChild(document.createElement('p')));
+ this.assertRespondsTo('writeAttribute', element);
+ this.assertEqual(element, element.writeAttribute('id', 'write_attribute_test'));
+ this.assertEqual('write_attribute_test', element.id);
+ this.assertEqual('http://prototypejs.org/', $('write_attribute_link').
+ writeAttribute({href: 'http://prototypejs.org/', title: 'Home of Prototype'}).href);
+ this.assertEqual('Home of Prototype', $('write_attribute_link').title);
+
+ var element2 = Element.extend(document.createElement('p'));
+ element2.writeAttribute('id', 'write_attribute_without_hash');
+ this.assertEqual('write_attribute_without_hash', element2.id);
+ element2.writeAttribute('animal', 'cat');
+ this.assertEqual('cat', element2.readAttribute('animal'));
+ },
+
+ testElementWriteAttributeWithBooleans: function() {
+ var input = $('write_attribute_input'),
+ select = $('write_attribute_select'),
+ checkbox = $('write_attribute_checkbox'),
+ checkedCheckbox = $('write_attribute_checked_checkbox');
+ this.assert( input. writeAttribute('readonly'). hasAttribute('readonly'));
+ this.assert(!input. writeAttribute('readonly', false). hasAttribute('readonly'));
+ this.assert( input. writeAttribute('readonly', true). hasAttribute('readonly'));
+ this.assert(!input. writeAttribute('readonly', null). hasAttribute('readonly'));
+ this.assert( input. writeAttribute('readonly', 'readonly').hasAttribute('readonly'));
+ this.assert( select. writeAttribute('multiple'). hasAttribute('multiple'));
+ this.assert( input. writeAttribute('disabled'). hasAttribute('disabled'));
+ this.assert( checkbox. writeAttribute('checked'). checked);
+ this.assert(!checkedCheckbox.writeAttribute('checked', false). checked);
+ },
+
+ testElementWriteAttributeWithIssues: function() {
+ var input = $('write_attribute_input').writeAttribute({maxlength: 90, minlength:80, tabindex: 10}),
+ td = $('write_attribute_td').writeAttribute({valign: 'bottom', colspan: 2, rowspan: 2});
+ this.assertEqual(80, input.readAttribute('minlength'));
+ this.assertEqual(90, input.readAttribute('maxlength'));
+ this.assertEqual(10, input.readAttribute('tabindex'));
+ this.assertEqual(2, td.readAttribute('colspan'));
+ this.assertEqual(2, td.readAttribute('rowspan'));
+ this.assertEqual('bottom', td.readAttribute('valign'));
+
+ var p = $('write_attribute_para'), label = $('write_attribute_label');
+ this.assertEqual('some-class', p. writeAttribute({'class': 'some-class'}). readAttribute('class'));
+ this.assertEqual('some-className', p. writeAttribute({className: 'some-className'}).readAttribute('class'));
+ this.assertEqual('some-id', label.writeAttribute({'for': 'some-id'}). readAttribute('for'));
+ this.assertEqual('some-other-id', label.writeAttribute({htmlFor: 'some-other-id'}). readAttribute('for'));
+
+ this.assert(p.writeAttribute({style: 'width: 5px;'}).readAttribute('style').toLowerCase().include('width'));
+
+ var table = $('write_attribute_table');
+ table.writeAttribute('cellspacing', '2')
+ table.writeAttribute('cellpadding', '3')
+ this.assertEqual('2', table.readAttribute('cellspacing'));
+ this.assertEqual('3', table.readAttribute('cellpadding'));
+
+ var iframe = new Element('iframe', { frameborder: 0 });
+ this.assertIdentical(0, parseInt(iframe.readAttribute('frameborder')));
+ },
+
+ testElementWriteAttributeWithCustom: function() {
+ var p = $('write_attribute_para').writeAttribute({name: 'martin', location: 'stockholm', age: 26});
+ this.assertEqual('martin', p.readAttribute('name'));
+ this.assertEqual('stockholm', p.readAttribute('location'));
+ this.assertEqual('26', p.readAttribute('age'));
+ },
+
+ testElementHasAttribute: function() {
+ var label = $('write_attribute_label');
+ this.assertIdentical(true, label.hasAttribute('for'));
+ this.assertIdentical(false, label.hasAttribute('htmlFor'));
+ this.assertIdentical(false, label.hasAttribute('className'));
+ this.assertIdentical(false, label.hasAttribute('rainbows'));
+
+ var input = $('write_attribute_input');
+ this.assertNotIdentical(null, input.hasAttribute('readonly'));
+ this.assertNotIdentical(null, input.hasAttribute('readOnly'));
+ },
+
+ testNewElement: function() {
+ this.assert(new Element('h1'));
+
+ var XHTML_TAGS = $w(
+ 'a abbr acronym address area '+
+ 'b bdo big blockquote br button caption '+
+ 'cite code col colgroup dd del dfn div dl dt '+
+ 'em fieldset form h1 h2 h3 h4 h5 h6 hr '+
+ 'i iframe img input ins kbd label legend li '+
+ 'map object ol optgroup option p param pre q samp '+
+ 'script select small span strong style sub sup '+
+ 'table tbody td textarea tfoot th thead tr tt ul var');
+
+ XHTML_TAGS.each(function(tag, index) {
+ var id = tag + '_' + index, element = document.body.appendChild(new Element(tag, {id: id}));
+ this.assertEqual(tag, element.tagName.toLowerCase());
+ this.assertEqual(element, document.body.lastChild);
+ this.assertEqual(id, element.id);
+ }, this);
+
+
+ this.assertRespondsTo('update', new Element('div'));
+ Element.addMethods({
+ cheeseCake: function(){
+ return 'Cheese cake';
+ }
+ });
+
+ this.assertRespondsTo('cheeseCake', new Element('div'));
+
+ /* window.ElementOld = function(tagName, attributes) {
+ if (Prototype.Browser.IE && attributes && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ }
+ return Element.extend(document.createElement(tagName)).writeAttribute(attributes || {});
+ };
+
+ this.benchmark(function(){
+ XHTML_TAGS.each(function(tagName) { new Element(tagName) });
+ }, 5);
+
+ this.benchmark(function(){
+ XHTML_TAGS.each(function(tagName) { new ElementOld(tagName) });
+ }, 5); */
+
+ this.assertEqual('foobar', new Element('a', {custom: 'foobar'}).readAttribute('custom'));
+ var input = document.body.appendChild(new Element('input',
+ {id: 'my_input_field_id', name: 'my_input_field'}));
+ this.assertEqual(input, document.body.lastChild);
+ this.assertEqual('my_input_field', $(document.body.lastChild).name);
+ if (Prototype.Browser.IE)
+ this.assertMatch(/name=["']?my_input_field["']?/, $('my_input_field').outerHTML);
+
+ if (originalElement && Prototype.BrowserFeatures.ElementExtensions) {
+ Element.prototype.fooBar = Prototype.emptyFunction
+ this.assertRespondsTo('fooBar', new Element('div'));
+ }
+
+ //test IE setting "type" property of newly created button element
+ var button = new Element('button', {id:'button_type_test',type: 'reset'});
+ var form = $('attributes_with_issues_form');
+ var input = $('attributes_with_issues_regular');
+
+ form.insert(button);
+ input.value = 1;
+ button.click();
+
+ this.assertEqual('0', input.value);
+ button.remove();
+ },
+
+ testElementGetHeight: function() {
+ this.assertIdentical(100, $('dimensions-visible').getHeight());
+ this.assertIdentical(100, $('dimensions-display-none').getHeight());
+ },
+
+ testElementGetWidth: function() {
+ this.assertIdentical(200, $('dimensions-visible').getWidth());
+ this.assertIdentical(200, $('dimensions-display-none').getWidth());
+ },
+
+ testElementGetDimensions: function() {
+ this.assertIdentical(100, $('dimensions-visible').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-visible').getDimensions().width);
+ this.assertIdentical(100, $('dimensions-display-none').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-display-none').getDimensions().width);
+
+ this.assertIdentical(100, $('dimensions-visible-pos-rel').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-visible-pos-rel').getDimensions().width);
+ this.assertIdentical(100, $('dimensions-display-none-pos-rel').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-display-none-pos-rel').getDimensions().width);
+
+ this.assertIdentical(100, $('dimensions-visible-pos-abs').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-visible-pos-abs').getDimensions().width);
+ this.assertIdentical(100, $('dimensions-display-none-pos-abs').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-display-none-pos-abs').getDimensions().width);
+
+ // known failing issue
+ // this.assert($('dimensions-nestee').getDimensions().width <= 500, 'check for proper dimensions of hidden child elements');
+
+ $('dimensions-td').hide();
+ this.assertIdentical(100, $('dimensions-td').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-td').getDimensions().width);
+ $('dimensions-td').show();
+
+ $('dimensions-tr').hide();
+ this.assertIdentical(100, $('dimensions-tr').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-tr').getDimensions().width);
+ $('dimensions-tr').show();
+
+ $('dimensions-table').hide();
+ this.assertIdentical(100, $('dimensions-table').getDimensions().height);
+ this.assertIdentical(200, $('dimensions-table').getDimensions().width);
+
+ },
+
+ testDOMAttributesHavePrecedenceOverExtendedElementMethods: function() {
+ this.assertNothingRaised(function() { $('dom_attribute_precedence').down('form') });
+ this.assertEqual($('dom_attribute_precedence').down('input'), $('dom_attribute_precedence').down('form').update);
+ },
+
+ testClassNames: function() {
+ this.assertEnumEqual([], $('class_names').classNames());
+ this.assertEnumEqual(['A'], $('class_names').down().classNames());
+ this.assertEnumEqual(['A', 'B'], $('class_names_ul').classNames());
+ },
+
+ testHasClassName: function() {
+ this.assertIdentical(false, $('class_names').hasClassName('does_not_exist'));
+ this.assertIdentical(true, $('class_names').down().hasClassName('A'));
+ this.assertIdentical(false, $('class_names').down().hasClassName('does_not_exist'));
+ this.assertIdentical(true, $('class_names_ul').hasClassName('A'));
+ this.assertIdentical(true, $('class_names_ul').hasClassName('B'));
+ this.assertIdentical(false, $('class_names_ul').hasClassName('does_not_exist'));
+ },
+
+ testAddClassName: function() {
+ $('class_names').addClassName('added_className');
+ this.assertEnumEqual(['added_className'], $('class_names').classNames());
+
+ $('class_names').addClassName('added_className'); // verify that className cannot be added twice.
+ this.assertEnumEqual(['added_className'], $('class_names').classNames());
+
+ $('class_names').addClassName('another_added_className');
+ this.assertEnumEqual(['added_className', 'another_added_className'], $('class_names').classNames());
+ },
+
+ testRemoveClassName: function() {
+ $('class_names').removeClassName('added_className');
+ this.assertEnumEqual(['another_added_className'], $('class_names').classNames());
+
+ $('class_names').removeClassName('added_className'); // verify that removing a non existent className is safe.
+ this.assertEnumEqual(['another_added_className'], $('class_names').classNames());
+
+ $('class_names').removeClassName('another_added_className');
+ this.assertEnumEqual([], $('class_names').classNames());
+ },
+
+ testToggleClassName: function() {
+ $('class_names').toggleClassName('toggled_className');
+ this.assertEnumEqual(['toggled_className'], $('class_names').classNames());
+
+ $('class_names').toggleClassName('toggled_className');
+ this.assertEnumEqual([], $('class_names').classNames());
+
+ $('class_names_ul').toggleClassName('toggled_className');
+ this.assertEnumEqual(['A', 'B', 'toggled_className'], $('class_names_ul').classNames());
+
+ $('class_names_ul').toggleClassName('toggled_className');
+ this.assertEnumEqual(['A', 'B'], $('class_names_ul').classNames());
+ },
+
+ testElementScrollTo: function() {
+ var elem = $('scroll_test_2');
+ Element.scrollTo('scroll_test_2');
+ this.assertEqual(Position.page(elem)[1], 0);
+ window.scrollTo(0, 0);
+
+ elem.scrollTo();
+ this.assertEqual(Position.page(elem)[1], 0);
+ window.scrollTo(0, 0);
+ },
+
+ testCustomElementMethods: function() {
+ var elem = $('navigation_test_f');
+ this.assertRespondsTo('hashBrowns', elem);
+ this.assertEqual('hash browns', elem.hashBrowns());
+
+ this.assertRespondsTo('hashBrowns', Element);
+ this.assertEqual('hash browns', Element.hashBrowns(elem));
+ },
+
+ testSpecificCustomElementMethods: function() {
+ var elem = $('navigation_test_f');
+
+ this.assert(Element.Methods.ByTag[elem.tagName]);
+ this.assertRespondsTo('pancakes', elem);
+ this.assertEqual("pancakes", elem.pancakes());
+
+ var elem2 = $('test-visible');
+
+ this.assert(Element.Methods.ByTag[elem2.tagName]);
+ this.assertUndefined(elem2.pancakes);
+ this.assertRespondsTo('waffles', elem2);
+ this.assertEqual("waffles", elem2.waffles());
+
+ this.assertRespondsTo('orangeJuice', elem);
+ this.assertRespondsTo('orangeJuice', elem2);
+ this.assertEqual("orange juice", elem.orangeJuice());
+ this.assertEqual("orange juice", elem2.orangeJuice());
+
+ this.assert(typeof Element.orangeJuice == 'undefined');
+ this.assert(typeof Element.pancakes == 'undefined');
+ this.assert(typeof Element.waffles == 'undefined');
+
+ },
+
+ testScriptFragment: function() {
+ var element = document.createElement('div');
+ // tests an issue with Safari 2.0 crashing when the ScriptFragment
+ // regular expression is using a pipe-based approach for
+ // matching any character
+ ['\r','\n',' '].each(function(character){
+ $(element).update("<script>"+character.times(10000)+"</scr"+"ipt>");
+ this.assertEqual('', element.innerHTML);
+ }, this);
+ $(element).update("<script>var blah='"+'\\'.times(10000)+"'</scr"+"ipt>");
+ this.assertEqual('', element.innerHTML);
+ },
+
+ testPositionedOffset: function() {
+ this.assertEnumEqual([10,10],
+ $('body_absolute').positionedOffset());
+ this.assertEnumEqual([10,10],
+ $('absolute_absolute').positionedOffset());
+ this.assertEnumEqual([10,10],
+ $('absolute_relative').positionedOffset());
+ this.assertEnumEqual([0,10],
+ $('absolute_relative_undefined').positionedOffset());
+ this.assertEnumEqual([10,10],
+ $('absolute_fixed_absolute').positionedOffset());
+
+ var afu = $('absolute_fixed_undefined');
+ this.assertEnumEqual([afu.offsetLeft, afu.offsetTop],
+ afu.positionedOffset());
+
+ var element = new Element('div'), offset = element.positionedOffset();
+ this.assertEnumEqual([0,0], offset);
+ this.assertIdentical(0, offset.top);
+ this.assertIdentical(0, offset.left);
+ },
+
+ testCumulativeOffset: function() {
+ var element = new Element('div'), offset = element.cumulativeOffset();
+ this.assertEnumEqual([0,0], offset);
+ this.assertIdentical(0, offset.top);
+ this.assertIdentical(0, offset.left);
+ },
+
+ testViewportOffset: function() {
+ this.assertEnumEqual([10,10],
+ $('body_absolute').viewportOffset());
+ this.assertEnumEqual([20,20],
+ $('absolute_absolute').viewportOffset());
+ this.assertEnumEqual([20,20],
+ $('absolute_relative').viewportOffset());
+ this.assertEnumEqual([20,30],
+ $('absolute_relative_undefined').viewportOffset());
+ var element = new Element('div'), offset = element.viewportOffset();
+ this.assertEnumEqual([0,0], offset);
+ this.assertIdentical(0, offset.top);
+ this.assertIdentical(0, offset.left);
+ },
+
+ testOffsetParent: function() {
+ this.assertEqual('body_absolute', $('absolute_absolute').getOffsetParent().id);
+ this.assertEqual('body_absolute', $('absolute_relative').getOffsetParent().id);
+ this.assertEqual('absolute_relative', $('inline').getOffsetParent().id);
+ this.assertEqual('absolute_relative', $('absolute_relative_undefined').getOffsetParent().id);
+
+ this.assertEqual(document.body, new Element('div').getOffsetParent());
+ },
+
+ testAbsolutize: function() {
+ $('notInlineAbsoluted', 'inlineAbsoluted').each(function(elt) {
+ if ('_originalLeft' in elt) delete elt._originalLeft;
+ elt.absolutize();
+ this.assertUndefined(elt._originalLeft, 'absolutize() did not detect absolute positioning');
+ }, this);
+ // invoking on "absolute" positioned element should return element
+ var element = $('absolute_fixed_undefined').setStyle({position: 'absolute'});
+ this.assertEqual(element, element.absolutize());
+
+ // test relatively positioned element with no height specified for IE7
+ var element = $('absolute_relative'), dimensions = element.getDimensions();
+ element.absolutize();
+ this.assertIdentical(dimensions.width, element.getDimensions().width);
+ this.assertIdentical(dimensions.height, element.getDimensions().height);
+ },
+
+ testRelativize: function() {
+ // invoking on "relative" positioned element should return element
+ var element = $('absolute_fixed_undefined').setStyle({position: 'relative'});
+ this.assertEqual(element, element.relativize());
+
+ var assertPositionEqual = function(modifier, element) {
+ element = $(element);
+ var offsets = element.cumulativeOffset();
+ Element[modifier](element);
+ this.assertEnumEqual(offsets, element.cumulativeOffset());
+ }.bind(this);
+
+ var testRelativize = assertPositionEqual.curry('relativize');
+ testRelativize('notInlineAbsoluted');
+ testRelativize('inlineAbsoluted');
+ testRelativize('absolute_absolute');
+ },
+
+ testElementToViewportDimensionsDoesNotAffectDocumentProperties: function() {
+ // No properties on the document should be affected when resizing
+ // an absolute positioned(0,0) element to viewport dimensions
+ var vd = document.viewport.getDimensions();
+
+ var before = documentViewportProperties.inspect();
+ $('elementToViewportDimensions').setStyle({ height: vd.height + 'px', width: vd.width + 'px' }).show();
+ var after = documentViewportProperties.inspect();
+ $('elementToViewportDimensions').hide();
+
+ documentViewportProperties.properties.each(function(prop) {
+ this.assertEqual(before[prop], after[prop], prop + ' was affected');
+ }, this);
+ },
+
+ testNodeConstants: function() {
+ this.assert(window.Node, 'window.Node is unavailable');
+
+ var constants = $H({
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+
+ constants.each(function(pair) {
+ this.assertEqual(Node[pair.key], pair.value);
+ }, this);
+ }
+});
+
+function preservingBrowserDimensions(callback) {
+ var original = document.viewport.getDimensions();
+ window.resizeTo(640, 480);
+ var resized = document.viewport.getDimensions();
+ original.width += 640 - resized.width, original.height += 480 - resized.height;
+
+ try {
+ window.resizeTo(original.width, original.height);
+ callback();
+ } finally {
+ window.resizeTo(original.width, original.height);
+ }
+}
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/element_mixins_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/element_mixins_test.js
new file mode 100644
index 0000000000..239179375c
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/element_mixins_test.js
@@ -0,0 +1,32 @@
+new Test.Unit.Runner({
+ testInput: function() {
+ this.assert($("input").present != null);
+ this.assert(typeof $("input").present == 'function');
+ this.assert($("input").select != null);
+ this.assertRespondsTo('present', Form.Element);
+ this.assertRespondsTo('present', Form.Element.Methods);
+ this.assertRespondsTo('coffee', $('input'));
+ this.assertIdentical(Prototype.K, Form.Element.coffee);
+ this.assertIdentical(Prototype.K, Form.Element.Methods.coffee);
+ },
+
+ testForm: function() {
+ this.assert($("form").reset != null);
+ this.assert($("form").getInputs().length == 2);
+ },
+
+ testEvent: function() {
+ this.assert($("form").observe != null)
+ // Can't really test this one with TestUnit...
+ $('form').observe("submit", function(e) {
+ alert("yeah!");
+ Event.stop(e);
+ });
+ },
+
+ testCollections: function() {
+ this.assert($$("input").all(function(input) {
+ return (input.focus != null);
+ }));
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/enumerable_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/enumerable_test.js
new file mode 100644
index 0000000000..fdc7c019d0
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/enumerable_test.js
@@ -0,0 +1,263 @@
+function prime(value) {
+ for (var i = 2; i < value; i++)
+ if (value % i == 0) return false;
+ return true;
+}
+
+new Test.Unit.Runner({
+ testEachBreak: function() {
+ var result = 0;
+ Fixtures.Basic.each(function(value) {
+ if ((result = value) == 2) throw $break;
+ });
+
+ this.assertEqual(2, result);
+ },
+
+ testEachReturnActsAsContinue: function() {
+ var results = [];
+ Fixtures.Basic.each(function(value) {
+ if (value == 2) return;
+ results.push(value);
+ });
+
+ this.assertEqual('1, 3', results.join(', '));
+ },
+
+ testEachChaining: function() {
+ this.assertEqual(Fixtures.Primes, Fixtures.Primes.each(Prototype.emptyFunction));
+ this.assertEqual(3, Fixtures.Basic.each(Prototype.emptyFunction).length);
+ },
+
+ testEnumContext: function() {
+ var results = [];
+ Fixtures.Basic.each(function(value) {
+ results.push(value * this.i);
+ }, { i: 2 });
+
+ this.assertEqual('2 4 6', results.join(' '));
+
+ this.assert(Fixtures.Basic.all(function(value){
+ return value >= this.min && value <= this.max;
+ }, { min: 1, max: 3 }));
+ this.assert(!Fixtures.Basic.all(function(value){
+ return value >= this.min && value <= this.max;
+ }));
+ this.assert(Fixtures.Basic.any(function(value){
+ return value == this.target_value;
+ }, { target_value: 2 }));
+ },
+
+ testAny: function() {
+ this.assert(!([].any()));
+
+ this.assert([true, true, true].any());
+ this.assert([true, false, false].any());
+ this.assert(![false, false, false].any());
+
+ this.assert(Fixtures.Basic.any(function(value) {
+ return value > 2;
+ }));
+ this.assert(!Fixtures.Basic.any(function(value) {
+ return value > 5;
+ }));
+ },
+
+ testAll: function() {
+ this.assert([].all());
+
+ this.assert([true, true, true].all());
+ this.assert(![true, false, false].all());
+ this.assert(![false, false, false].all());
+
+ this.assert(Fixtures.Basic.all(function(value) {
+ return value > 0;
+ }));
+ this.assert(!Fixtures.Basic.all(function(value) {
+ return value > 1;
+ }));
+ },
+
+ testCollect: function() {
+ this.assertEqual(Fixtures.Nicknames.join(', '),
+ Fixtures.People.collect(function(person) {
+ return person.nickname;
+ }).join(", "));
+
+ this.assertEqual(26, Fixtures.Primes.map().length);
+ },
+
+ testDetect: function() {
+ this.assertEqual('Marcel Molina Jr.',
+ Fixtures.People.detect(function(person) {
+ return person.nickname.match(/no/);
+ }).name);
+ },
+
+ testEachSlice: function() {
+ this.assertEnumEqual([], [].eachSlice(2));
+ this.assertEqual(1, [1].eachSlice(1).length);
+ this.assertEnumEqual([1], [1].eachSlice(1)[0]);
+ this.assertEqual(2, Fixtures.Basic.eachSlice(2).length);
+ this.assertEnumEqual(
+ [3, 2, 1, 11, 7, 5, 19, 17, 13, 31, 29, 23, 43, 41, 37, 59, 53, 47, 71, 67, 61, 83, 79, 73, 97, 89],
+ Fixtures.Primes.eachSlice( 3, function(slice){ return slice.reverse() }).flatten()
+ );
+ this.assertEnumEqual(Fixtures.Basic, Fixtures.Basic.eachSlice(-10));
+ this.assertEnumEqual(Fixtures.Basic, Fixtures.Basic.eachSlice(0));
+ this.assertNotIdentical(Fixtures.Basic, Fixtures.Basic.eachSlice(0));
+ },
+
+ testEachWithIndex: function() {
+ var nicknames = [], indexes = [];
+ Fixtures.People.each(function(person, index) {
+ nicknames.push(person.nickname);
+ indexes.push(index);
+ });
+
+ this.assertEqual(Fixtures.Nicknames.join(', '),
+ nicknames.join(', '));
+ this.assertEqual('0, 1, 2, 3', indexes.join(', '));
+ },
+
+ testFindAll: function() {
+ this.assertEqual(Fixtures.Primes.join(', '),
+ Fixtures.Z.findAll(prime).join(', '));
+ },
+
+ testGrep: function() {
+ this.assertEqual('noradio, htonl',
+ Fixtures.Nicknames.grep(/o/).join(", "));
+
+ this.assertEqual('NORADIO, HTONL',
+ Fixtures.Nicknames.grep(/o/, function(nickname) {
+ return nickname.toUpperCase();
+ }).join(", "))
+
+ this.assertEnumEqual($('grepHeader', 'grepCell'),
+ $('grepTable', 'grepTBody', 'grepRow', 'grepHeader', 'grepCell').grep(new Selector('.cell')));
+ },
+
+ testInclude: function() {
+ this.assert(Fixtures.Nicknames.include('sam-'));
+ this.assert(Fixtures.Nicknames.include('noradio'));
+ this.assert(!Fixtures.Nicknames.include('gmosx'));
+ this.assert(Fixtures.Basic.include(2));
+ this.assert(Fixtures.Basic.include('2'));
+ this.assert(!Fixtures.Basic.include('4'));
+ },
+
+ testInGroupsOf: function() {
+ this.assertEnumEqual([], [].inGroupsOf(3));
+
+ var arr = [1, 2, 3, 4, 5, 6].inGroupsOf(3);
+ this.assertEqual(2, arr.length);
+ this.assertEnumEqual([1, 2, 3], arr[0]);
+ this.assertEnumEqual([4, 5, 6], arr[1]);
+
+ arr = [1, 2, 3, 4, 5, 6].inGroupsOf(4);
+ this.assertEqual(2, arr.length);
+ this.assertEnumEqual([1, 2, 3, 4], arr[0]);
+ this.assertEnumEqual([5, 6, null, null], arr[1]);
+
+ var basic = Fixtures.Basic
+
+ arr = basic.inGroupsOf(4,'x');
+ this.assertEqual(1, arr.length);
+ this.assertEnumEqual([1, 2, 3, 'x'], arr[0]);
+
+ this.assertEnumEqual([1,2,3,'a'], basic.inGroupsOf(2, 'a').flatten());
+
+ arr = basic.inGroupsOf(5, '');
+ this.assertEqual(1, arr.length);
+ this.assertEnumEqual([1, 2, 3, '', ''], arr[0]);
+
+ this.assertEnumEqual([1,2,3,0], basic.inGroupsOf(2, 0).flatten());
+ this.assertEnumEqual([1,2,3,false], basic.inGroupsOf(2, false).flatten());
+ },
+
+ testInject: function() {
+ this.assertEqual(1061,
+ Fixtures.Primes.inject(0, function(sum, value) {
+ return sum + value;
+ }));
+ },
+
+ testInvoke: function() {
+ var result = [[2, 1, 3], [6, 5, 4]].invoke('sort');
+ this.assertEqual(2, result.length);
+ this.assertEqual('1, 2, 3', result[0].join(', '));
+ this.assertEqual('4, 5, 6', result[1].join(', '));
+
+ result = result.invoke('invoke', 'toString', 2);
+ this.assertEqual('1, 10, 11', result[0].join(', '));
+ this.assertEqual('100, 101, 110', result[1].join(', '));
+ },
+
+ testMax: function() {
+ this.assertEqual(100, Fixtures.Z.max());
+ this.assertEqual(97, Fixtures.Primes.max());
+ this.assertEqual(2, [ -9, -8, -7, -6, -4, -3, -2, 0, -1, 2 ].max());
+ this.assertEqual('sam-', Fixtures.Nicknames.max()); // ?s > ?U
+ },
+
+ testMin: function() {
+ this.assertEqual(1, Fixtures.Z.min());
+ this.assertEqual(0, [ 1, 2, 3, 4, 5, 6, 7, 8, 0, 9 ].min());
+ this.assertEqual('Ulysses', Fixtures.Nicknames.min()); // ?U < ?h
+ },
+
+ testPartition: function() {
+ var result = Fixtures.People.partition(function(person) {
+ return person.name.length < 15;
+ }).invoke('pluck', 'nickname');
+
+ this.assertEqual(2, result.length);
+ this.assertEqual('sam-, htonl', result[0].join(', '));
+ this.assertEqual('noradio, Ulysses', result[1].join(', '));
+ },
+
+ testPluck: function() {
+ this.assertEqual(Fixtures.Nicknames.join(', '),
+ Fixtures.People.pluck('nickname').join(', '));
+ },
+
+ testReject: function() {
+ this.assertEqual(0,
+ Fixtures.Nicknames.reject(Prototype.K).length);
+
+ this.assertEqual('sam-, noradio, htonl',
+ Fixtures.Nicknames.reject(function(nickname) {
+ return nickname != nickname.toLowerCase();
+ }).join(', '));
+ },
+
+ testSortBy: function() {
+ this.assertEqual('htonl, noradio, sam-, Ulysses',
+ Fixtures.People.sortBy(function(value) {
+ return value.nickname.toLowerCase();
+ }).pluck('nickname').join(', '));
+ },
+
+ testToArray: function() {
+ var result = Fixtures.People.toArray();
+ this.assert(result != Fixtures.People); // they're different objects...
+ this.assertEqual(Fixtures.Nicknames.join(', '),
+ result.pluck('nickname').join(', ')); // but the values are the same
+ },
+
+ testZip: function() {
+ var result = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
+ this.assertEqual('[[1, 4, 7], [2, 5, 8], [3, 6, 9]]', result.inspect());
+
+ result = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) { return array.reverse() });
+ this.assertEqual('[[7, 4, 1], [8, 5, 2], [9, 6, 3]]', result.inspect());
+ },
+
+ testSize: function() {
+ this.assertEqual(4, Fixtures.People.size());
+ this.assertEqual(4, Fixtures.Nicknames.size());
+ this.assertEqual(26, Fixtures.Primes.size());
+ this.assertEqual(0, [].size());
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/event_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/event_test.js
new file mode 100644
index 0000000000..ae10a77a7f
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/event_test.js
@@ -0,0 +1,286 @@
+var documentLoaded = document.loaded;
+
+new Test.Unit.Runner({
+
+ // test firing an event and observing it on the element it's fired from
+ testCustomEventFiring: function() {
+ var span = $("span"), fired = false, observer = function(event) {
+ this.assertEqual(span, event.element());
+ this.assertEqual(1, event.memo.index);
+ fired = true;
+ }.bind(this);
+
+ span.observe("test:somethingHappened", observer);
+ span.fire("test:somethingHappened", { index: 1 });
+ this.assert(fired);
+
+ fired = false;
+ span.fire("test:somethingElseHappened");
+ this.assert(!fired);
+
+ span.stopObserving("test:somethingHappened", observer);
+ span.fire("test:somethingHappened");
+ this.assert(!fired);
+ },
+
+ // test firing an event and observing it on a containing element
+ testCustomEventBubbling: function() {
+ var span = $("span"), outer = $("outer"), fired = false, observer = function(event) {
+ this.assertEqual(span, event.element());
+ fired = true;
+ }.bind(this);
+
+ outer.observe("test:somethingHappened", observer);
+ span.fire("test:somethingHappened");
+ this.assert(fired);
+
+ fired = false;
+ span.fire("test:somethingElseHappened");
+ this.assert(!fired);
+
+ outer.stopObserving("test:somethingHappened", observer);
+ span.fire("test:somethingHappened");
+ this.assert(!fired);
+ },
+
+ testCustomEventCanceling: function() {
+ var span = $("span"), outer = $("outer"), inner = $("inner");
+ var fired = false, stopped = false;
+
+ function outerObserver(event) {
+ fired = span == event.element();
+ }
+
+ function innerObserver(event) {
+ event.stop();
+ stopped = true;
+ }
+
+ inner.observe("test:somethingHappened", innerObserver);
+ outer.observe("test:somethingHappened", outerObserver);
+ span.fire("test:somethingHappened");
+ this.assert(stopped);
+ this.assert(!fired);
+
+ fired = stopped = false;
+ inner.stopObserving("test:somethingHappened", innerObserver);
+ span.fire("test:somethingHappened");
+ this.assert(!stopped);
+ this.assert(fired);
+
+ outer.stopObserving("test:somethingHappened", outerObserver);
+ },
+
+ testEventObjectIsExtended: function() {
+ var span = $("span"), event, observedEvent, observer = function(e) { observedEvent = e };
+ span.observe("test:somethingHappened", observer);
+ event = span.fire("test:somethingHappened");
+ this.assertEqual(event, observedEvent);
+ this.assertEqual(Event.Methods.stop.methodize(), event.stop);
+ span.stopObserving("test:somethingHappened", observer);
+
+ event = span.fire("test:somethingHappenedButNoOneIsListening");
+ this.assertEqual(Event.Methods.stop.methodize(), event.stop);
+ },
+
+ testEventObserversAreBoundToTheObservedElement: function() {
+ var span = $("span"), target, observer = function() { target = this };
+
+ span.observe("test:somethingHappened", observer);
+ span.fire("test:somethingHappened");
+ span.stopObserving("test:somethingHappened", observer);
+ this.assertEqual(span, target);
+ target = null;
+
+ var outer = $("outer");
+ outer.observe("test:somethingHappened", observer);
+ span.fire("test:somethingHappened");
+ outer.stopObserving("test:somethingHappened", observer);
+ this.assertEqual(outer, target);
+ },
+
+ testMultipleCustomEventObserversWithTheSameHandler: function() {
+ var span = $("span"), count = 0, observer = function() { count++ };
+
+ span.observe("test:somethingHappened", observer);
+ span.observe("test:somethingElseHappened", observer);
+ span.fire("test:somethingHappened");
+ this.assertEqual(1, count);
+ span.fire("test:somethingElseHappened");
+ this.assertEqual(2, count);
+ span.stopObserving("test:somethingHappened", observer);
+ span.stopObserving("test:somethingElseHappened", observer);
+ },
+
+ testStopObservingWithoutArguments: function() {
+ var span = $("span"), count = 0, observer = function() { count++ };
+
+ span.observe("test:somethingHappened", observer);
+ span.observe("test:somethingElseHappened", observer);
+ span.stopObserving();
+ span.fire("test:somethingHappened");
+ this.assertEqual(0, count);
+ span.fire("test:somethingElseHappened");
+ this.assertEqual(0, count);
+
+ this.assertEqual(window, Event.stopObserving(window));
+
+ // test element with no observers
+ this.assertNothingRaised(function() { $(document.body).stopObserving() });
+ },
+
+ testStopObservingWithoutHandlerArgument: function() {
+ var span = $("span"), count = 0, observer = function() { count++ };
+
+ span.observe("test:somethingHappened", observer);
+ span.observe("test:somethingElseHappened", observer);
+ span.stopObserving("test:somethingHappened");
+ span.fire("test:somethingHappened");
+ this.assertEqual(0, count);
+ span.fire("test:somethingElseHappened");
+ this.assertEqual(1, count);
+ span.stopObserving("test:somethingElseHappened");
+ span.fire("test:somethingElseHappened");
+ this.assertEqual(1, count);
+
+ // test element with no observers
+ this.assertNothingRaised(function() { $(document.body).stopObserving("test:somethingHappened") });
+ },
+
+ testStopObservingRemovesHandlerFromCache: function() {
+ var span = $("span"), observer = function() { }, eventID;
+
+ span.observe("test:somethingHappened", observer);
+ eventID = span._prototypeEventID;
+
+ this.assert(Event.cache[eventID]);
+ this.assert(Object.isArray(Event.cache[eventID]["test:somethingHappened"]));
+ this.assertEqual(1, Event.cache[eventID]["test:somethingHappened"].length);
+
+ span.stopObserving("test:somethingHappened", observer);
+ this.assert(Event.cache[eventID]);
+ this.assert(Object.isArray(Event.cache[eventID]["test:somethingHappened"]));
+ this.assertEqual(0, Event.cache[eventID]["test:somethingHappened"].length);
+ },
+
+ testObserveAndStopObservingAreChainable: function() {
+ var span = $("span"), observer = function() { };
+
+ this.assertEqual(span, span.observe("test:somethingHappened", observer));
+ this.assertEqual(span, span.stopObserving("test:somethingHappened", observer));
+
+ span.observe("test:somethingHappened", observer);
+ this.assertEqual(span, span.stopObserving("test:somethingHappened"));
+
+ span.observe("test:somethingHappened", observer);
+ this.assertEqual(span, span.stopObserving());
+ this.assertEqual(span, span.stopObserving()); // assert it again, after there are no observers
+
+ span.observe("test:somethingHappened", observer);
+ this.assertEqual(span, span.observe("test:somethingHappened", observer)); // try to reuse the same observer
+ span.stopObserving();
+ },
+
+ testObserveInsideHandlers: function() {
+ var fired = false, observer = function(event) {
+ fired = true;
+ };
+
+ document.observe("test:somethingHappened", function() {
+ document.observe("test:somethingHappened", observer);
+ });
+
+ document.fire("test:somethingHappened");
+ this.assert(!fired);
+
+ document.fire("test:somethingHappened");
+ this.assert(fired);
+ document.stopObserving("test:somethingHappened");
+ },
+
+ testStopObservingInsideHandlers: function() {
+ var fired = false, observer = function(event) {
+ fired = true;
+ };
+
+ document.observe("test:somethingHappened", observer);
+ document.observe("test:somethingHappened", function() {
+ document.stopObserving("test:somethingHappened", observer);
+ });
+
+ document.fire("test:somethingHappened");
+ this.assert(fired);
+
+ fired = false;
+ document.fire("test:somethingHappened");
+ document.stopObserving("test:somethingHappened");
+ this.assert(!fired);
+ },
+
+ testDocumentLoaded: function() {
+ this.assert(!documentLoaded);
+ this.assert(document.loaded);
+ },
+
+ testDocumentContentLoadedEventFiresBeforeWindowLoad: function() {
+ this.assert(eventResults.contentLoaded, "contentLoaded");
+ this.assert(eventResults.contentLoaded.endOfDocument, "contentLoaded.endOfDocument");
+ this.assert(!eventResults.contentLoaded.windowLoad, "!contentLoaded.windowLoad");
+ this.assert(eventResults.windowLoad, "windowLoad");
+ this.assert(eventResults.windowLoad.endOfDocument, "windowLoad.endOfDocument");
+ this.assert(eventResults.windowLoad.contentLoaded, "windowLoad.contentLoaded");
+ },
+
+ testEventStopped: function() {
+ var span = $("span"), event;
+
+ span.observe("test:somethingHappened", function() { });
+ event = span.fire("test:somethingHappened");
+ this.assert(!event.stopped, "event.stopped should be false with an empty observer");
+ span.stopObserving("test:somethingHappened");
+
+ span.observe("test:somethingHappened", function(e) { e.stop() });
+ event = span.fire("test:somethingHappened");
+ this.assert(event.stopped, "event.stopped should be true for an observer that calls stop");
+ span.stopObserving("test:somethingHappened");
+ },
+
+ testEventFindElement: function() {
+ var span = $("span"), event;
+ event = span.fire("test:somethingHappened");
+ this.assertElementMatches(event.findElement(), 'span#span');
+ this.assertElementMatches(event.findElement('span'), 'span#span');
+ this.assertElementMatches(event.findElement('p'), 'p#inner');
+ this.assertEqual(null, event.findElement('div.does_not_exist'));
+ this.assertElementMatches(event.findElement('.does_not_exist, span'), 'span#span');
+ },
+
+ testEventIDDuplication: function() {
+ $('container').down().observe("test:somethingHappened", Prototype.emptyFunction);
+ $('container').innerHTML += $('container').innerHTML;
+ this.assertUndefined($('container').down(1)._prototypeEventID);
+ },
+
+ testHandlerCallsPreserveOrder: function() {
+ var span = $("span"), order = [ ];
+ span.observe("test:somethingHappened", function(e) { order.push('first') });
+ span.observe("test:somethingHappened", function(e) { order.push('second') });
+ span.fire("test:somethingHappened");
+ this.assertEnumEqual(['first', 'second'], order);
+ span.stopObserving("test:somethingHappened");
+ }
+});
+
+document.observe("dom:loaded", function(event) {
+ eventResults.contentLoaded = {
+ endOfDocument: eventResults.endOfDocument,
+ windowLoad: eventResults.windowLoad
+ };
+});
+
+Event.observe(window, "load", function(event) {
+ eventResults.windowLoad = {
+ endOfDocument: eventResults.endOfDocument,
+ contentLoaded: eventResults.contentLoaded
+ };
+});
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.html
new file mode 100644
index 0000000000..4af5e267ac
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.html
@@ -0,0 +1,2 @@
+<div id="content"></div>
+<div id="content2" style="color:red"></div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.js
new file mode 100644
index 0000000000..8487943f73
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/ajax.js
@@ -0,0 +1,42 @@
+var Fixtures = {
+ js: {
+ responseBody: '$("content").update("<H2>Hello world!</H2>");',
+ 'Content-Type': ' text/javascript '
+ },
+
+ html: {
+ responseBody: "Pack my box with <em>five dozen</em> liquor jugs! " +
+ "Oh, how <strong>quickly</strong> daft jumping zebras vex..."
+ },
+
+ xml: {
+ responseBody: '<?xml version="1.0" encoding="UTF-8" ?><name attr="foo">bar</name>',
+ 'Content-Type': 'application/xml'
+ },
+
+ json: {
+ responseBody: '{\n\r"test": 123}',
+ 'Content-Type': 'application/json'
+ },
+
+ jsonWithoutContentType: {
+ responseBody: '{"test": 123}'
+ },
+
+ invalidJson: {
+ responseBody: '{});window.attacked = true;({}',
+ 'Content-Type': 'application/json'
+ },
+
+ headerJson: {
+ 'X-JSON': '{"test": "hello #éà"}'
+ }
+};
+
+var responderCounter = 0;
+
+// lowercase comparison because of MSIE which presents HTML tags in uppercase
+var sentence = ("Pack my box with <em>five dozen</em> liquor jugs! " +
+"Oh, how <strong>quickly</strong> daft jumping zebras vex...").toLowerCase();
+
+var message = 'You must be running your tests from rake to test this feature.'; \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/array.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/array.html
new file mode 100644
index 0000000000..8f091878c8
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/array.html
@@ -0,0 +1 @@
+<div id="test_node">22<span id="span_1"></span><span id="span_2"></span></div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.html
new file mode 100644
index 0000000000..5a08fbd554
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.html
@@ -0,0 +1,6 @@
+<div id="test"></div>
+<ul id="list">
+ <li></li>
+ <li></li>
+ <li></li>
+</ul> \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.js
new file mode 100644
index 0000000000..8e9e00bd54
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/base.js
@@ -0,0 +1,106 @@
+var Person = function(name){
+ this.name = name;
+};
+
+Person.prototype.toJSON = function() {
+ return '-' + this.name;
+};
+
+var arg1 = 1;
+var arg2 = 2;
+var arg3 = 3;
+function TestObj() { };
+TestObj.prototype.assertingEventHandler =
+ function(event, assertEvent, assert1, assert2, assert3, a1, a2, a3) {
+ assertEvent(event);
+ assert1(a1);
+ assert2(a2);
+ assert3(a3);
+ };
+
+var globalBindTest = null;
+
+
+// base class
+var Animal = Class.create({
+ initialize: function(name) {
+ this.name = name;
+ },
+ name: "",
+ eat: function() {
+ return this.say("Yum!");
+ },
+ say: function(message) {
+ return this.name + ": " + message;
+ }
+});
+
+// subclass that augments a method
+var Cat = Class.create(Animal, {
+ eat: function($super, food) {
+ if (food instanceof Mouse) return $super();
+ else return this.say("Yuk! I only eat mice.");
+ }
+});
+
+// empty subclass
+var Mouse = Class.create(Animal, {});
+
+//mixins
+var Sellable = {
+ getValue: function(pricePerKilo) {
+ return this.weight * pricePerKilo;
+ },
+
+ inspect: function() {
+ return '#<Sellable: #{weight}kg>'.interpolate(this);
+ }
+};
+
+var Reproduceable = {
+ reproduce: function(partner) {
+ if (partner.constructor != this.constructor || partner.sex == this.sex)
+ return null;
+ var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
+ return new this.constructor('baby', weight, sex);
+ }
+};
+
+// base class with mixin
+var Plant = Class.create(Sellable, {
+ initialize: function(name, weight) {
+ this.name = name;
+ this.weight = weight;
+ },
+
+ inspect: function() {
+ return '#<Plant: #{name}>'.interpolate(this);
+ }
+});
+
+// subclass with mixin
+var Dog = Class.create(Animal, Reproduceable, {
+ initialize: function($super, name, weight, sex) {
+ this.weight = weight;
+ this.sex = sex;
+ $super(name);
+ }
+});
+
+// subclass with mixins
+var Ox = Class.create(Animal, Sellable, Reproduceable, {
+ initialize: function($super, name, weight, sex) {
+ this.weight = weight;
+ this.sex = sex;
+ $super(name);
+ },
+
+ eat: function(food) {
+ if (food instanceof Plant)
+ this.weight += food.weight;
+ },
+
+ inspect: function() {
+ return '#<Ox: #{name}>'.interpolate(this);
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/content.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/content.html
new file mode 100644
index 0000000000..ee3701d3c3
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/content.html
@@ -0,0 +1 @@
+Pack my box with <em>five dozen</em> liquor jugs! Oh, how <strong>quickly</strong> daft jumping zebras vex... \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/data.json b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/data.json
new file mode 100644
index 0000000000..85391eb939
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/data.json
@@ -0,0 +1 @@
+{test: 123} \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.css b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.css
new file mode 100644
index 0000000000..453a569c26
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.css
@@ -0,0 +1,85 @@
+#style_test_1 { cursor: pointer; font-size:12px;}
+div.style-test { margin-left: 1px }
+
+#style_test_dimensions_container {
+ position: absolute;
+ top: 0;
+ left: 500px;
+ width: 20px;
+ height: 30px;
+ margin: 10px;
+ padding: 10px;
+ border: 3px solid red;
+}
+
+#not_floating_style { float: none }
+#floating_style { float: left }
+#op2 { opacity:0.5;filter:alpha(opacity=50)progid:DXImageTransform.Microsoft.Blur(strength=10);}
+
+#scroll_test_1 {
+ margin: 10px;
+ padding: 10px;
+ position: relative;
+}
+
+#scroll_test_2 {
+ position: absolute;
+ left: 10px;
+ top: 10px;
+}
+
+#dimensions-visible,
+#dimensions-display-none,
+#dimensions-visible-pos-rel,
+#dimensions-display-none-pos-rel,
+#dimensions-visible-pos-abs,
+#dimensions-display-none-pos-abs {
+ font-size: 10px;
+ height: 10em;
+ width: 20em;
+}
+
+#dimensions-visible-pos-abs,
+#dimensions-display-none-pos-abs {
+ position: absolute;
+ top: 15px;
+ left: 15px;
+}
+
+#dimensions-visible-pos-rel,
+#dimensions-display-none-pos-rel {
+ position: relative;
+ top: 15px;
+ left: 15px;
+}
+
+#dimensions-display-none, #dimensions-display-none-pos-rel, #dimensions-display-none-pos-abs {
+ display: none;
+}
+
+#dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td {
+ font-size: 10px;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ border-spacing: 0;
+ height: 10em;
+ width: 20em;
+}
+
+
+#notInlineAbsoluted { position: absolute; }
+
+#elementToViewportDimensions {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 10px;
+ width: 10px;
+ background: #000;
+}
+
+/* for scroll test on really big screens */
+body {
+ height: 40000px;
+} \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.html
new file mode 100644
index 0000000000..1273098e6d
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.html
@@ -0,0 +1,285 @@
+<div id="scroll_test_1">
+ <p id="scroll_test_2">Scroll test</p>
+</div>
+
+<div id="test-visible">visible</div>
+<div id="test-hidden" style="display:none;">hidden</div>
+<div id="test-toggle-visible">visible</div>
+<div id="test-toggle-hidden" style="display:none;">hidden</div>
+<div id="test-hide-visible">visible</div>
+<div id="test-hide-hidden" style="display:none;">hidden</div>
+<div id="test-show-visible">visible</div>
+<div id="test-show-hidden" style="display:none;">hidden</div>
+<div id="removable-container"><div id="removable"></div></div>
+
+<div>
+ <table>
+ <tbody id="table">
+ <tr>
+ <td>Data</td>
+ </tr>
+ <tr>
+ <td id="a_cell">First Row</td>
+ </tr>
+ <tr id="second_row">
+ <td>Second Row</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<div id="table-container-to-replace">
+ <table>
+ <tbody id="table-to-replace">
+ <tr>
+ <td>Data</td>
+ </tr>
+ <tr>
+ <td id="a_cell-to-replace">First Row</td>
+ </tr>
+ <tr id="second_row-to-replace">
+ <td>Second Row</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<p class="test">Test paragraph outside of container</p>
+
+<div id="container">
+ <p class="test" id="intended">Test paragraph 1 inside of container</p>
+ <p class="test">Test paragraph 2 inside of container</p>
+ <p class="test">Test paragraph 3 inside of container</p>
+ <p class="test">Test paragraph 4 inside of container</p>
+</div>
+
+<div id="testdiv">to be updated</div>
+<div id="testdiv-replace-container-1"><div id="testdiv-replace-1"></div></div>
+<div id="testdiv-replace-container-2"><div id="testdiv-replace-2"></div></div>
+<div id="testdiv-replace-container-3"><div id="testdiv-replace-3"></div></div>
+<div id="testdiv-replace-container-4"><div id="testdiv-replace-4"></div></div>
+<div id="testdiv-replace-container-5"><div id="testdiv-replace-5"></div></div>
+<div id="testdiv-replace-container-element"><div id="testdiv-replace-element"></div></div>
+<div id="testdiv-replace-container-toelement"><div id="testdiv-replace-toelement"></div></div>
+<div id="testdiv-replace-container-tohtml"><div id="testdiv-replace-tohtml"></div></div>
+<div id="testtable-replace-container"><table id="testtable-replace"></table></div>
+<table id="testrow-replace-container"><tr id="testrow-replace"></tr></table>
+<select id="testoption-replace-container"><option id="testoption-replace"></option><option>stays</option></select>
+<div id="testform-replace-container"><p>some text</p><form id="testform-replace"><input id="testinput-replace" type="text" /></form><p>some text</p></div>
+
+<div id="element_with_visible_overflow" style="overflow:visible">V</div>
+<div id="element_with_hidden_overflow" style="overflow:hidden">H</div>
+<div id="element_with_scroll_overflow" style="overflow:scroll">S</div>
+
+<div id="element_extend_test"> </div>
+
+<div id="element_reextend_test"><div id="discard_1"></div></div>
+
+<div id="test_whitespace"> <span> </span>
+
+
+<div><div></div> </div><span> </span>
+</div>
+
+
+<div id="nav_tests_isolator">
+ <div id="nav_test_first_sibling"></div>
+ <div></div>
+ <p id="nav_test_p" class="test"></p>
+ <span id="nav_test_prev_sibling"></span>
+
+ <ul id="navigation_test" style="display: none">
+ <!-- comment node to screw things up -->
+ <li class="first"><em>A</em></li>
+ <li><em class="dim">B</em></li>
+ <li id="navigation_test_c">
+ <em>C</em>
+ <ul>
+ <li><em class="dim">E</em></li>
+ <li id="navigation_test_f"><em>F</em></li>
+ </ul>
+ </li>
+ <li class="last"><em>D</em></li>
+ </ul>
+
+ <div id="navigation_test_next_sibling">
+ <!-- -->
+ </div>
+
+ <p></p>
+</div>
+
+<div id="class_names">
+ <p class="A"></p>
+ <ul class="A B" id="class_names_ul">
+ <li class="C"></li>
+ <li class="A C"></li>
+ <li class="1"></li>
+ </ul>
+ <div class="B C D"></div>
+ <div id="unextended"></div>
+</div>
+
+<div id="style_test_1" style="display:none;"></div>
+<div id="style_test_2" class="style-test" style="font-size:11px;"></div>
+
+<div id="style_test_3">blah</div>
+<span id="style_test_4">blah</span>
+<span id="style_test_5">blah</span>
+
+<div id="style_test_dimensions_container">
+ <div id="style_test_dimensions" style="background:#ddd;padding:1px;margin:1px;border:1px solid #00f"><div style="height:5px;background:#eee;width:5px;padding:2px;margin:2px;border:2px solid #0f0"> </div>
+ </div>
+</div>
+
+<div id="test_csstext_1">test_csstext_1</div>
+<div id="test_csstext_2">test_csstext_2</div>
+<div id="test_csstext_3" style="border: 1px solid red">test_csstext_3</div>
+<div id="test_csstext_4" style="font-size: 20px">test_csstext_4</div>
+<div id="test_csstext_5">test_csstext_5</div>
+
+<div id="custom_attributes">
+ <div foo="1" bar="2"></div>
+ <div foo="2"></div>
+</div>
+
+<div id="cloned_element_attributes_issue" foo="original"></div>
+<a id="attributes_with_issues_1" href="test.html" accesskey="L" tabindex="50" title="a link" onclick="alert('hello world');"></a>
+<a id="attributes_with_issues_2" href="" accesskey="" tabindex="" title=""></a>
+<a id="attributes_with_issues_3"></a>
+<form id="attributes_with_issues_form" method="post" action="blah" class="blah-class">
+ <input type="hidden" id="id" />
+ <input type="hidden" name="id" />
+ <input type="checkbox" id="action" />
+ <input type="checkbox" name="action" />
+ <input type="text" id="method" />
+ <input type="hidden" name="class" />
+ <input type="checkbox" id="attributes_with_issues_checked" name="a" checked="checked"/>
+ <input type="checkbox" id="attributes_with_issues_disabled" name="b" checked="checked" disabled="disabled"/>
+ <input type="text" id="attributes_with_issues_readonly" name="c" readonly="readonly" value="blech"/>
+ <input type="text" id="attributes_with_issues_regular" name="d" value="0"/>
+ <input type="date" id="attributes_with_issues_type" value="blech" />
+ <select id="attributes_with_issues_multiple" name="e" multiple="multiple">
+ <option>blech</option>
+ <option>blah</option>
+ </select>
+</form>
+
+<!-- writeAttributes -->
+<p id="write_attribute_para"></p>
+<a id="write_attribute_link" href="test.html"></a>
+<form action="/dev/null" id="write_attribute_form" method="get" accept-charset="utf-8">
+ <label id="write_attribute_label"></label>
+ <input type="checkbox" name="write_attribute_checkbox" value="" id="write_attribute_checkbox">
+ <input type="checkbox" checked="checked" name="write_attribute_checked_checkbox" value="" id="write_attribute_checked_checkbox">
+ <input type="text" name="write_attribute_input" value="" id="write_attribute_input">
+ <select id="write_attribute_select">
+ <option>Cat</option>
+ <option>Dog</option>
+ </select>
+</form>
+
+<table id="write_attribute_table" cellpadding="6" cellspacing="4">
+ <tr><td id="write_attribute_td">A</td><td>B</td></tr>
+ <tr><td>C</td></tr>
+ <tr><td>D</td><td>E</td><td>F</td></tr>
+</table>
+
+<div id="dom_attribute_precedence">
+ <form action="blech" method="post">
+ <input type="submit" id="update" />
+ </form>
+</div>
+
+<div id="not_floating_none">NFN</div>
+<div id="not_floating_inline" style="float:none">NFI</div>
+<div id="not_floating_style">NFS</div>
+
+<div id="floating_inline" style="float:left">FI</div>
+<div id="floating_style">FS</div>
+<!-- Test Element opacity functions -->
+<img id="op1" alt="op2" src="fixtures/logo.gif" style="opacity:0.5;filter:alpha(opacity=50)" />
+<img id="op2" alt="op2" src="fixtures/logo.gif"/>
+<img id="op3" alt="op3" src="fixtures/logo.gif"/>
+<img id="op4-ie" alt="op3" src="fixtures/logo.gif" style="filter:alpha(opacity=30)" />
+<div id="dimensions-visible"></div>
+<div id="dimensions-display-none"></div>
+<div id="dimensions-visible-pos-rel"></div>
+<div id="dimensions-display-none-pos-rel"></div>
+<div id="dimensions-visible-pos-abs"></div>
+<div id="dimensions-display-none-pos-abs"></div>
+<table border="0" cellspacing="0" cellpadding="0" id="dimensions-table">
+ <tbody id="dimensions-tbody">
+ <tr id="dimensions-tr">
+ <td id="dimensions-td">Data</td>
+ </tr>
+ </tbody>
+</table>
+
+<div id="dimensions-nester" style="width: 500px;">
+ <div id="dimensions-nestee" style="display: none">This is a nested DIV. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
+</div>
+
+
+<p id="test-empty"></p>
+<p id="test-empty-but-contains-whitespace">
+
+
+</p>
+<p id="test-full">content</p>
+<div id="ancestor">
+ <div id="child">
+ <div id="grand-child">
+ <div id="great-grand-child"></div>
+ </div></div><!-- intentional formatting; don't change this line -->
+ <div id="sibling"><div id="grand-sibling"></div></div>
+</div>
+<div id="not-in-the-family"></div>
+
+<div id="insertions-container"><div id="insertions-main"><p>some content.</p></div></div>
+<div id="insertions-node-container"><div id="insertions-node-main"><p>some content.</p></div></div>
+<div id="element-insertions-container"><div id="element-insertions-main"><p>some content.</p></div></div>
+<div id="element-insertions-multiple-container"><div id="element-insertions-multiple-main"><p>some content.</p></div></div>
+<table id="table_for_insertions"></table>
+<table id="table_for_row_insertions"><tr id="row_1"></tr></table>
+<form method="post" action="blah">
+ <select id="select_for_update" name="select_for_update">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+ <select id="select_for_insert_bottom" name="select_for_insert_bottom">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+ <select id="select_for_insert_top" name="select_for_insert_top">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+</form>
+<div id="wrap-container"><p id="wrap"></p></div>
+
+<!-- Positioning methods bench -->
+<div id="body_absolute" style="position: absolute; top: 10px; left: 10px">
+ <div id="absolute_absolute" style="position: absolute; top: 10px; left:10px"> </div>
+ <div id="absolute_relative" style="position: relative; top: 10px; left:10px">
+ <div style="height:10px;font-size:2px">test<span id="inline">test</span></div>
+ <div id="absolute_relative_undefined">XYZ</div>
+ </div>
+ <div id="absolute_fixed" style="position: fixed; top: 10px; left: 10px">
+ <span id="absolute_fixed_absolute" style="position: absolute; top: 10px; left: 10px">foo</span>
+ <span id="absolute_fixed_undefined" style="display:block">bar</span>
+ </div></div>
+<div id="notInlineAbsoluted"></div>
+<div id="inlineAbsoluted" style="position: absolute"></div>
+
+<div id="unextended"></div>
+<div id="identification">
+ <div id="predefined_id"></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div id="anonymous_element_3"></div>
+</div>
+
+<div id='elementToViewportDimensions' style='display: none'></div>
+<div id="auto_dimensions" style="height:auto"></div> \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.js
new file mode 100644
index 0000000000..18f62f354c
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/dom.js
@@ -0,0 +1,17 @@
+var testVar = 'to be updated', testVar2 = '', documentViewportProperties;
+
+Element.addMethods({
+ hashBrowns: function(element) { return 'hash browns'; }
+});
+
+Element.addMethods("LI", {
+ pancakes: function(element) { return "pancakes"; }
+});
+
+Element.addMethods("DIV", {
+ waffles: function(element) { return "waffles"; }
+});
+
+Element.addMethods($w("li div"), {
+ orangeJuice: function(element) { return "orange juice"; }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.html
new file mode 100644
index 0000000000..583d0e5f30
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.html
@@ -0,0 +1,4 @@
+<form id="form">
+ <input type="text" id="input" value="4" />
+ <input type="submit" />
+</form> \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.js
new file mode 100644
index 0000000000..22b1c3fd14
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/element_mixins.js
@@ -0,0 +1,2 @@
+Form.Element.Methods.coffee = Prototype.K;
+Element.addMethods(); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.html
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.js
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/empty.js
@@ -0,0 +1 @@
+
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.html
new file mode 100644
index 0000000000..5e7e8f3b3c
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.html
@@ -0,0 +1,8 @@
+<table id="grepTable">
+<tbody id="grepTBody">
+ <tr id="grepRow">
+ <th id="grepHeader" class="cell"></th>
+ <td id="grepCell" class="cell"></td>
+ </tr>
+</tbody>
+</table>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.js
new file mode 100644
index 0000000000..5fae7662dd
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/enumerable.js
@@ -0,0 +1,23 @@
+var Fixtures = {
+ People: [
+ {name: 'Sam Stephenson', nickname: 'sam-'},
+ {name: 'Marcel Molina Jr.', nickname: 'noradio'},
+ {name: 'Scott Barron', nickname: 'htonl'},
+ {name: 'Nicholas Seckar', nickname: 'Ulysses'}
+ ],
+
+ Nicknames: $w('sam- noradio htonl Ulysses'),
+
+ Basic: [1, 2, 3],
+
+ Primes: [
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23,
+ 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97
+ ],
+
+ Z: []
+};
+
+for (var i = 1; i <= 100; i++)
+ Fixtures.Z.push(i);
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/event.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/event.html
new file mode 100644
index 0000000000..baa88a7d10
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/event.html
@@ -0,0 +1,4 @@
+<div id="outer" style="display: none">
+ <p id="inner">One two three <span id="span">four</span></p>
+</div>
+<div id="container"><div></div></div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/form.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/form.html
new file mode 100644
index 0000000000..dc7f171bf2
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/form.html
@@ -0,0 +1,112 @@
+<form id="form" method="get" action="fixtures/empty.js">
+ <input type="text" name="val1" id="input_enabled" value="4" />
+ <div>This is not a form element</div>
+ <input type="text" name="val2" id="input_disabled" disabled="disabled" value="5" />
+ <input type="submit" name="first_submit" value="Commit it!" />
+ <input type="submit" name="second_submit" value="Delete it!" />
+ <input type="text" name="action" value="blah" />
+</form>
+
+<form id="bigform" method="get" action="fixtures/empty.js">
+ <div id="inputs">
+ <input type="text" name="dummy" id="dummy_disabled" disabled="disabled"/>
+ <input type="submit" name="commit" id="submit" />
+ <input type="button" name="clicky" value="click me" />
+ <input type="reset" name="revert" />
+ <input type="text" name="greeting" id="focus_text" value="Hello" />
+ </div>
+
+ <div id="buttons">
+ <button type="button" id="button_type_button" name="button">Click Me</button>
+ </div>div>
+
+ <!-- some edge cases in serialization -->
+ <div id="value_checks">
+ <input name="twin" type="text" value="" />
+ <input name="twin" type="text" value="siamese" />
+ <!-- Rails checkbox hack with hidden input: -->
+ <input name="checky" type="checkbox" id="checkbox_hack" value="1" />
+ <input name="checky" type="hidden" value="0" />
+ </div>
+
+ <!-- all variations of SELECT controls -->
+ <div id="selects_wrapper">
+ <select name="vu">
+ <option value="1" selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <select id="multiSel1" name="vm[]" multiple="multiple">
+ <option id="multiSel1_opt1" value="1" selected="selected">One</option>
+ <option id="multiSel1_opt2" value="2">Two</option>
+ <option id="multiSel1_opt3" value="3" selected="selected">Three</option>
+ </select>
+ <select name="nvu">
+ <option selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+
+ <fieldset id="selects_fieldset">
+ <select name="nvm[]" multiple="multiple">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option selected="selected">Three</option>
+ </select>
+ <select name="evu">
+ <option value="" selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <select name="evm[]" multiple="multiple">
+ <option value="" selected="selected">One</option>
+ <option>Two</option>
+ <option selected="selected">Three</option>
+ </select>
+ </fieldset>
+ </div>
+
+ <div id="various">
+ <select name="tf_selectOne"><option selected="selected"></option><option>1</option></select>
+ <textarea name="tf_textarea"></textarea>
+ <input type="checkbox" name="tf_checkbox" value="on" />
+ <select name="tf_selectMany" multiple="multiple"></select>
+ <input type="text" name="tf_text" />
+ <div>This is not a form element</div>
+ <input type="radio" name="tf_radio" value="on" />
+ <input type="hidden" name="tf_hidden" />
+ <input type="password" name="tf_password" />
+ </div>
+</form>
+
+<form id="form_focus_hidden" style="display: none">
+ <input type="text" />
+</form>
+
+<form id="form_with_file_input">
+ <input type="file" name="file_name" value="foo" />
+</form>
+
+<!-- tabindexed forms -->
+<div id="tabindex">
+ <form id="ffe">
+ <p><input type="text" disabled="disabled" id="ffe_disabled" /></p>
+ <input type="hidden" id="ffe_hidden" />
+ <input type="checkbox" id="ffe_checkbox" />
+ </form>
+
+ <form id="ffe_ti">
+ <p><input type="text" disabled="disabled" id="ffe_ti_disabled" /></p>
+ <input type="hidden" id="ffe_ti_hidden" />
+ <input type="checkbox" id="ffe_ti_checkbox" />
+ <input type="submit" id="ffe_ti_submit" tabindex="1" />
+ </form>
+
+ <form id="ffe_ti2">
+ <p><input type="text" disabled="disabled" id="ffe_ti2_disabled" /></p>
+ <input type="hidden" id="ffe_ti2_hidden" />
+ <input type="checkbox" id="ffe_ti2_checkbox" tabindex="0" />
+ <input type="submit" id="ffe_ti2_submit" tabindex="1" />
+ </form>
+
+</div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hash.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hash.js
new file mode 100644
index 0000000000..be5ccfa7d9
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hash.js
@@ -0,0 +1,25 @@
+var Fixtures = {
+ one: { a: 'A#' },
+
+ many: {
+ a: 'A',
+ b: 'B',
+ c: 'C',
+ d: 'D#'
+ },
+
+ functions: {
+ quad: function(n) { return n*n },
+ plus: function(n) { return n+n }
+ },
+
+ multiple: { color: $w('r g b') },
+ multiple_nil: { color: ['r', null, 'g', undefined, 0] },
+ multiple_all_nil: { color: [null, undefined] },
+ multiple_empty: { color: [] },
+ multiple_special: { 'stuff[]': $w('$ a ;') },
+
+ value_undefined: { a:"b", c:undefined },
+ value_null: { a:"b", c:null },
+ value_zero: { a:"b", c:0 }
+};
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hello.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hello.js
new file mode 100644
index 0000000000..63d81117bf
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/hello.js
@@ -0,0 +1 @@
+$("content").update("<H2>Hello world!</H2>");
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/logo.gif b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/logo.gif
new file mode 100644
index 0000000000..71574abd76
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/logo.gif
Binary files differ
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/position.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/position.html
new file mode 100644
index 0000000000..5fd7bdf4b6
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/position.html
@@ -0,0 +1,9 @@
+<div id="ensure_scrollbars" style="width:10000px; height:10000px; position:absolute" > </div>
+
+<div id="body_absolute" style="position: absolute; top: 10px; left: 10px">
+ <div id="absolute_absolute" style="position: absolute; top: 10px; left:10px"> </div>
+ <div id="absolute_relative" style="position: relative; top: 10px; left:10px">
+ <div style="height:10px;font-size:2px">test<span id="inline">test</span></div>
+ <div id="absolute_relative_undefined"> </div>
+ </div>
+</div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/selector.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/selector.html
new file mode 100644
index 0000000000..d745c8fd55
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/selector.html
@@ -0,0 +1,69 @@
+<div id="fixtures" style="display: none">
+ <h1 class="title">Some title <span>here</span></h1>
+ <p id="p" class="first summary">
+ <strong id="strong">This</strong> is a short blurb
+ <a id="link_1" class="first internal" rel="external nofollow" href="#">with a <em id="em2">link</em></a> or
+ <a id="link_2" class="internal highlight" href="#"><em id="em">two</em></a>.
+ Or <cite id="with_title" title="hello world!">a citation</cite>.
+ </p>
+ <ul id="list">
+ <li id="item_1" class="first"><a id="link_3" href="#" class="external"><span id="span">Another link</span></a></li>
+ <li id="item_2">Some text</li>
+ <li id="item_3" xml:lang="es-us" class="">Otra cosa</li>
+ </ul>
+
+ <!-- this form has a field with the name 'id',
+ therefore its ID property won't be 'troubleForm': -->
+ <form id="troubleForm">
+ <input type="hidden" name="id" id="hidden" />
+ <input type="text" name="disabled_text_field" id="disabled_text_field" disabled="disabled" />
+ <input type="text" name="enabled_text_field" id="enabled_text_field" />
+ <input type="checkbox" name="checkboxes" id="checked_box" checked="checked" value="Checked" />
+ <input type="checkbox" name="checkboxes" id="unchecked_box" value="Unchecked"/>
+ <input type="radio" name="radiobuttons" id="checked_radio" checked="checked" value="Checked" />
+ <input type="radio" name="radiobuttons" id="unchecked_radio" value="Unchecked" />
+ </form>
+
+ <form id="troubleForm2">
+ <input type="checkbox" name="brackets[5][]" id="chk_1" checked="checked" value="1" />
+ <input type="checkbox" name="brackets[5][]" id="chk_2" value="2" />
+ </form>
+
+ <div id="level1">
+ <span id="level2_1">
+ <span id="level3_1"></span>
+ <!-- This comment should be ignored by the adjacent selector -->
+ <span id="level3_2"></span>
+ </span>
+ <span id="level2_2">
+ <em id="level_only_child">
+ </em>
+ </span>
+ <div id="level2_3"></div>
+ </div> <!-- #level1 -->
+
+ <div id="dupContainer">
+ <span id="dupL1" class="span_foo span_bar">
+ <span id="dupL2">
+ <span id="dupL3">
+ <span id="dupL4">
+ <span id="dupL5"></span>
+ </span>
+ </span>
+ </span>
+ </span>
+ </div> <!-- #dupContainer -->
+
+ <div id="grandfather"> grandfather
+ <div id="father" class="brothers men"> father
+ <div id="son"> son </div>
+ </div>
+ <div id="uncle" class="brothers men"> uncle </div>
+ </div>
+
+ <form id="commaParent" title="commas,are,good">
+ <input type="hidden" id="commaChild" name="foo" value="#commaOne,#commaTwo" />
+ <input type="hidden" id="commaTwo" name="foo2" value="oops" />
+ </form>
+ <div id="counted_container"><div class="is_counted"></div></div>
+</div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/string.js b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/string.js
new file mode 100644
index 0000000000..e4483a5b61
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/string.js
@@ -0,0 +1,8 @@
+var attackTarget;
+var evalScriptsCounter = 0,
+ largeTextEscaped = '&lt;span&gt;test&lt;/span&gt;',
+ largeTextUnescaped = '<span>test</span>';
+(2048).times(function(){
+ largeTextEscaped += ' ABC';
+ largeTextUnescaped += ' ABC';
+});
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/unittest.html b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/unittest.html
new file mode 100644
index 0000000000..15d303aa3d
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/fixtures/unittest.html
@@ -0,0 +1,18 @@
+<div id="testlog_2"> </div>
+
+<!-- Test elements follow -->
+<div id="test_1" class="a bbbbbbbbbbbb cccccccccc dddd"> </div>
+
+<div id="test_2"> <span> </span>
+
+
+
+<div><div></div> </div><span> </span>
+</div>
+
+<ul id="tlist"><li id="tlist_1">x1</li><li id="tlist_2">x2</li></ul>
+<ul id="tlist2"><li class="a" id="tlist2_1">x1</li><li id="tlist2_2">x2</li></ul>
+
+<div id="testmoveby" style="background-color:#333;width:100px;">XXXX</div>
+
+<div id="testcss1">testcss1<span id="testcss1_span" style="display:none;">blah</span></div><div id="testcss2">testcss1</div>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/form_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/form_test.js
new file mode 100644
index 0000000000..4cda87d81b
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/form_test.js
@@ -0,0 +1,384 @@
+// sweet sweet additional assertions
+Object.extend(Test.Unit.Testcase.prototype, {
+ assertEnabled: function() {
+ for (var i = 0, element; element = arguments[i]; i++) {
+ this.assert(!$(element).disabled, Test.Unit.inspect(element) + ' was disabled');
+ }
+ },
+ assertDisabled: function() {
+ for (var i = 0, element; element = arguments[i]; i++) {
+ this.assert($(element).disabled, Test.Unit.inspect(element) + ' was enabled');
+ }
+ }
+});
+
+new Test.Unit.Runner({
+
+ // Make sure to set defaults in the test forms, as some browsers override this
+ // with previously entered values on page reload
+ setup: function(){
+ $$('form').each(function(f){ f.reset() });
+ // hidden value does not reset (for some reason)
+ $('bigform')['tf_hidden'].value = '';
+ },
+
+ testDollarF: function(){
+ this.assertEqual("4", $F("input_enabled"));
+ },
+
+ testFormElementEventObserver: function(){
+ var callbackCounter = 0;
+ var observer = new Form.Element.EventObserver('input_enabled', function(){
+ callbackCounter++;
+ });
+
+ this.assertEqual(0, callbackCounter);
+ $('input_enabled').value = 'boo!';
+ observer.onElementEvent(); // can't test the event directly, simulating
+ this.assertEqual(1, callbackCounter);
+ },
+
+ testFormElementObserver: function(){
+ var timedCounter = 0;
+ // First part: regular field
+ var observer = new Form.Element.Observer('input_enabled', 0.5, function() {
+ ++timedCounter;
+ });
+
+ // Test it's unchanged yet
+ this.assertEqual(0, timedCounter);
+ // Test it doesn't change on first check
+ this.wait(550, function() {
+ this.assertEqual(0, timedCounter);
+ // Change, test it doesn't immediately change
+ $('input_enabled').value = 'yowza!';
+ this.assertEqual(0, timedCounter);
+ // Test it changes on next check, but not again on the next
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ observer.stop();
+ });
+ });
+ });
+
+ // Second part: multiple-select
+ [1, 2, 3].each(function(index) {
+ $('multiSel1_opt' + index).selected = (1 == index);
+ });
+ timedCounter = 0;
+ observer = new Form.Element.Observer('multiSel1', 0.5, function() {
+ ++timedCounter;
+ });
+
+ // Test it's unchanged yet
+ this.assertEqual(0, timedCounter);
+ // Test it doesn't change on first check
+ this.wait(550, function() {
+ this.assertEqual(0, timedCounter);
+ // Change, test it doesn't immediately change
+ // NOTE: it is important that the 3rd be re-selected, for the
+ // serialize form to obtain the expected value :-)
+ $('multiSel1_opt3').selected = true;
+ this.assertEqual(0, timedCounter);
+ // Test it changes on next check, but not again on the next
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ observer.stop();
+ });
+ });
+ });
+ },
+
+ testFormObserver: function(){
+ var timedCounter = 0;
+ // should work the same way was Form.Element.Observer
+ var observer = new Form.Observer('form', 0.5, function(form, value) {
+ ++timedCounter;
+ });
+
+ // Test it's unchanged yet
+ this.assertEqual(0, timedCounter);
+ // Test it doesn't change on first check
+ this.wait(550, function() {
+ this.assertEqual(0, timedCounter);
+ // Change, test it doesn't immediately change
+ $('input_enabled').value = 'yowza!';
+ this.assertEqual(0, timedCounter);
+ // Test it changes on next check, but not again on the next
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ this.wait(550, function() {
+ this.assertEqual(1, timedCounter);
+ observer.stop();
+ });
+ });
+ });
+ },
+
+ testFormEnabling: function(){
+ var form = $('bigform')
+ var input1 = $('dummy_disabled');
+ var input2 = $('focus_text');
+
+ this.assertDisabled(input1);
+ this.assertEnabled(input2);
+
+ form.disable();
+ this.assertDisabled(input1, input2);
+ form.enable();
+ this.assertEnabled(input1, input2);
+ input1.disable();
+ this.assertDisabled(input1);
+
+ // non-form elements:
+ var fieldset = $('selects_fieldset');
+ var fields = fieldset.immediateDescendants();
+ fields.each(function(select) { this.assertEnabled(select) }, this);
+
+ Form.disable(fieldset)
+ fields.each(function(select) { this.assertDisabled(select) }, this);
+
+ Form.enable(fieldset)
+ fields.each(function(select) { this.assertEnabled(select) }, this);
+ },
+
+ testFormElementEnabling: function(){
+ var field = $('input_disabled');
+ field.enable();
+ this.assertEnabled(field);
+ field.disable();
+ this.assertDisabled(field);
+
+ var field = $('input_enabled');
+ this.assertEnabled(field);
+ field.disable();
+ this.assertDisabled(field);
+ field.enable();
+ this.assertEnabled(field);
+ },
+
+ // due to the lack of a DOM hasFocus() API method,
+ // we're simulating things here a little bit
+ testFormActivating: function(){
+ // Firefox, IE, and Safari 2+
+ function getSelection(element){
+ try {
+ if (typeof element.selectionStart == 'number') {
+ return element.value.substring(element.selectionStart, element.selectionEnd);
+ } else if (document.selection && document.selection.createRange) {
+ return document.selection.createRange().text;
+ }
+ }
+ catch(e){ return null }
+ }
+
+ // Form.focusFirstElement shouldn't focus disabled elements
+ var element = Form.findFirstElement('bigform');
+ this.assertEqual('submit', element.id);
+
+ // Test IE doesn't select text on buttons
+ Form.focusFirstElement('bigform');
+ if (document.selection) this.assertEqual('', getSelection(element));
+
+ // Form.Element.activate shouldn't select text on buttons
+ element = $('focus_text');
+ this.assertEqual('', getSelection(element));
+
+ // Form.Element.activate should select text on text input elements
+ element.activate();
+ this.assertEqual('Hello', getSelection(element));
+
+ // Form.Element.activate shouldn't raise an exception when the form or field is hidden
+ this.assertNothingRaised(function() {
+ $('form_focus_hidden').focusFirstElement();
+ });
+ },
+
+ testFormGetElements: function() {
+ var elements = Form.getElements('various'),
+ names = $w('tf_selectOne tf_textarea tf_checkbox tf_selectMany tf_text tf_radio tf_hidden tf_password');
+ this.assertEnumEqual(names, elements.pluck('name'))
+ },
+
+ testFormGetInputs: function() {
+ var form = $('form');
+ [form.getInputs(), Form.getInputs(form)].each(function(inputs){
+ this.assertEqual(inputs.length, 5);
+ this.assert(inputs instanceof Array);
+ this.assert(inputs.all(function(input) { return (input.tagName == "INPUT"); }));
+ }, this);
+ },
+
+ testFormFindFirstElement: function() {
+ this.assertEqual($('ffe_checkbox'), $('ffe').findFirstElement());
+ this.assertEqual($('ffe_ti_submit'), $('ffe_ti').findFirstElement());
+ this.assertEqual($('ffe_ti2_checkbox'), $('ffe_ti2').findFirstElement());
+ },
+
+ testFormSerialize: function() {
+ // form is initially empty
+ var form = $('bigform');
+ var expected = { tf_selectOne:'', tf_textarea:'', tf_text:'', tf_hidden:'', tf_password:'' };
+ this.assertHashEqual(expected, Form.serialize('various', true));
+
+ // set up some stuff
+ form['tf_selectOne'].selectedIndex = 1;
+ form['tf_textarea'].value = "boo hoo!";
+ form['tf_text'].value = "123öäü";
+ form['tf_hidden'].value = "moo%hoo&test";
+ form['tf_password'].value = 'sekrit code';
+ form['tf_checkbox'].checked = true;
+ form['tf_radio'].checked = true;
+ var expected = { tf_selectOne:1, tf_textarea:"boo hoo!", tf_text:"123öäü",
+ tf_hidden:"moo%hoo&test", tf_password:'sekrit code', tf_checkbox:'on', tf_radio:'on' }
+
+ // return params
+ this.assertHashEqual(expected, Form.serialize('various', true));
+ // return string
+ this.assertEnumEqual(Object.toQueryString(expected).split('&').sort(),
+ Form.serialize('various').split('&').sort());
+ this.assertEqual('string', typeof $('form').serialize({ hash:false }));
+
+ // Checks that disabled element is not included in serialized form.
+ $('input_enabled').enable();
+ this.assertHashEqual({ val1:4, action:'blah', first_submit:'Commit it!' },
+ $('form').serialize(true));
+
+ // should not eat empty values for duplicate names
+ $('checkbox_hack').checked = false;
+ var data = Form.serialize('value_checks', true);
+ this.assertEnumEqual(['', 'siamese'], data['twin']);
+ this.assertEqual('0', data['checky']);
+
+ $('checkbox_hack').checked = true;
+ this.assertEnumEqual($w('1 0'), Form.serialize('value_checks', true)['checky']);
+
+ // all kinds of SELECT controls
+ var params = Form.serialize('selects_fieldset', true);
+ var expected = { 'nvm[]':['One', 'Three'], evu:'', 'evm[]':['', 'Three'] };
+ this.assertHashEqual(expected, params);
+ params = Form.serialize('selects_wrapper', true);
+ this.assertHashEqual(Object.extend(expected, { vu:1, 'vm[]':[1, 3], nvu:'One' }), params);
+
+ // explicit submit button
+ this.assertHashEqual({ val1:4, action:'blah', second_submit:'Delete it!' },
+ $('form').serialize({ submit: 'second_submit' }));
+ this.assertHashEqual({ val1:4, action:'blah' },
+ $('form').serialize({ submit: false }));
+ this.assertHashEqual({ val1:4, action:'blah' },
+ $('form').serialize({ submit: 'inexistent' }));
+
+ // file input should not be serialized
+ this.assertEqual('', $('form_with_file_input').serialize());
+ },
+
+ testFormMethodsOnExtendedElements: function() {
+ var form = $('form');
+ this.assertEqual(Form.serialize('form'), form.serialize());
+ this.assertEqual(Form.Element.serialize('input_enabled'), $('input_enabled').serialize());
+ this.assertNotEqual(form.serialize, $('input_enabled').serialize);
+
+ Element.addMethods('INPUT', { anInputMethod: function(input) { return 'input' } });
+ Element.addMethods('SELECT', { aSelectMethod: function(select) { return 'select' } });
+
+ form = $('bigform');
+ var input = form['tf_text'], select = form['tf_selectOne'];
+ input._extendedByPrototype = select._extendedByPrototype = false;
+
+ this.assert($(input).anInputMethod);
+ this.assert(!input.aSelectMethod);
+ this.assertEqual('input', input.anInputMethod());
+
+ this.assert($(select).aSelectMethod);
+ this.assert(!select.anInputMethod);
+ this.assertEqual('select', select.aSelectMethod());
+ },
+
+ testFormRequest: function() {
+ request = $("form").request();
+ this.assert($("form").hasAttribute("method"));
+ this.assert(request.url.include("fixtures/empty.js?val1=4"));
+ this.assertEqual("get", request.method);
+
+ request = $("form").request({ method: "put", parameters: {val2: "hello"} });
+ var url = request.url;
+
+ this.assert(url.startsWith("fixtures/empty.js"));
+ this.assert(url.endsWith("_method=put") || url.endsWith("_method=put&_="));
+ this.assertEqual(4, request.options.parameters['val1']);
+ this.assertEqual('hello', request.options.parameters['val2']);
+ this.assertEqual("post", request.method);
+ this.assertEqual("put", request.parameters['_method']);
+
+ // with empty action attribute
+ request = $("ffe").request({ method: 'post' });
+ this.assert(request.url.include("unit/tmp/form_test.html"),
+ 'wrong default action for form element with empty action attribute');
+ },
+
+ testFormElementMethodsChaining: function(){
+ var methods = $w('clear activate disable enable'),
+ formElements = $('form').getElements();
+ methods.each(function(method){
+ formElements.each(function(element){
+ var returned = element[method]();
+ this.assertIdentical(element, returned);
+ }, this);
+ }, this);
+ },
+
+ testGetValue: function() {
+ this.assertEqual('Click Me', Form.Element.getValue('button_type_button')); // Button element
+ this.assertEqual(null, Form.Element.getValue('someBorkedId')); // Unknown
+ this.assertEqual(null, Form.Element.getValue('form')); // Unsupported
+ this.assertEqual(null, Form.Element.getValue('form')); // Unsupported
+ },
+
+ testSetValue: function(){
+ // unkown element
+ this.assertEqual(null, Form.Element.setValue('someBorkedId', 'blah')); // Unknown
+
+ // test button element
+ var button = $('button_type_button');
+ button.setValue('I Changed');
+ this.assertEqual('I Changed', button.getValue());
+
+ // text input
+ var input = $('input_enabled'), oldValue = input.getValue();
+ this.assertEqual(input, input.setValue('foo'), 'setValue chaining is broken');
+ this.assertEqual('foo', input.getValue(), 'value improperly set');
+ input.setValue(oldValue);
+ this.assertEqual(oldValue, input.getValue(), 'value improperly restored to original');
+
+ // checkbox
+ input = $('checkbox_hack');
+ input.setValue(false);
+ this.assertEqual(null, input.getValue(), 'checkbox should be unchecked');
+ input.setValue(true);
+ this.assertEqual("1", input.getValue(), 'checkbox should be checked');
+
+ // selectbox
+ input = $('bigform')['vu'];
+ input.setValue('3');
+ this.assertEqual('3', input.getValue(), 'single select option improperly set');
+ input.setValue('1');
+ this.assertEqual('1', input.getValue());
+
+ // multiple select
+ input = $('bigform')['vm[]'];
+ input.setValue(['2', '3']);
+ this.assertEnumEqual(['2', '3'], input.getValue(),
+ 'multiple select options improperly set');
+ input.setValue(['1', '3']);
+ this.assertEnumEqual(['1', '3'], input.getValue());
+
+ // test chainability
+ var input = $('input_enabled');
+ this.assert(Object.isElement(button.setValue('New Value')));
+ this.assert(Object.isElement(input.setValue('New Value')));
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/hash_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/hash_test.js
new file mode 100644
index 0000000000..cc3d083135
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/hash_test.js
@@ -0,0 +1,178 @@
+new Test.Unit.Runner({
+ testSet: function() {
+ var h = $H({a: 'A'})
+
+ this.assertEqual('B', h.set('b', 'B'));
+ this.assertHashEqual({a: 'A', b: 'B'}, h);
+
+ this.assertUndefined(h.set('c'));
+ this.assertHashEqual({a: 'A', b: 'B', c: undefined}, h);
+ },
+
+ testGet: function() {
+ var h = $H({a: 'A'});
+ this.assertEqual('A', h.get('a'));
+ this.assertUndefined(h.a);
+ this.assertUndefined($H({}).get('a'));
+
+ this.assertUndefined($H({}).get('toString'));
+ this.assertUndefined($H({}).get('constructor'));
+ },
+
+ testUnset: function() {
+ var hash = $H(Fixtures.many);
+ this.assertEqual('B', hash.unset('b'));
+ this.assertHashEqual({a:'A', c: 'C', d:'D#'}, hash);
+ this.assertUndefined(hash.unset('z'));
+ this.assertHashEqual({a:'A', c: 'C', d:'D#'}, hash);
+ // not equivalent to Hash#remove
+ this.assertEqual('A', hash.unset('a', 'c'));
+ this.assertHashEqual({c: 'C', d:'D#'}, hash);
+ },
+
+ testToObject: function() {
+ var hash = $H(Fixtures.many), object = hash.toObject();
+ this.assertInstanceOf(Object, object);
+ this.assertHashEqual(Fixtures.many, object);
+ this.assertNotIdentical(Fixtures.many, object);
+ hash.set('foo', 'bar');
+ this.assertHashNotEqual(object, hash.toObject());
+ },
+
+ testConstruct: function() {
+ var object = Object.clone(Fixtures.one);
+ var h = new Hash(object), h2 = $H(object);
+ this.assertInstanceOf(Hash, h);
+ this.assertInstanceOf(Hash, h2);
+
+ this.assertHashEqual({}, new Hash());
+ this.assertHashEqual(object, h);
+ this.assertHashEqual(object, h2);
+
+ h.set('foo', 'bar');
+ this.assertHashNotEqual(object, h);
+
+ var clone = $H(h);
+ this.assertInstanceOf(Hash, clone);
+ this.assertHashEqual(h, clone);
+ h.set('foo', 'foo');
+ this.assertHashNotEqual(h, clone);
+ this.assertIdentical($H, Hash.from);
+ },
+
+ testKeys: function() {
+ this.assertEnumEqual([], $H({}).keys());
+ this.assertEnumEqual(['a'], $H(Fixtures.one).keys());
+ this.assertEnumEqual($w('a b c d'), $H(Fixtures.many).keys().sort());
+ this.assertEnumEqual($w('plus quad'), $H(Fixtures.functions).keys().sort());
+ },
+
+ testValues: function() {
+ this.assertEnumEqual([], $H({}).values());
+ this.assertEnumEqual(['A#'], $H(Fixtures.one).values());
+ this.assertEnumEqual($w('A B C D#'), $H(Fixtures.many).values().sort());
+ this.assertEnumEqual($w('function function'),
+ $H(Fixtures.functions).values().map(function(i){ return typeof i }));
+ this.assertEqual(9, $H(Fixtures.functions).get('quad')(3));
+ this.assertEqual(6, $H(Fixtures.functions).get('plus')(3));
+ },
+
+ testIndex: function() {
+ this.assertUndefined($H().index('foo'));
+
+ this.assert('a', $H(Fixtures.one).index('A#'));
+ this.assert('a', $H(Fixtures.many).index('A'));
+ this.assertUndefined($H(Fixtures.many).index('Z'))
+
+ var hash = $H({a:1,b:'2',c:1});
+ this.assert(['a','c'].include(hash.index(1)));
+ this.assertUndefined(hash.index('1'));
+ },
+
+ testMerge: function() {
+ var h = $H(Fixtures.many);
+ this.assertNotIdentical(h, h.merge());
+ this.assertNotIdentical(h, h.merge({}));
+ this.assertInstanceOf(Hash, h.merge());
+ this.assertInstanceOf(Hash, h.merge({}));
+ this.assertHashEqual(h, h.merge());
+ this.assertHashEqual(h, h.merge({}));
+ this.assertHashEqual(h, h.merge($H()));
+ this.assertHashEqual({a:'A', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.merge({aaa: 'AAA'}));
+ this.assertHashEqual({a:'A#', b:'B', c:'C', d:'D#' }, h.merge(Fixtures.one));
+ },
+
+ testUpdate: function() {
+ var h = $H(Fixtures.many);
+ this.assertIdentical(h, h.update());
+ this.assertIdentical(h, h.update({}));
+ this.assertHashEqual(h, h.update());
+ this.assertHashEqual(h, h.update({}));
+ this.assertHashEqual(h, h.update($H()));
+ this.assertHashEqual({a:'A', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.update({aaa: 'AAA'}));
+ this.assertHashEqual({a:'A#', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.update(Fixtures.one));
+ },
+
+ testToQueryString: function() {
+ this.assertEqual('', $H({}).toQueryString());
+ this.assertEqual('a%23=A', $H({'a#': 'A'}).toQueryString());
+ this.assertEqual('a=A%23', $H(Fixtures.one).toQueryString());
+ this.assertEqual('a=A&b=B&c=C&d=D%23', $H(Fixtures.many).toQueryString());
+ this.assertEqual("a=b&c", $H(Fixtures.value_undefined).toQueryString());
+ this.assertEqual("a=b&c", $H("a=b&c".toQueryParams()).toQueryString());
+ this.assertEqual("a=b&c=", $H(Fixtures.value_null).toQueryString());
+ this.assertEqual("a=b&c=0", $H(Fixtures.value_zero).toQueryString());
+ this.assertEqual("color=r&color=g&color=b", $H(Fixtures.multiple).toQueryString());
+ this.assertEqual("color=r&color=&color=g&color&color=0", $H(Fixtures.multiple_nil).toQueryString());
+ this.assertEqual("color=&color", $H(Fixtures.multiple_all_nil).toQueryString());
+ this.assertEqual("", $H(Fixtures.multiple_empty).toQueryString());
+ this.assertEqual("", $H({foo: {}, bar: {}}).toQueryString());
+ this.assertEqual("stuff%5B%5D=%24&stuff%5B%5D=a&stuff%5B%5D=%3B", $H(Fixtures.multiple_special).toQueryString());
+ this.assertHashEqual(Fixtures.multiple_special, $H(Fixtures.multiple_special).toQueryString().toQueryParams());
+ this.assertIdentical(Object.toQueryString, Hash.toQueryString);
+ },
+
+ testInspect: function() {
+ this.assertEqual('#<Hash:{}>', $H({}).inspect());
+ this.assertEqual("#<Hash:{'a': 'A#'}>", $H(Fixtures.one).inspect());
+ this.assertEqual("#<Hash:{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D#'}>", $H(Fixtures.many).inspect());
+ },
+
+ testClone: function() {
+ var h = $H(Fixtures.many);
+ this.assertHashEqual(h, h.clone());
+ this.assertInstanceOf(Hash, h.clone());
+ this.assertNotIdentical(h, h.clone());
+ },
+
+ testToJSON: function() {
+ this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
+ $H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}).toJSON());
+ },
+
+ testAbilityToContainAnyKey: function() {
+ var h = $H({ _each: 'E', map: 'M', keys: 'K', pluck: 'P', unset: 'U' });
+ this.assertEnumEqual($w('_each keys map pluck unset'), h.keys().sort());
+ this.assertEqual('U', h.unset('unset'));
+ this.assertHashEqual({ _each: 'E', map: 'M', keys: 'K', pluck: 'P' }, h);
+ },
+
+ testHashToTemplateReplacements: function() {
+ var template = new Template("#{a} #{b}"), hash = $H({ a: "hello", b: "world" });
+ this.assertEqual("hello world", template.evaluate(hash.toObject()));
+ this.assertEqual("hello world", template.evaluate(hash));
+ this.assertEqual("hello", "#{a}".interpolate(hash));
+ },
+
+ testPreventIterationOverShadowedProperties: function() {
+ // redundant now that object is systematically cloned.
+ var FooMaker = function(value) {
+ this.key = value;
+ };
+ FooMaker.prototype.key = 'foo';
+ var foo = new FooMaker('bar');
+ this.assertEqual("key=bar", new Hash(foo).toQueryString());
+ this.assertEqual("key=bar", new Hash(new Hash(foo)).toQueryString());
+ }
+
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/number_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/number_test.js
new file mode 100644
index 0000000000..4e09529b2c
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/number_test.js
@@ -0,0 +1,44 @@
+new Test.Unit.Runner({
+
+ testNumberMathMethods: function() {
+ this.assertEqual(1, (0.9).round());
+ this.assertEqual(-2, (-1.9).floor());
+ this.assertEqual(-1, (-1.9).ceil());
+
+ $w('abs floor round ceil').each(function(method) {
+ this.assertEqual(Math[method](Math.PI), Math.PI[method]());
+ }, this);
+ },
+
+ testNumberToColorPart: function() {
+ this.assertEqual('00', (0).toColorPart());
+ this.assertEqual('0a', (10).toColorPart());
+ this.assertEqual('ff', (255).toColorPart());
+ },
+
+ testNumberToPaddedString: function() {
+ this.assertEqual('00', (0).toPaddedString(2, 16));
+ this.assertEqual('0a', (10).toPaddedString(2, 16));
+ this.assertEqual('ff', (255).toPaddedString(2, 16));
+ this.assertEqual('000', (0).toPaddedString(3));
+ this.assertEqual('010', (10).toPaddedString(3));
+ this.assertEqual('100', (100).toPaddedString(3));
+ this.assertEqual('1000', (1000).toPaddedString(3));
+ },
+
+ testNumberToJSON: function() {
+ this.assertEqual('null', Number.NaN.toJSON());
+ this.assertEqual('0', (0).toJSON());
+ this.assertEqual('-293', (-293).toJSON());
+ },
+
+ testNumberTimes: function() {
+ var results = [];
+ (5).times(function(i) { results.push(i) });
+ this.assertEnumEqual($R(0, 4), results);
+
+ results = [];
+ (5).times(function(i) { results.push(i * this.i) }, { i: 2 });
+ this.assertEnumEqual([0, 2, 4, 6, 8], results);
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/position_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/position_test.js
new file mode 100644
index 0000000000..5cea791b1f
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/position_test.js
@@ -0,0 +1,44 @@
+var testVar = 'to be updated';
+
+new Test.Unit.Runner({
+
+ setup: function() {
+ scrollTo(0,0);
+ Position.prepare();
+ Position.includeScrollOffsets = false;
+ },
+
+ teardown: function() {
+ scrollTo(0,0);
+ Position.prepare();
+ Position.includeScrollOffsets = false;
+ },
+
+ testPrepare: function() {
+ Position.prepare();
+ this.assertEqual(0, Position.deltaX);
+ this.assertEqual(0, Position.deltaY);
+ scrollTo(20,30);
+ Position.prepare();
+ this.assertEqual(20, Position.deltaX);
+ this.assertEqual(30, Position.deltaY);
+ },
+
+ testWithin: function() {
+ [true, false].each(function(withScrollOffsets) {
+ Position.includeScrollOffsets = withScrollOffsets;
+ this.assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
+ this.assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
+ this.assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
+ this.assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
+ }, this);
+
+ scrollTo(20,30);
+ Position.prepare();
+ Position.includeScrollOffsets = true;
+ this.assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
+ this.assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
+ this.assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
+ this.assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/range_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/range_test.js
new file mode 100644
index 0000000000..bcf5acb0b5
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/range_test.js
@@ -0,0 +1,58 @@
+new Test.Unit.Runner({
+
+ testInclude: function() {
+ this.assert(!$R(0, 0, true).include(0));
+ this.assert($R(0, 0, false).include(0));
+
+ this.assert($R(0, 5, true).include(0));
+ this.assert($R(0, 5, true).include(4));
+ this.assert(!$R(0, 5, true).include(5));
+
+ this.assert($R(0, 5, false).include(0));
+ this.assert($R(0, 5, false).include(5));
+ this.assert(!$R(0, 5, false).include(6));
+ },
+
+ testEach: function() {
+ var results = [];
+ $R(0, 0, true).each(function(value) {
+ results.push(value);
+ });
+
+ this.assertEnumEqual([], results);
+
+ results = [];
+ $R(0, 3, false).each(function(value) {
+ results.push(value);
+ });
+
+ this.assertEnumEqual([0, 1, 2, 3], results);
+ },
+
+ testAny: function() {
+ this.assert(!$R(1, 1, true).any());
+ this.assert($R(0, 3, false).any(function(value) {
+ return value == 3;
+ }));
+ },
+
+ testAll: function() {
+ this.assert($R(1, 1, true).all());
+ this.assert($R(0, 3, false).all(function(value) {
+ return value <= 3;
+ }));
+ },
+
+ testToArray: function() {
+ this.assertEnumEqual([], $R(0, 0, true).toArray());
+ this.assertEnumEqual([0], $R(0, 0, false).toArray());
+ this.assertEnumEqual([0], $R(0, 1, true).toArray());
+ this.assertEnumEqual([0, 1], $R(0, 1, false).toArray());
+ this.assertEnumEqual([-3, -2, -1, 0, 1, 2], $R(-3, 3, true).toArray());
+ this.assertEnumEqual([-3, -2, -1, 0, 1, 2, 3], $R(-3, 3, false).toArray());
+ },
+
+ testDefaultsToNotExclusive: function() {
+ this.assertEnumEqual($R(-3,3), $R(-3,3,false));
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/selector_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/selector_test.js
new file mode 100644
index 0000000000..72506fc17d
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/selector_test.js
@@ -0,0 +1,377 @@
+var $RunBenchmarks = false;
+
+new Test.Unit.Runner({
+
+ testSelectorWithTagName: function() {
+ this.assertEnumEqual($A(document.getElementsByTagName('li')), $$('li'));
+ this.assertEnumEqual([$('strong')], $$('strong'));
+ this.assertEnumEqual([], $$('nonexistent'));
+
+ var allNodes = $A(document.getElementsByTagName('*')).select( function(node) {
+ return node.tagName !== '!';
+ });
+ this.assertEnumEqual(allNodes, $$('*'));
+ },
+
+ testSelectorWithId: function() {
+ this.assertEnumEqual([$('fixtures')], $$('#fixtures'));
+ this.assertEnumEqual([], $$('#nonexistent'));
+ this.assertEnumEqual([$('troubleForm')], $$('#troubleForm'));
+ },
+
+ testSelectorWithClassName: function() {
+ this.assertEnumEqual($('p', 'link_1', 'item_1'), $$('.first'));
+ this.assertEnumEqual([], $$('.second'));
+ },
+
+ testSelectorWithTagNameAndId: function() {
+ this.assertEnumEqual([$('strong')], $$('strong#strong'));
+ this.assertEnumEqual([], $$('p#strong'));
+ },
+
+ testSelectorWithTagNameAndClassName: function() {
+ this.assertEnumEqual($('link_1', 'link_2'), $$('a.internal'));
+ this.assertEnumEqual([$('link_2')], $$('a.internal.highlight'));
+ this.assertEnumEqual([$('link_2')], $$('a.highlight.internal'));
+ this.assertEnumEqual([], $$('a.highlight.internal.nonexistent'));
+ },
+
+ testSelectorWithIdAndClassName: function() {
+ this.assertEnumEqual([$('link_2')], $$('#link_2.internal'));
+ this.assertEnumEqual([$('link_2')], $$('.internal#link_2'));
+ this.assertEnumEqual([$('link_2')], $$('#link_2.internal.highlight'));
+ this.assertEnumEqual([], $$('#link_2.internal.nonexistent'));
+ },
+
+ testSelectorWithTagNameAndIdAndClassName: function() {
+ this.assertEnumEqual([$('link_2')], $$('a#link_2.internal'));
+ this.assertEnumEqual([$('link_2')], $$('a.internal#link_2'));
+ this.assertEnumEqual([$('item_1')], $$('li#item_1.first'));
+ this.assertEnumEqual([], $$('li#item_1.nonexistent'));
+ this.assertEnumEqual([], $$('li#item_1.first.nonexistent'));
+ },
+
+ test$$MatchesAncestryWithTokensSeparatedByWhitespace: function() {
+ this.assertEnumEqual($('em2', 'em', 'span'), $$('#fixtures a *'));
+ this.assertEnumEqual([$('p')], $$('div#fixtures p'));
+ },
+
+ test$$CombinesResultsWhenMultipleExpressionsArePassed: function() {
+ this.assertEnumEqual($('link_1', 'link_2', 'item_1', 'item_2', 'item_3'), $$('#p a', ' ul#list li '));
+ },
+
+ testSelectorWithTagNameAndAttributeExistence: function() {
+ this.assertEnumEqual($$('#fixtures h1'), $$('h1[class]'), 'h1[class]');
+ this.assertEnumEqual($$('#fixtures h1'), $$('h1[CLASS]'), 'h1[CLASS]');
+ this.assertEnumEqual([$('item_3')], $$('li#item_3[class]'), 'li#item_3[class]');
+ },
+
+ testSelectorWithTagNameAndSpecificAttributeValue: function() {
+ this.assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href="#"]'));
+ this.assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href=#]'));
+ },
+
+ testSelectorWithTagNameAndWhitespaceTokenizedAttributeValue: function() {
+ this.assertEnumEqual($('link_1', 'link_2'), $$('a[class~="internal"]'), "a[class~=\"internal\"]");
+ this.assertEnumEqual($('link_1', 'link_2'), $$('a[class~=internal]'), "a[class~=internal]");
+ },
+
+ testSelectorWithAttributeAndNoTagName: function() {
+ this.assertEnumEqual($(document.body).select('a[href]'), $(document.body).select('[href]'));
+ this.assertEnumEqual($$('a[class~="internal"]'), $$('[class~=internal]'));
+ this.assertEnumEqual($$('*[id]'), $$('[id]'));
+ this.assertEnumEqual($('checked_radio', 'unchecked_radio'), $$('[type=radio]'));
+ this.assertEnumEqual($$('*[type=checkbox]'), $$('[type=checkbox]'));
+ this.assertEnumEqual($('with_title', 'commaParent'), $$('[title]'));
+ this.assertEnumEqual($$('#troubleForm *[type=radio]'), $$('#troubleForm [type=radio]'));
+ this.assertEnumEqual($$('#troubleForm *[type]'), $$('#troubleForm [type]'));
+ },
+
+ testSelectorWithUniversalAndHyphenTokenizedAttributeValue: function() {
+ this.assertEnumEqual([$('item_3')], $$('*[xml:lang|="es"]'));
+ this.assertEnumEqual([$('item_3')], $$('*[xml:lang|="ES"]'));
+ },
+
+ testSelectorWithTagNameAndNegatedAttributeValue: function() {
+ this.assertEnumEqual([], $$('a[href!="#"]'));
+ },
+
+ testSelectorWithBracketAttributeValue: function() {
+ this.assertEnumEqual($('chk_1', 'chk_2'), $$('#troubleForm2 input[name="brackets[5][]"]'));
+ this.assertEnumEqual([$('chk_1')], $$('#troubleForm2 input[name="brackets[5][]"]:checked'));
+ this.assertEnumEqual([$('chk_2')], $$('#troubleForm2 input[name="brackets[5][]"][value=2]'));
+ this.assertEnumEqual([], $$('#troubleForm2 input[name=brackets[5][]]'));
+ },
+
+ test$$WithNestedAttributeSelectors: function() {
+ this.assertEnumEqual([$('strong')], $$('div[style] p[id] strong'), 'div[style] p[id] strong');
+ },
+
+ testSelectorWithMultipleConditions: function() {
+ this.assertEnumEqual([$('link_3')], $$('a[class~=external][href="#"]'),
+ 'a[class~=external][href="#"]');
+ this.assertEnumEqual([], $$('a[class~=external][href!="#"]'),
+ 'a[class~=external][href!="#"]');
+ },
+
+ testSelectorMatchElements: function() {
+ this.assertElementsMatch(Selector.matchElements($('list').descendants(), 'li'), '#item_1', '#item_2', '#item_3');
+ this.assertElementsMatch(Selector.matchElements($('fixtures').descendants(), 'a.internal'), '#link_1', '#link_2');
+ this.assertEnumEqual([], Selector.matchElements($('fixtures').descendants(), 'p.last'));
+ this.assertElementsMatch(Selector.matchElements($('fixtures').descendants(), '.inexistant, a.internal'), '#link_1', '#link_2');
+ },
+
+ testSelectorFindElement: function() {
+ this.assertElementMatches(Selector.findElement($('list').descendants(), 'li'), 'li#item_1.first');
+ this.assertElementMatches(Selector.findElement($('list').descendants(), 'li', 1), 'li#item_2');
+ this.assertElementMatches(Selector.findElement($('list').descendants(), 'li#item_3'), 'li');
+ this.assertEqual(undefined, Selector.findElement($('list').descendants(), 'em'));
+ },
+
+ testElementMatch: function() {
+ var span = $('dupL1');
+
+ // tests that should pass
+ this.assert(span.match('span'));
+ this.assert(span.match('span#dupL1'));
+ this.assert(span.match('div > span'), 'child combinator');
+ this.assert(span.match('#dupContainer span'), 'descendant combinator');
+ this.assert(span.match('#dupL1'), 'ID only');
+ this.assert(span.match('span.span_foo'), 'class name 1');
+ this.assert(span.match('span.span_bar'), 'class name 2');
+ this.assert(span.match('span:first-child'), 'first-child pseudoclass');
+
+ this.assert(!span.match('span.span_wtf'), 'bogus class name');
+ this.assert(!span.match('#dupL2'), 'different ID');
+ this.assert(!span.match('div'), 'different tag name');
+ this.assert(!span.match('span span'), 'different ancestry');
+ this.assert(!span.match('span > span'), 'different parent');
+ this.assert(!span.match('span:nth-child(5)'), 'different pseudoclass');
+
+ this.assert(!$('link_2').match('a[rel^=external]'));
+ this.assert($('link_1').match('a[rel^=external]'));
+ this.assert($('link_1').match('a[rel^="external"]'));
+ this.assert($('link_1').match("a[rel^='external']"));
+
+ this.assert(span.match({ match: function(element) { return true }}), 'custom selector');
+ this.assert(!span.match({ match: function(element) { return false }}), 'custom selector');
+ },
+
+ testSelectorWithSpaceInAttributeValue: function() {
+ this.assertEnumEqual([$('with_title')], $$('cite[title="hello world!"]'));
+ },
+
+ // AND NOW COME THOSE NEW TESTS AFTER ANDREW'S REWRITE!
+
+ testSelectorWithNamespacedAttributes: function() {
+ if (Prototype.BrowserFeatures.XPath) {
+ this.assertUndefined(new Selector('html[xml:lang]').xpath);
+ this.assertUndefined(new Selector('body p[xml:lang]').xpath);
+ } else
+ this.info("Could not test XPath bypass: no XPath to begin with!");
+
+ this.assertElementsMatch($$('[xml:lang]'), 'html', '#item_3');
+ this.assertElementsMatch($$('*[xml:lang]'), 'html', '#item_3');
+ },
+
+ testSelectorWithChild: function() {
+ this.assertEnumEqual($('link_1', 'link_2'), $$('p.first > a'));
+ this.assertEnumEqual($('father', 'uncle'), $$('div#grandfather > div'));
+ this.assertEnumEqual($('level2_1', 'level2_2'), $$('#level1>span'));
+ this.assertEnumEqual($('level2_1', 'level2_2'), $$('#level1 > span'));
+ this.assertEnumEqual($('level3_1', 'level3_2'), $$('#level2_1 > *'));
+ this.assertEnumEqual([], $$('div > #nonexistent'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#level1 > span') }, 1000);
+ });
+ },
+
+ testSelectorWithAdjacence: function() {
+ this.assertEnumEqual([$('uncle')], $$('div.brothers + div.brothers'));
+ this.assertEnumEqual([$('uncle')], $$('div.brothers + div'));
+ this.assertEqual($('level2_2'), $$('#level2_1+span').reduce());
+ this.assertEqual($('level2_2'), $$('#level2_1 + span').reduce());
+ this.assertEqual($('level2_2'), $$('#level2_1 + *').reduce());
+ this.assertEnumEqual([], $$('#level2_2 + span'));
+ this.assertEqual($('level3_2'), $$('#level3_1 + span').reduce());
+ this.assertEqual($('level3_2'), $$('#level3_1 + *').reduce());
+ this.assertEnumEqual([], $$('#level3_2 + *'));
+ this.assertEnumEqual([], $$('#level3_1 + em'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#level3_1 + span') }, 1000);
+ });
+ },
+
+ testSelectorWithLaterSibling: function() {
+ this.assertEnumEqual([$('list')], $$('h1 ~ ul'));
+ this.assertEqual($('level2_2'), $$('#level2_1 ~ span').reduce());
+ this.assertEnumEqual($('level2_2', 'level2_3'), $$('#level2_1 ~ *').reduce());
+ this.assertEnumEqual([], $$('#level2_2 ~ span'));
+ this.assertEnumEqual([], $$('#level3_2 ~ *'));
+ this.assertEnumEqual([], $$('#level3_1 ~ em'));
+ this.assertEnumEqual([$('level3_2')], $$('#level3_1 ~ #level3_2'));
+ this.assertEnumEqual([$('level3_2')], $$('span ~ #level3_2'));
+ this.assertEnumEqual([], $$('div ~ #level3_2'));
+ this.assertEnumEqual([], $$('div ~ #level2_3'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#level2_1 ~ span') }, 1000);
+ });
+ },
+
+ testSelectorWithNewAttributeOperators: function() {
+ this.assertEnumEqual($('father', 'uncle'), $$('div[class^=bro]'), 'matching beginning of string');
+ this.assertEnumEqual($('father', 'uncle'), $$('div[class$=men]'), 'matching end of string');
+ this.assertEnumEqual($('father', 'uncle'), $$('div[class*="ers m"]'), 'matching substring')
+ this.assertEnumEqual($('level2_1', 'level2_2', 'level2_3'), $$('#level1 *[id^="level2_"]'));
+ this.assertEnumEqual($('level2_1', 'level2_2', 'level2_3'), $$('#level1 *[id^=level2_]'));
+ this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 *[id$="_1"]'));
+ this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 *[id$=_1]'));
+ this.assertEnumEqual($('level2_1', 'level3_2', 'level2_2', 'level2_3'), $$('#level1 *[id*="2"]'));
+ this.assertEnumEqual($('level2_1', 'level3_2', 'level2_2', 'level2_3'), $$('#level1 *[id*=2]'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#level1 *[id^=level2_]') }, 1000, '[^=]');
+ this.benchmark(function() { $$('#level1 *[id$=_1]') }, 1000, '[$=]');
+ this.benchmark(function() { $$('#level1 *[id*=_2]') }, 1000, '[*=]');
+ });
+ },
+
+ testSelectorWithDuplicates: function() {
+ this.assertEnumEqual($$('div div'), $$('div div').uniq());
+ this.assertEnumEqual($('dupL2', 'dupL3', 'dupL4', 'dupL5'), $$('#dupContainer span span'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#dupContainer span span') }, 1000);
+ });
+ },
+
+ testSelectorWithFirstLastOnlyNthNthLastChild: function() {
+ this.assertEnumEqual([$('level2_1')], $$('#level1>*:first-child'));
+ this.assertEnumEqual($('level2_1', 'level3_1', 'level_only_child'), $$('#level1 *:first-child'));
+ this.assertEnumEqual([$('level2_3')], $$('#level1>*:last-child'));
+ this.assertEnumEqual($('level3_2', 'level_only_child', 'level2_3'), $$('#level1 *:last-child'));
+ this.assertEnumEqual([$('level2_3')], $$('#level1>div:last-child'));
+ this.assertEnumEqual([$('level2_3')], $$('#level1 div:last-child'));
+ this.assertEnumEqual([], $$('#level1>div:first-child'));
+ this.assertEnumEqual([], $$('#level1>span:last-child'));
+ this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 span:first-child'));
+ this.assertEnumEqual([], $$('#level1:first-child'));
+ this.assertEnumEqual([], $$('#level1>*:only-child'));
+ this.assertEnumEqual([$('level_only_child')], $$('#level1 *:only-child'));
+ this.assertEnumEqual([], $$('#level1:only-child'));
+ this.assertEnumEqual([$('link_2')], $$('#p *:nth-last-child(2)'), 'nth-last-child');
+ this.assertEnumEqual([$('link_2')], $$('#p *:nth-child(3)'), 'nth-child');
+ this.assertEnumEqual([$('link_2')], $$('#p a:nth-child(3)'), 'nth-child');
+ this.assertEnumEqual($('item_2', 'item_3'), $$('#list > li:nth-child(n+2)'));
+ this.assertEnumEqual($('item_1', 'item_2'), $$('#list > li:nth-child(-n+2)'));
+ $RunBenchmarks && this.wait(500, function() {
+ this.benchmark(function() { $$('#level1 *:first-child') }, 1000, ':first-child');
+ this.benchmark(function() { $$('#level1 *:last-child') }, 1000, ':last-child');
+ this.benchmark(function() { $$('#level1 *:only-child') }, 1000, ':only-child');
+ });
+ },
+
+ testSelectorWithFirstLastNthNthLastOfType: function() {
+ this.assertEnumEqual([$('link_2')], $$('#p a:nth-of-type(2)'), 'nth-of-type');
+ this.assertEnumEqual([$('link_1')], $$('#p a:nth-of-type(1)'), 'nth-of-type');
+ this.assertEnumEqual([$('link_2')], $$('#p a:nth-last-of-type(1)'), 'nth-last-of-type');
+ this.assertEnumEqual([$('link_1')], $$('#p a:first-of-type'), 'first-of-type');
+ this.assertEnumEqual([$('link_2')], $$('#p a:last-of-type'), 'last-of-type');
+ },
+
+ testSelectorWithNot: function() {
+ this.assertEnumEqual([$('link_2')], $$('#p a:not(a:first-of-type)'), 'first-of-type');
+ this.assertEnumEqual([$('link_1')], $$('#p a:not(a:last-of-type)'), 'last-of-type');
+ this.assertEnumEqual([$('link_2')], $$('#p a:not(a:nth-of-type(1))'), 'nth-of-type');
+ this.assertEnumEqual([$('link_1')], $$('#p a:not(a:nth-last-of-type(1))'), 'nth-last-of-type');
+ this.assertEnumEqual([$('link_2')], $$('#p a:not([rel~=nofollow])'), 'attribute 1');
+ this.assertEnumEqual([$('link_2')], $$('#p a:not(a[rel^=external])'), 'attribute 2');
+ this.assertEnumEqual([$('link_2')], $$('#p a:not(a[rel$=nofollow])'), 'attribute 3');
+ this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"]) > em'), 'attribute 4')
+ this.assertEnumEqual([$('item_2')], $$('#list li:not(#item_1):not(#item_3)'), 'adjacent :not clauses');
+ this.assertEnumEqual([$('son')], $$('#grandfather > div:not(#uncle) #son'));
+ this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"]) em'), 'attribute 4 + all descendants');
+ this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"])>em'), 'attribute 4 (without whitespace)');
+ },
+
+ testSelectorWithEnabledDisabledChecked: function() {
+ this.assertEnumEqual([$('disabled_text_field')], $$('#troubleForm > *:disabled'));
+ // bug 452708 this.assertEnumEqual($('troubleForm').getInputs().without($('disabled_text_field'), $('hidden')), $$('#troubleForm > *:enabled'));
+ this.assertEnumEqual($('checked_box', 'checked_radio'), $$('#troubleForm *:checked'));
+ },
+
+ testSelectorWithEmpty: function() {
+ $('level3_1').innerHTML = "";
+ this.assertEnumEqual($('level3_1', 'level3_2', 'level2_3'),
+ $$('#level1 *:empty'), '#level1 *:empty');
+ this.assertEnumEqual([], $$('#level_only_child:empty'), 'newlines count as content!');
+ },
+
+ testIdenticalResultsFromEquivalentSelectors: function() {
+ this.assertEnumEqual($$('div.brothers'), $$('div[class~=brothers]'));
+ this.assertEnumEqual($$('div.brothers'), $$('div[class~=brothers].brothers'));
+ this.assertEnumEqual($$('div:not(.brothers)'), $$('div:not([class~=brothers])'));
+ this.assertEnumEqual($$('li ~ li'), $$('li:not(:first-child)'));
+ this.assertEnumEqual($$('ul > li'), $$('ul > li:nth-child(n)'));
+ this.assertEnumEqual($$('ul > li:nth-child(even)'), $$('ul > li:nth-child(2n)'));
+ this.assertEnumEqual($$('ul > li:nth-child(odd)'), $$('ul > li:nth-child(2n+1)'));
+ this.assertEnumEqual($$('ul > li:first-child'), $$('ul > li:nth-child(1)'));
+ this.assertEnumEqual($$('ul > li:last-child'), $$('ul > li:nth-last-child(1)'));
+ this.assertEnumEqual($$('ul > li:nth-child(n-999)'), $$('ul > li'));
+ this.assertEnumEqual($$('ul>li'), $$('ul > li'));
+ this.assertEnumEqual($$('#p a:not(a[rel$="nofollow"])>em'), $$('#p a:not(a[rel$="nofollow"]) > em'))
+ },
+
+ testSelectorsThatShouldReturnNothing: function() {
+ this.assertEnumEqual([], $$('span:empty > *'));
+ this.assertEnumEqual([], $$('div.brothers:not(.brothers)'));
+ this.assertEnumEqual([], $$('#level2_2 :only-child:not(:last-child)'));
+ this.assertEnumEqual([], $$('#level2_2 :only-child:not(:first-child)'));
+ },
+
+ testCommasFor$$: function() {
+ this.assertEnumEqual($('list', 'p', 'link_1', 'item_1', 'item_3', 'troubleForm'), $$('#list, .first,*[xml:lang="es-us"] , #troubleForm'));
+ this.assertEnumEqual($('list', 'p', 'link_1', 'item_1', 'item_3', 'troubleForm'), $$('#list, .first,', '*[xml:lang="es-us"] , #troubleForm'));
+ this.assertEnumEqual($('commaParent', 'commaChild'), $$('form[title*="commas,"], input[value="#commaOne,#commaTwo"]'));
+ this.assertEnumEqual($('commaParent', 'commaChild'), $$('form[title*="commas,"]', 'input[value="#commaOne,#commaTwo"]'));
+ },
+
+ testSelectorExtendsAllNodes: function(){
+ var element = document.createElement('div');
+ (3).times(function(){
+ element.appendChild(document.createElement('div'));
+ });
+ element.setAttribute('id','scratch_element');
+ $$('body')[0].appendChild(element);
+
+ var results = $$('#scratch_element div');
+ this.assert(typeof results[0].show == 'function');
+ this.assert(typeof results[1].show == 'function');
+ this.assert(typeof results[2].show == 'function');
+ },
+
+ testCountedIsNotAnAttribute: function() {
+ var el = $('list');
+ Selector.handlers.mark([el]);
+ this.assert(!el.innerHTML.include("_counted"));
+ Selector.handlers.unmark([el]);
+ this.assert(!el.innerHTML.include("_counted"));
+ },
+
+ testCopiedNodesGetIncluded: function() {
+ this.assertElementsMatch(
+ Selector.matchElements($('counted_container').descendants(), 'div'),
+ 'div.is_counted'
+ );
+ $('counted_container').innerHTML += $('counted_container').innerHTML;
+ this.assertElementsMatch(
+ Selector.matchElements($('counted_container').descendants(), 'div'), 'div.is_counted',
+ 'div.is_counted'
+ );
+ },
+
+ testElementDown: function() {
+ var a = $('dupL4');
+ var b = $('dupContainer').down('#dupL4');
+
+ this.assertEqual(a, b);
+ }
+});
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/string_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/string_test.js
new file mode 100644
index 0000000000..c4e21f095b
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/string_test.js
@@ -0,0 +1,540 @@
+new Test.Unit.Runner({
+ testInterpret: function(){
+ this.assertIdentical('true', String.interpret(true));
+ this.assertIdentical('123', String.interpret(123));
+ this.assertIdentical('foo bar', String.interpret('foo bar'));
+ this.assertIdentical(
+ 'object string',
+ String.interpret({ toString: function(){ return 'object string' } }));
+
+ this.assertIdentical('0', String.interpret(0));
+ this.assertIdentical('false', String.interpret(false));
+ this.assertIdentical('', String.interpret(undefined));
+ this.assertIdentical('', String.interpret(null));
+ this.assertIdentical('', String.interpret(''));
+ },
+
+ testGsubWithReplacementFunction: function() {
+ var source = 'foo boo boz';
+
+ this.assertEqual('Foo Boo BoZ',
+ source.gsub(/[^o]+/, function(match) {
+ return match[0].toUpperCase()
+ }));
+ this.assertEqual('f2 b2 b1z',
+ source.gsub(/o+/, function(match) {
+ return match[0].length;
+ }));
+ this.assertEqual('f0 b0 b1z',
+ source.gsub(/o+/, function(match) {
+ return match[0].length % 2;
+ }));
+
+ },
+
+ testGsubWithReplacementString: function() {
+ var source = 'foo boo boz';
+
+ this.assertEqual('foobooboz',
+ source.gsub(/\s+/, ''));
+ this.assertEqual(' z',
+ source.gsub(/(.)(o+)/, ''));
+
+ this.assertEqual('ウィメンズ2007<br/>クルーズコレクション',
+ 'ウィメンズ2007\nクルーズコレクション'.gsub(/\n/,'<br/>'));
+ this.assertEqual('ウィメンズ2007<br/>クルーズコレクション',
+ 'ウィメンズ2007\nクルーズコレクション'.gsub('\n','<br/>'));
+ },
+
+ testGsubWithReplacementTemplateString: function() {
+ var source = 'foo boo boz';
+
+ this.assertEqual('-oo-#{1}- -oo-#{1}- -o-#{1}-z',
+ source.gsub(/(.)(o+)/, '-#{2}-\\#{1}-'));
+ this.assertEqual('-foo-f- -boo-b- -bo-b-z',
+ source.gsub(/(.)(o+)/, '-#{0}-#{1}-'));
+ this.assertEqual('-oo-f- -oo-b- -o-b-z',
+ source.gsub(/(.)(o+)/, '-#{2}-#{1}-'));
+ this.assertEqual(' z',
+ source.gsub(/(.)(o+)/, '#{3}'));
+ },
+
+ testSubWithReplacementFunction: function() {
+ var source = 'foo boo boz';
+
+ this.assertEqual('Foo boo boz',
+ source.sub(/[^o]+/, function(match) {
+ return match[0].toUpperCase()
+ }), 1);
+ this.assertEqual('Foo Boo boz',
+ source.sub(/[^o]+/, function(match) {
+ return match[0].toUpperCase()
+ }, 2), 2);
+ this.assertEqual(source,
+ source.sub(/[^o]+/, function(match) {
+ return match[0].toUpperCase()
+ }, 0), 0);
+ this.assertEqual(source,
+ source.sub(/[^o]+/, function(match) {
+ return match[0].toUpperCase()
+ }, -1), -1);
+ },
+
+ testSubWithReplacementString: function() {
+ var source = 'foo boo boz';
+
+ this.assertEqual('oo boo boz',
+ source.sub(/[^o]+/, ''));
+ this.assertEqual('oooo boz',
+ source.sub(/[^o]+/, '', 2));
+ this.assertEqual('-f-oo boo boz',
+ source.sub(/[^o]+/, '-#{0}-'));
+ this.assertEqual('-f-oo- b-oo boz',
+ source.sub(/[^o]+/, '-#{0}-', 2));
+ },
+
+ testScan: function() {
+ var source = 'foo boo boz', results = [];
+ var str = source.scan(/[o]+/, function(match) {
+ results.push(match[0].length);
+ });
+ this.assertEnumEqual([2, 2, 1], results);
+ this.assertEqual(source, source.scan(/x/, this.fail));
+ this.assert(typeof str == 'string');
+ },
+
+ testToArray: function() {
+ this.assertEnumEqual([],''.toArray());
+ this.assertEnumEqual(['a'],'a'.toArray());
+ this.assertEnumEqual(['a','b'],'ab'.toArray());
+ this.assertEnumEqual(['f','o','o'],'foo'.toArray());
+ },
+
+ /*
+ Note that camelize() differs from its Rails counterpart,
+ as it is optimized for dealing with JavaScript object
+ properties in conjunction with CSS property names:
+ - Looks for dashes, not underscores
+ - CamelCases first word if there is a front dash
+ */
+ testCamelize: function() {
+ this.assertEqual('', ''.camelize());
+ this.assertEqual('', '-'.camelize());
+ this.assertEqual('foo', 'foo'.camelize());
+ this.assertEqual('foo_bar', 'foo_bar'.camelize());
+ this.assertEqual('FooBar', '-foo-bar'.camelize());
+ this.assertEqual('FooBar', 'FooBar'.camelize());
+
+ this.assertEqual('fooBar', 'foo-bar'.camelize());
+ this.assertEqual('borderBottomWidth', 'border-bottom-width'.camelize());
+
+ this.assertEqual('classNameTest','class-name-test'.camelize());
+ this.assertEqual('classNameTest','className-test'.camelize());
+ this.assertEqual('classNameTest','class-nameTest'.camelize());
+
+ /* this.benchmark(function(){
+ 'class-name-test'.camelize();
+ },10000); */
+ },
+
+ testCapitalize: function() {
+ this.assertEqual('',''.capitalize());
+ this.assertEqual('Ä','ä'.capitalize());
+ this.assertEqual('A','A'.capitalize());
+ this.assertEqual('Hello','hello'.capitalize());
+ this.assertEqual('Hello','HELLO'.capitalize());
+ this.assertEqual('Hello','Hello'.capitalize());
+ this.assertEqual('Hello world','hello WORLD'.capitalize());
+ },
+
+ testUnderscore: function() {
+ this.assertEqual('', ''.underscore());
+ this.assertEqual('_', '-'.underscore());
+ this.assertEqual('foo', 'foo'.underscore());
+ this.assertEqual('foo', 'Foo'.underscore());
+ this.assertEqual('foo_bar', 'foo_bar'.underscore());
+ this.assertEqual('border_bottom', 'borderBottom'.underscore());
+ this.assertEqual('border_bottom_width', 'borderBottomWidth'.underscore());
+ this.assertEqual('border_bottom_width', 'border-Bottom-Width'.underscore());
+ },
+
+ testDasherize: function() {
+ this.assertEqual('', ''.dasherize());
+ this.assertEqual('foo', 'foo'.dasherize());
+ this.assertEqual('Foo', 'Foo'.dasherize());
+ this.assertEqual('foo-bar', 'foo-bar'.dasherize());
+ this.assertEqual('border-bottom-width', 'border_bottom_width'.dasherize());
+ },
+
+ testTruncate: function() {
+ var source = 'foo boo boz foo boo boz foo boo boz foo boo boz';
+ this.assertEqual(source, source.truncate(source.length));
+ this.assertEqual('foo boo boz foo boo boz foo...', source.truncate(0));
+ this.assertEqual('fo...', source.truncate(5));
+ this.assertEqual('foo b', source.truncate(5, ''));
+
+ this.assert(typeof 'foo'.truncate(5) == 'string');
+ this.assert(typeof 'foo bar baz'.truncate(5) == 'string');
+ },
+
+ testStrip: function() {
+ this.assertEqual('hello world', ' hello world '.strip());
+ this.assertEqual('hello world', 'hello world'.strip());
+ this.assertEqual('hello \n world', ' hello \n world '.strip());
+ this.assertEqual('', ' '.strip());
+ },
+
+ testStripTags: function() {
+ this.assertEqual('hello world', 'hello world'.stripTags());
+ this.assertEqual('hello world', 'hello <span>world</span>'.stripTags());
+ this.assertEqual('hello world', '<a href="#" onclick="moo!">hello</a> world'.stripTags());
+ this.assertEqual('hello world', 'h<b><em>e</em></b>l<i>l</i>o w<span class="moo" id="x"><b>o</b></span>rld'.stripTags());
+ this.assertEqual('1\n2', '1\n2'.stripTags());
+ },
+
+ testStripScripts: function() {
+ this.assertEqual('foo bar', 'foo bar'.stripScripts());
+ this.assertEqual('foo bar', ('foo <script>boo();<'+'/script>bar').stripScripts());
+ this.assertEqual('foo bar', ('foo <script type="text/javascript">boo();\nmoo();<'+'/script>bar').stripScripts());
+ },
+
+ testExtractScripts: function() {
+ this.assertEnumEqual([], 'foo bar'.extractScripts());
+ this.assertEnumEqual(['boo();'], ('foo <script>boo();<'+'/script>bar').extractScripts());
+ this.assertEnumEqual(['boo();','boo();\nmoo();'],
+ ('foo <script>boo();<'+'/script><script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
+ this.assertEnumEqual(['boo();','boo();\nmoo();'],
+ ('foo <script>boo();<'+'/script>blub\nblub<script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
+
+ var russianChars = '//кПЌеМтарОй\n';
+ var longComment = '//' + Array(7000).join('.') + '\n';
+ var longScript = '\nvar foo = 1;\n' + russianChars + longComment;
+ var longString = '<script type="text/javascript">'+ longScript + '<'+'/script>';
+ this.assertEnumEqual([longScript], longString.extractScripts());
+
+ this.assertEnumEqual([], ('<!--\n<script>boo();<'+'/script>\n-->').extractScripts());
+ },
+
+ testEvalScripts: function() {
+ this.assertEqual(0, evalScriptsCounter);
+
+ ('foo <script>evalScriptsCounter++<'+'/script>bar').evalScripts();
+ this.assertEqual(1, evalScriptsCounter);
+
+ var stringWithScripts = '';
+ (3).times(function(){ stringWithScripts += 'foo <script>evalScriptsCounter++<'+'/script>bar' });
+ stringWithScripts.evalScripts();
+ this.assertEqual(4, evalScriptsCounter);
+ },
+
+ testEscapeHTML: function() {
+ this.assertEqual('foo bar', 'foo bar'.escapeHTML());
+ this.assertEqual('foo &lt;span&gt;bar&lt;/span&gt;', 'foo <span>bar</span>'.escapeHTML());
+ this.assertEqual('foo ß bar', 'foo ß bar'.escapeHTML());
+
+ this.assertEqual('ウィメンズ2007\nクルーズコレクション',
+ 'ウィメンズ2007\nクルーズコレクション'.escapeHTML());
+
+ this.assertEqual('a&lt;a href=&quot;blah&quot;&gt;blub&lt;/a&gt;b&lt;span&gt;&lt;div&gt;&lt;/div&gt;&lt;/span&gt;cdef&lt;strong&gt;!!!!&lt;/strong&gt;g',
+ 'a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g'.escapeHTML());
+
+ this.assertEqual(largeTextEscaped, largeTextUnescaped.escapeHTML());
+
+ this.assertEqual('1\n2', '1\n2'.escapeHTML());
+
+ this.benchmark(function() { largeTextUnescaped.escapeHTML() }, 1000);
+ },
+
+ testUnescapeHTML: function() {
+ this.assertEqual('foo bar', 'foo bar'.unescapeHTML());
+ this.assertEqual('foo <span>bar</span>', 'foo &lt;span&gt;bar&lt;/span&gt;'.unescapeHTML());
+ this.assertEqual('foo ß bar', 'foo ß bar'.unescapeHTML());
+
+ this.assertEqual('a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g',
+ 'a&lt;a href="blah"&gt;blub&lt;/a&gt;b&lt;span&gt;&lt;div&gt;&lt;/div&gt;&lt;/span&gt;cdef&lt;strong&gt;!!!!&lt;/strong&gt;g'.unescapeHTML());
+
+ this.assertEqual(largeTextUnescaped, largeTextEscaped.unescapeHTML());
+
+ this.assertEqual('test \xfa', 'test &uacute;'.unescapeHTML());
+ this.assertEqual('1\n2', '1\n2'.unescapeHTML());
+ this.assertEqual('Pride & Prejudice', '<h1>Pride &amp; Prejudice</h1>'.unescapeHTML());
+
+ var escapedTest = '"&lt;" means "<" in HTML';
+ this.assertEqual(escapedTest, escapedTest.escapeHTML().unescapeHTML());
+
+ this.benchmark(function() { largeTextEscaped.unescapeHTML() }, 1000);
+
+ },
+
+ testTemplateEvaluation: function() {
+ var source = '<tr><td>#{name}</td><td>#{age}</td></tr>';
+ var person = {name: 'Sam', age: 21};
+ var template = new Template(source);
+
+ this.assertEqual('<tr><td>Sam</td><td>21</td></tr>',
+ template.evaluate(person));
+ this.assertEqual('<tr><td></td><td></td></tr>',
+ template.evaluate({}));
+ },
+
+ testTemplateEvaluationWithEmptyReplacement: function() {
+ var template = new Template('##{}');
+ this.assertEqual('#', template.evaluate({}));
+ this.assertEqual('#', template.evaluate({foo: 'bar'}));
+
+ template = new Template('#{}');
+ this.assertEqual('', template.evaluate({}));
+ },
+
+ testTemplateEvaluationWithFalses: function() {
+ var source = '<tr><td>#{zero}</td><td>#{false_}</td><td>#{undef}</td><td>#{null_}</td><td>#{empty}</td></tr>';
+ var falses = {zero:0, false_:false, undef:undefined, null_:null, empty:""};
+ var template = new Template(source);
+
+ this.assertEqual('<tr><td>0</td><td>false</td><td></td><td></td><td></td></tr>',
+ template.evaluate(falses));
+ },
+
+ testTemplateEvaluationWithNested: function() {
+ var source = '#{name} #{manager.name} #{manager.age} #{manager.undef} #{manager.age.undef} #{colleagues.first.name}';
+ var subject = { manager: { name: 'John', age: 29 }, name: 'Stephan', age: 22, colleagues: { first: { name: 'Mark' }} };
+ this.assertEqual('Stephan', new Template('#{name}').evaluate(subject));
+ this.assertEqual('John', new Template('#{manager.name}').evaluate(subject));
+ this.assertEqual('29', new Template('#{manager.age}').evaluate(subject));
+ this.assertEqual('', new Template('#{manager.undef}').evaluate(subject));
+ this.assertEqual('', new Template('#{manager.age.undef}').evaluate(subject));
+ this.assertEqual('Mark', new Template('#{colleagues.first.name}').evaluate(subject));
+ this.assertEqual('Stephan John 29 Mark', new Template(source).evaluate(subject));
+ },
+
+ testTemplateEvaluationWithIndexing: function() {
+ var source = '#{0} = #{[0]} - #{1} = #{[1]} - #{[2][0]} - #{[2].name} - #{first[0]} - #{[first][0]} - #{[\\]]} - #{first[\\]]}';
+ var subject = [ 'zero', 'one', [ 'two-zero' ] ];
+ subject[2].name = 'two-zero-name';
+ subject.first = subject[2];
+ subject[']'] = '\\';
+ subject.first[']'] = 'first\\';
+ this.assertEqual('zero', new Template('#{[0]}').evaluate(subject));
+ this.assertEqual('one', new Template('#{[1]}').evaluate(subject));
+ this.assertEqual('two-zero', new Template('#{[2][0]}').evaluate(subject));
+ this.assertEqual('two-zero-name', new Template('#{[2].name}').evaluate(subject));
+ this.assertEqual('two-zero', new Template('#{first[0]}').evaluate(subject));
+ this.assertEqual('\\', new Template('#{[\\]]}').evaluate(subject));
+ this.assertEqual('first\\', new Template('#{first[\\]]}').evaluate(subject));
+ this.assertEqual('empty - empty2', new Template('#{[]} - #{m[]}').evaluate({ '': 'empty', m: {'': 'empty2'}}));
+ this.assertEqual('zero = zero - one = one - two-zero - two-zero-name - two-zero - two-zero - \\ - first\\', new Template(source).evaluate(subject));
+ },
+
+ testTemplateToTemplateReplacements: function() {
+ var source = 'My name is #{name}, my job is #{job}';
+ var subject = {
+ name: 'Stephan',
+ getJob: function() { return 'Web developer'; },
+ toTemplateReplacements: function() { return { name: this.name, job: this.getJob() } }
+ };
+ this.assertEqual('My name is Stephan, my job is Web developer', new Template(source).evaluate(subject));
+ },
+
+ testTemplateEvaluationCombined: function() {
+ var source = '#{name} is #{age} years old, managed by #{manager.name}, #{manager.age}.\n' +
+ 'Colleagues include #{colleagues[0].name} and #{colleagues[1].name}.';
+ var subject = {
+ name: 'Stephan', age: 22,
+ manager: { name: 'John', age: 29 },
+ colleagues: [ { name: 'Mark' }, { name: 'Indy' } ]
+ };
+ this.assertEqual('Stephan is 22 years old, managed by John, 29.\n' +
+ 'Colleagues include Mark and Indy.',
+ new Template(source).evaluate(subject));
+ },
+
+ testInterpolate: function() {
+ var subject = { name: 'Stephan' };
+ var pattern = /(^|.|\r|\n)(#\((.*?)\))/;
+ this.assertEqual('#{name}: Stephan', '\\#{name}: #{name}'.interpolate(subject));
+ this.assertEqual('#(name): Stephan', '\\#(name): #(name)'.interpolate(subject, pattern));
+ },
+
+ testToQueryParams: function() {
+ // only the query part
+ var result = {a:undefined, b:'c'};
+ this.assertHashEqual({}, ''.toQueryParams(), 'empty query');
+ this.assertHashEqual({}, 'foo?'.toQueryParams(), 'empty query with URL');
+ this.assertHashEqual(result, 'foo?a&b=c'.toQueryParams(), 'query with URL');
+ this.assertHashEqual(result, 'foo?a&b=c#fragment'.toQueryParams(), 'query with URL and fragment');
+ this.assertHashEqual(result, 'a;b=c'.toQueryParams(';'), 'custom delimiter');
+
+ this.assertHashEqual({a:undefined}, 'a'.toQueryParams(), 'key without value');
+ this.assertHashEqual({a:'b'}, 'a=b&=c'.toQueryParams(), 'empty key');
+ this.assertHashEqual({a:'b', c:''}, 'a=b&c='.toQueryParams(), 'empty value');
+
+ this.assertHashEqual({'a b':'c', d:'e f', g:'h'},
+ 'a%20b=c&d=e%20f&g=h'.toQueryParams(), 'proper decoding');
+ this.assertHashEqual({a:'b=c=d'}, 'a=b=c=d'.toQueryParams(), 'multiple equal signs');
+ this.assertHashEqual({a:'b', c:'d'}, '&a=b&&&c=d'.toQueryParams(), 'proper splitting');
+
+ this.assertEnumEqual($w('r g b'), 'col=r&col=g&col=b'.toQueryParams()['col'],
+ 'collection without square brackets');
+ var msg = 'empty values inside collection';
+ this.assertEnumEqual(['r', '', 'b'], 'c=r&c=&c=b'.toQueryParams()['c'], msg);
+ this.assertEnumEqual(['', 'blue'], 'c=&c=blue'.toQueryParams()['c'], msg);
+ this.assertEnumEqual(['blue', ''], 'c=blue&c='.toQueryParams()['c'], msg);
+ },
+
+ testInspect: function() {
+ this.assertEqual('\'\'', ''.inspect());
+ this.assertEqual('\'test\'', 'test'.inspect());
+ this.assertEqual('\'test \\\'test\\\' "test"\'', 'test \'test\' "test"'.inspect());
+ this.assertEqual('\"test \'test\' \\"test\\"\"', 'test \'test\' "test"'.inspect(true));
+ this.assertEqual('\'\\b\\t\\n\\f\\r"\\\\\'', '\b\t\n\f\r"\\'.inspect());
+ this.assertEqual('\"\\b\\t\\n\\f\\r\\"\\\\\"', '\b\t\n\f\r"\\'.inspect(true));
+ this.assertEqual('\'\\b\\t\\n\\f\\r\'', '\x08\x09\x0a\x0c\x0d'.inspect());
+ this.assertEqual('\'\\u001a\'', '\x1a'.inspect());
+ },
+
+ testInclude: function() {
+ this.assert('hello world'.include('h'));
+ this.assert('hello world'.include('hello'));
+ this.assert('hello world'.include('llo w'));
+ this.assert('hello world'.include('world'));
+ this.assert(!'hello world'.include('bye'));
+ this.assert(!''.include('bye'));
+ },
+
+ testStartsWith: function() {
+ this.assert('hello world'.startsWith('h'));
+ this.assert('hello world'.startsWith('hello'));
+ this.assert(!'hello world'.startsWith('bye'));
+ this.assert(!''.startsWith('bye'));
+ this.assert(!'hell'.startsWith('hello'));
+ },
+
+ testEndsWith: function() {
+ this.assert('hello world'.endsWith('d'));
+ this.assert('hello world'.endsWith(' world'));
+ this.assert(!'hello world'.endsWith('planet'));
+ this.assert(!''.endsWith('planet'));
+ this.assert('hello world world'.endsWith(' world'));
+ this.assert(!'z'.endsWith('az'));
+ },
+
+ testBlank: function() {
+ this.assert(''.blank());
+ this.assert(' '.blank());
+ this.assert('\t\r\n '.blank());
+ this.assert(!'a'.blank());
+ this.assert(!'\t y \n'.blank());
+ },
+
+ testEmpty: function() {
+ this.assert(''.empty());
+ this.assert(!' '.empty());
+ this.assert(!'\t\r\n '.empty());
+ this.assert(!'a'.empty());
+ this.assert(!'\t y \n'.empty());
+ },
+
+ testSucc: function() {
+ this.assertEqual('b', 'a'.succ());
+ this.assertEqual('B', 'A'.succ());
+ this.assertEqual('1', '0'.succ());
+ this.assertEqual('abce', 'abcd'.succ());
+ this.assertEqual('{', 'z'.succ());
+ this.assertEqual(':', '9'.succ());
+ },
+
+ testTimes: function() {
+
+ this.assertEqual('', ''.times(0));
+ this.assertEqual('', ''.times(5));
+ this.assertEqual('', 'a'.times(-1));
+ this.assertEqual('', 'a'.times(0));
+ this.assertEqual('a', 'a'.times(1));
+ this.assertEqual('aa', 'a'.times(2));
+ this.assertEqual('aaaaa', 'a'.times(5));
+ this.assertEqual('foofoofoofoofoo', 'foo'.times(5));
+ this.assertEqual('', 'foo'.times(-5));
+
+ /*window.String.prototype.oldTimes = function(count) {
+ var result = '';
+ for (var i = 0; i < count; i++) result += this;
+ return result;
+ };
+
+ this.benchmark(function() {
+ 'foo'.times(15);
+ }, 1000, 'new: ');
+
+ this.benchmark(function() {
+ 'foo'.oldTimes(15);
+ }, 1000, 'previous: ');*/
+ },
+
+ testToJSON: function() {
+ this.assertEqual('\"\"', ''.toJSON());
+ this.assertEqual('\"test\"', 'test'.toJSON());
+ },
+
+ testIsJSON: function() {
+ this.assert(!''.isJSON());
+ this.assert(!' '.isJSON());
+ this.assert('""'.isJSON());
+ this.assert('"foo"'.isJSON());
+ this.assert('{}'.isJSON());
+ this.assert('[]'.isJSON());
+ this.assert('null'.isJSON());
+ this.assert('123'.isJSON());
+ this.assert('true'.isJSON());
+ this.assert('false'.isJSON());
+ this.assert('"\\""'.isJSON());
+ this.assert(!'\\"'.isJSON());
+ this.assert(!'new'.isJSON());
+ this.assert(!'\u0028\u0029'.isJSON());
+ // we use '@' as a placeholder for characters authorized only inside brackets,
+ // so this tests make sure it is not considered authorized elsewhere.
+ this.assert(!'@'.isJSON());
+ },
+
+ testEvalJSON: function() {
+ var valid = '{"test": \n\r"hello world!"}';
+ var invalid = '{"test": "hello world!"';
+ var dangerous = '{});attackTarget = "attack succeeded!";({}';
+
+ // use smaller huge string size for KHTML
+ var size = navigator.userAgent.include('KHTML') ? 20 : 100;
+ var longString = '"' + '123456789\\"'.times(size * 10) + '"';
+ var object = '{' + longString + ': ' + longString + '},';
+ var huge = '[' + object.times(size) + '{"test": 123}]';
+
+ this.assertEqual('hello world!', valid.evalJSON().test);
+ this.assertEqual('hello world!', valid.evalJSON(true).test);
+ this.assertRaise('SyntaxError', function() { invalid.evalJSON() });
+ this.assertRaise('SyntaxError', function() { invalid.evalJSON(true) });
+
+ attackTarget = "scared";
+ dangerous.evalJSON();
+ this.assertEqual("attack succeeded!", attackTarget);
+
+ attackTarget = "Not scared!";
+ this.assertRaise('SyntaxError', function(){dangerous.evalJSON(true)});
+ this.assertEqual("Not scared!", attackTarget);
+
+ this.assertEqual('hello world!', ('/*-secure- \r \n ' + valid + ' \n */').evalJSON().test);
+ var temp = Prototype.JSONFilter;
+ Prototype.JSONFilter = /^\/\*([\s\S]*)\*\/$/; // test custom delimiters.
+ this.assertEqual('hello world!', ('/*' + valid + '*/').evalJSON().test);
+ Prototype.JSONFilter = temp;
+
+ this.assertMatch(123, huge.evalJSON(true).last().test);
+
+ this.assertEqual('', '""'.evalJSON());
+ this.assertEqual('foo', '"foo"'.evalJSON());
+ this.assert('object', typeof '{}'.evalJSON());
+ this.assert(Object.isArray('[]'.evalJSON()));
+ this.assertNull('null'.evalJSON());
+ this.assert(123, '123'.evalJSON());
+ this.assertIdentical(true, 'true'.evalJSON());
+ this.assertIdentical(false, 'false'.evalJSON());
+ this.assertEqual('"', '"\\""'.evalJSON());
+ }
+}); \ No newline at end of file
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/ajax_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/ajax_test.html
new file mode 100644
index 0000000000..b38fd306ca
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/ajax_test.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Ajax</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/ajax.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../ajax_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Ajax</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="content"></div>
+<div id="content2" style="color:red"></div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/array_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/array_test.html
new file mode 100644
index 0000000000..cc3b9ef629
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/array_test.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Array</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../array_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Array</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="test_node">22<span id="span_1"></span><span id="span_2"></span></div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/base_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/base_test.html
new file mode 100644
index 0000000000..bcd29f91df
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/base_test.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Base</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/base.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../base_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Base</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="test"></div>
+<ul id="list">
+ <li></li>
+ <li></li>
+ <li></li>
+</ul>
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/dom_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/dom_test.html
new file mode 100644
index 0000000000..c1ffb7536f
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/dom_test.html
@@ -0,0 +1,326 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Dom</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+ <link rel="stylesheet" href="../fixtures/dom.css" type="text/css" charset="utf-8" />
+
+
+
+ <script src="../fixtures/dom.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../dom_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Dom</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="scroll_test_1">
+ <p id="scroll_test_2">Scroll test</p>
+</div>
+
+<div id="test-visible">visible</div>
+<div id="test-hidden" style="display:none;">hidden</div>
+<div id="test-toggle-visible">visible</div>
+<div id="test-toggle-hidden" style="display:none;">hidden</div>
+<div id="test-hide-visible">visible</div>
+<div id="test-hide-hidden" style="display:none;">hidden</div>
+<div id="test-show-visible">visible</div>
+<div id="test-show-hidden" style="display:none;">hidden</div>
+<div id="removable-container"><div id="removable"></div></div>
+
+<div>
+ <table>
+ <tbody id="table">
+ <tr>
+ <td>Data</td>
+ </tr>
+ <tr>
+ <td id="a_cell">First Row</td>
+ </tr>
+ <tr id="second_row">
+ <td>Second Row</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<div id="table-container-to-replace">
+ <table>
+ <tbody id="table-to-replace">
+ <tr>
+ <td>Data</td>
+ </tr>
+ <tr>
+ <td id="a_cell-to-replace">First Row</td>
+ </tr>
+ <tr id="second_row-to-replace">
+ <td>Second Row</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<p class="test">Test paragraph outside of container</p>
+
+<div id="container">
+ <p class="test" id="intended">Test paragraph 1 inside of container</p>
+ <p class="test">Test paragraph 2 inside of container</p>
+ <p class="test">Test paragraph 3 inside of container</p>
+ <p class="test">Test paragraph 4 inside of container</p>
+</div>
+
+<div id="testdiv">to be updated</div>
+<div id="testdiv-replace-container-1"><div id="testdiv-replace-1"></div></div>
+<div id="testdiv-replace-container-2"><div id="testdiv-replace-2"></div></div>
+<div id="testdiv-replace-container-3"><div id="testdiv-replace-3"></div></div>
+<div id="testdiv-replace-container-4"><div id="testdiv-replace-4"></div></div>
+<div id="testdiv-replace-container-5"><div id="testdiv-replace-5"></div></div>
+<div id="testdiv-replace-container-element"><div id="testdiv-replace-element"></div></div>
+<div id="testdiv-replace-container-toelement"><div id="testdiv-replace-toelement"></div></div>
+<div id="testdiv-replace-container-tohtml"><div id="testdiv-replace-tohtml"></div></div>
+<div id="testtable-replace-container"><table id="testtable-replace"></table></div>
+<table id="testrow-replace-container"><tr id="testrow-replace"></tr></table>
+<select id="testoption-replace-container"><option id="testoption-replace"></option><option>stays</option></select>
+<div id="testform-replace-container"><p>some text</p><form id="testform-replace"><input id="testinput-replace" type="text" /></form><p>some text</p></div>
+
+<div id="element_with_visible_overflow" style="overflow:visible">V</div>
+<div id="element_with_hidden_overflow" style="overflow:hidden">H</div>
+<div id="element_with_scroll_overflow" style="overflow:scroll">S</div>
+
+<div id="element_extend_test"> </div>
+
+<div id="element_reextend_test"><div id="discard_1"></div></div>
+
+<div id="test_whitespace"> <span> </span>
+
+
+<div><div></div> </div><span> </span>
+</div>
+
+
+<div id="nav_tests_isolator">
+ <div id="nav_test_first_sibling"></div>
+ <div></div>
+ <p id="nav_test_p" class="test"></p>
+ <span id="nav_test_prev_sibling"></span>
+
+ <ul id="navigation_test" style="display: none">
+ <!-- comment node to screw things up -->
+ <li class="first"><em>A</em></li>
+ <li><em class="dim">B</em></li>
+ <li id="navigation_test_c">
+ <em>C</em>
+ <ul>
+ <li><em class="dim">E</em></li>
+ <li id="navigation_test_f"><em>F</em></li>
+ </ul>
+ </li>
+ <li class="last"><em>D</em></li>
+ </ul>
+
+ <div id="navigation_test_next_sibling">
+ <!-- -->
+ </div>
+
+ <p></p>
+</div>
+
+<div id="class_names">
+ <p class="A"></p>
+ <ul class="A B" id="class_names_ul">
+ <li class="C"></li>
+ <li class="A C"></li>
+ <li class="1"></li>
+ </ul>
+ <div class="B C D"></div>
+ <div id="unextended"></div>
+</div>
+
+<div id="style_test_1" style="display:none;"></div>
+<div id="style_test_2" class="style-test" style="font-size:11px;"></div>
+
+<div id="style_test_3">blah</div>
+<span id="style_test_4">blah</span>
+<span id="style_test_5">blah</span>
+
+<div id="style_test_dimensions_container">
+ <div id="style_test_dimensions" style="background:#ddd;padding:1px;margin:1px;border:1px solid #00f"><div style="height:5px;background:#eee;width:5px;padding:2px;margin:2px;border:2px solid #0f0"> </div>
+ </div>
+</div>
+
+<div id="test_csstext_1">test_csstext_1</div>
+<div id="test_csstext_2">test_csstext_2</div>
+<div id="test_csstext_3" style="border: 1px solid red">test_csstext_3</div>
+<div id="test_csstext_4" style="font-size: 20px">test_csstext_4</div>
+<div id="test_csstext_5">test_csstext_5</div>
+
+<div id="custom_attributes">
+ <div foo="1" bar="2"></div>
+ <div foo="2"></div>
+</div>
+
+<div id="cloned_element_attributes_issue" foo="original"></div>
+<a id="attributes_with_issues_1" href="test.html" accesskey="L" tabindex="50" title="a link" onclick="alert('hello world');"></a>
+<a id="attributes_with_issues_2" href="" accesskey="" tabindex="" title=""></a>
+<a id="attributes_with_issues_3"></a>
+<form id="attributes_with_issues_form" method="post" action="blah" class="blah-class">
+ <input type="hidden" id="id" />
+ <input type="hidden" name="id" />
+ <input type="checkbox" id="action" />
+ <input type="checkbox" name="action" />
+ <input type="text" id="method" />
+ <input type="hidden" name="class" />
+ <input type="checkbox" id="attributes_with_issues_checked" name="a" checked="checked"/>
+ <input type="checkbox" id="attributes_with_issues_disabled" name="b" checked="checked" disabled="disabled"/>
+ <input type="text" id="attributes_with_issues_readonly" name="c" readonly="readonly" value="blech"/>
+ <input type="text" id="attributes_with_issues_regular" name="d" value="0"/>
+ <input type="date" id="attributes_with_issues_type" value="blech" />
+ <select id="attributes_with_issues_multiple" name="e" multiple="multiple">
+ <option>blech</option>
+ <option>blah</option>
+ </select>
+</form>
+
+<!-- writeAttributes -->
+<p id="write_attribute_para"></p>
+<a id="write_attribute_link" href="test.html"></a>
+<form action="/dev/null" id="write_attribute_form" method="get" accept-charset="utf-8">
+ <label id="write_attribute_label"></label>
+ <input type="checkbox" name="write_attribute_checkbox" value="" id="write_attribute_checkbox">
+ <input type="checkbox" checked="checked" name="write_attribute_checked_checkbox" value="" id="write_attribute_checked_checkbox">
+ <input type="text" name="write_attribute_input" value="" id="write_attribute_input">
+ <select id="write_attribute_select">
+ <option>Cat</option>
+ <option>Dog</option>
+ </select>
+</form>
+
+<table id="write_attribute_table" cellpadding="6" cellspacing="4">
+ <tr><td id="write_attribute_td">A</td><td>B</td></tr>
+ <tr><td>C</td></tr>
+ <tr><td>D</td><td>E</td><td>F</td></tr>
+</table>
+
+<div id="dom_attribute_precedence">
+ <form action="blech" method="post">
+ <input type="submit" id="update" />
+ </form>
+</div>
+
+<div id="not_floating_none">NFN</div>
+<div id="not_floating_inline" style="float:none">NFI</div>
+<div id="not_floating_style">NFS</div>
+
+<div id="floating_inline" style="float:left">FI</div>
+<div id="floating_style">FS</div>
+<!-- Test Element opacity functions -->
+<img id="op1" alt="op2" src="fixtures/logo.gif" style="opacity:0.5;filter:alpha(opacity=50)" />
+<img id="op2" alt="op2" src="fixtures/logo.gif"/>
+<img id="op3" alt="op3" src="fixtures/logo.gif"/>
+<img id="op4-ie" alt="op3" src="fixtures/logo.gif" style="filter:alpha(opacity=30)" />
+<div id="dimensions-visible"></div>
+<div id="dimensions-display-none"></div>
+<div id="dimensions-visible-pos-rel"></div>
+<div id="dimensions-display-none-pos-rel"></div>
+<div id="dimensions-visible-pos-abs"></div>
+<div id="dimensions-display-none-pos-abs"></div>
+<table border="0" cellspacing="0" cellpadding="0" id="dimensions-table">
+ <tbody id="dimensions-tbody">
+ <tr id="dimensions-tr">
+ <td id="dimensions-td">Data</td>
+ </tr>
+ </tbody>
+</table>
+
+<div id="dimensions-nester" style="width: 500px;">
+ <div id="dimensions-nestee" style="display: none">This is a nested DIV. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
+</div>
+
+
+<p id="test-empty"></p>
+<p id="test-empty-but-contains-whitespace">
+
+
+</p>
+<p id="test-full">content</p>
+<div id="ancestor">
+ <div id="child">
+ <div id="grand-child">
+ <div id="great-grand-child"></div>
+ </div></div><!-- intentional formatting; don't change this line -->
+ <div id="sibling"><div id="grand-sibling"></div></div>
+</div>
+<div id="not-in-the-family"></div>
+
+<div id="insertions-container"><div id="insertions-main"><p>some content.</p></div></div>
+<div id="insertions-node-container"><div id="insertions-node-main"><p>some content.</p></div></div>
+<div id="element-insertions-container"><div id="element-insertions-main"><p>some content.</p></div></div>
+<div id="element-insertions-multiple-container"><div id="element-insertions-multiple-main"><p>some content.</p></div></div>
+<table id="table_for_insertions"></table>
+<table id="table_for_row_insertions"><tr id="row_1"></tr></table>
+<form method="post" action="blah">
+ <select id="select_for_update" name="select_for_update">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+ <select id="select_for_insert_bottom" name="select_for_insert_bottom">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+ <select id="select_for_insert_top" name="select_for_insert_top">
+ <option>option 1</option>
+ <option>option 2</option>
+ </select>
+</form>
+<div id="wrap-container"><p id="wrap"></p></div>
+
+<!-- Positioning methods bench -->
+<div id="body_absolute" style="position: absolute; top: 10px; left: 10px">
+ <div id="absolute_absolute" style="position: absolute; top: 10px; left:10px"> </div>
+ <div id="absolute_relative" style="position: relative; top: 10px; left:10px">
+ <div style="height:10px;font-size:2px">test<span id="inline">test</span></div>
+ <div id="absolute_relative_undefined">XYZ</div>
+ </div>
+ <div id="absolute_fixed" style="position: fixed; top: 10px; left: 10px">
+ <span id="absolute_fixed_absolute" style="position: absolute; top: 10px; left: 10px">foo</span>
+ <span id="absolute_fixed_undefined" style="display:block">bar</span>
+ </div></div>
+<div id="notInlineAbsoluted"></div>
+<div id="inlineAbsoluted" style="position: absolute"></div>
+
+<div id="unextended"></div>
+<div id="identification">
+ <div id="predefined_id"></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div id="anonymous_element_3"></div>
+</div>
+
+<div id='elementToViewportDimensions' style='display: none'></div>
+<div id="auto_dimensions" style="height:auto"></div>
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/element_mixins_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/element_mixins_test.html
new file mode 100644
index 0000000000..51a789e79b
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/element_mixins_test.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Element mixins</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/element_mixins.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../element_mixins_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Element mixins</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<form id="form">
+ <input type="text" id="input" value="4" />
+ <input type="submit" />
+</form>
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/enumerable_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/enumerable_test.html
new file mode 100644
index 0000000000..8f462b6395
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/enumerable_test.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Enumerable</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/enumerable.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../enumerable_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Enumerable</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<table id="grepTable">
+<tbody id="grepTBody">
+ <tr id="grepRow">
+ <th id="grepHeader" class="cell"></th>
+ <td id="grepCell" class="cell"></td>
+ </tr>
+</tbody>
+</table>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/event_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/event_test.html
new file mode 100644
index 0000000000..12accf239f
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/event_test.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Event</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../event_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Event</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="outer" style="display: none">
+ <p id="inner">One two three <span id="span">four</span></p>
+</div>
+<div id="container"><div></div></div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/form_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/form_test.html
new file mode 100644
index 0000000000..14349eb511
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/form_test.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Form</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../form_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Form</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<form id="form" method="get" action="fixtures/empty.js">
+ <input type="text" name="val1" id="input_enabled" value="4" />
+ <div>This is not a form element</div>
+ <input type="text" name="val2" id="input_disabled" disabled="disabled" value="5" />
+ <input type="submit" name="first_submit" value="Commit it!" />
+ <input type="submit" name="second_submit" value="Delete it!" />
+ <input type="text" name="action" value="blah" />
+</form>
+
+<form id="bigform" method="get" action="fixtures/empty.js">
+ <div id="inputs">
+ <input type="text" name="dummy" id="dummy_disabled" disabled="disabled"/>
+ <input type="submit" name="commit" id="submit" />
+ <input type="button" name="clicky" value="click me" />
+ <input type="reset" name="revert" />
+ <input type="text" name="greeting" id="focus_text" value="Hello" />
+ </div>
+
+ <div id="buttons">
+ <button type="button" id="button_type_button" name="button">Click Me</button>
+ </div>div>
+
+ <!-- some edge cases in serialization -->
+ <div id="value_checks">
+ <input name="twin" type="text" value="" />
+ <input name="twin" type="text" value="siamese" />
+ <!-- Rails checkbox hack with hidden input: -->
+ <input name="checky" type="checkbox" id="checkbox_hack" value="1" />
+ <input name="checky" type="hidden" value="0" />
+ </div>
+
+ <!-- all variations of SELECT controls -->
+ <div id="selects_wrapper">
+ <select name="vu">
+ <option value="1" selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <select id="multiSel1" name="vm[]" multiple="multiple">
+ <option id="multiSel1_opt1" value="1" selected="selected">One</option>
+ <option id="multiSel1_opt2" value="2">Two</option>
+ <option id="multiSel1_opt3" value="3" selected="selected">Three</option>
+ </select>
+ <select name="nvu">
+ <option selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+
+ <fieldset id="selects_fieldset">
+ <select name="nvm[]" multiple="multiple">
+ <option selected="selected">One</option>
+ <option>Two</option>
+ <option selected="selected">Three</option>
+ </select>
+ <select name="evu">
+ <option value="" selected="selected">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <select name="evm[]" multiple="multiple">
+ <option value="" selected="selected">One</option>
+ <option>Two</option>
+ <option selected="selected">Three</option>
+ </select>
+ </fieldset>
+ </div>
+
+ <div id="various">
+ <select name="tf_selectOne"><option selected="selected"></option><option>1</option></select>
+ <textarea name="tf_textarea"></textarea>
+ <input type="checkbox" name="tf_checkbox" value="on" />
+ <select name="tf_selectMany" multiple="multiple"></select>
+ <input type="text" name="tf_text" />
+ <div>This is not a form element</div>
+ <input type="radio" name="tf_radio" value="on" />
+ <input type="hidden" name="tf_hidden" />
+ <input type="password" name="tf_password" />
+ </div>
+</form>
+
+<form id="form_focus_hidden" style="display: none">
+ <input type="text" />
+</form>
+
+<form id="form_with_file_input">
+ <input type="file" name="file_name" value="foo" />
+</form>
+
+<!-- tabindexed forms -->
+<div id="tabindex">
+ <form id="ffe">
+ <p><input type="text" disabled="disabled" id="ffe_disabled" /></p>
+ <input type="hidden" id="ffe_hidden" />
+ <input type="checkbox" id="ffe_checkbox" />
+ </form>
+
+ <form id="ffe_ti">
+ <p><input type="text" disabled="disabled" id="ffe_ti_disabled" /></p>
+ <input type="hidden" id="ffe_ti_hidden" />
+ <input type="checkbox" id="ffe_ti_checkbox" />
+ <input type="submit" id="ffe_ti_submit" tabindex="1" />
+ </form>
+
+ <form id="ffe_ti2">
+ <p><input type="text" disabled="disabled" id="ffe_ti2_disabled" /></p>
+ <input type="hidden" id="ffe_ti2_hidden" />
+ <input type="checkbox" id="ffe_ti2_checkbox" tabindex="0" />
+ <input type="submit" id="ffe_ti2_submit" tabindex="1" />
+ </form>
+
+</div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/hash_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/hash_test.html
new file mode 100644
index 0000000000..4e4f0be75d
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/hash_test.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Hash</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/hash.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../hash_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Hash</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/number_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/number_test.html
new file mode 100644
index 0000000000..85eed3191b
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/number_test.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Number</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../number_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Number</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/position_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/position_test.html
new file mode 100644
index 0000000000..78ed551d0e
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/position_test.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Position</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../position_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Position</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="ensure_scrollbars" style="width:10000px; height:10000px; position:absolute" > </div>
+
+<div id="body_absolute" style="position: absolute; top: 10px; left: 10px">
+ <div id="absolute_absolute" style="position: absolute; top: 10px; left:10px"> </div>
+ <div id="absolute_relative" style="position: relative; top: 10px; left:10px">
+ <div style="height:10px;font-size:2px">test<span id="inline">test</span></div>
+ <div id="absolute_relative_undefined"> </div>
+ </div>
+</div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/range_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/range_test.html
new file mode 100644
index 0000000000..cbc6f828b6
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/range_test.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Range</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../range_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Range</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/selector_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/selector_test.html
new file mode 100644
index 0000000000..84bde55e50
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/selector_test.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Selector</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../selector_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Selector</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="fixtures" style="display: none">
+ <h1 class="title">Some title <span>here</span></h1>
+ <p id="p" class="first summary">
+ <strong id="strong">This</strong> is a short blurb
+ <a id="link_1" class="first internal" rel="external nofollow" href="#">with a <em id="em2">link</em></a> or
+ <a id="link_2" class="internal highlight" href="#"><em id="em">two</em></a>.
+ Or <cite id="with_title" title="hello world!">a citation</cite>.
+ </p>
+ <ul id="list">
+ <li id="item_1" class="first"><a id="link_3" href="#" class="external"><span id="span">Another link</span></a></li>
+ <li id="item_2">Some text</li>
+ <li id="item_3" xml:lang="es-us" class="">Otra cosa</li>
+ </ul>
+
+ <!-- this form has a field with the name 'id',
+ therefore its ID property won't be 'troubleForm': -->
+ <form id="troubleForm">
+ <input type="hidden" name="id" id="hidden" />
+ <input type="text" name="disabled_text_field" id="disabled_text_field" disabled="disabled" />
+ <input type="text" name="enabled_text_field" id="enabled_text_field" />
+ <input type="checkbox" name="checkboxes" id="checked_box" checked="checked" value="Checked" />
+ <input type="checkbox" name="checkboxes" id="unchecked_box" value="Unchecked"/>
+ <input type="radio" name="radiobuttons" id="checked_radio" checked="checked" value="Checked" />
+ <input type="radio" name="radiobuttons" id="unchecked_radio" value="Unchecked" />
+ </form>
+
+ <form id="troubleForm2">
+ <input type="checkbox" name="brackets[5][]" id="chk_1" checked="checked" value="1" />
+ <input type="checkbox" name="brackets[5][]" id="chk_2" value="2" />
+ </form>
+
+ <div id="level1">
+ <span id="level2_1">
+ <span id="level3_1"></span>
+ <!-- This comment should be ignored by the adjacent selector -->
+ <span id="level3_2"></span>
+ </span>
+ <span id="level2_2">
+ <em id="level_only_child">
+ </em>
+ </span>
+ <div id="level2_3"></div>
+ </div> <!-- #level1 -->
+
+ <div id="dupContainer">
+ <span id="dupL1" class="span_foo span_bar">
+ <span id="dupL2">
+ <span id="dupL3">
+ <span id="dupL4">
+ <span id="dupL5"></span>
+ </span>
+ </span>
+ </span>
+ </span>
+ </div> <!-- #dupContainer -->
+
+ <div id="grandfather"> grandfather
+ <div id="father" class="brothers men"> father
+ <div id="son"> son </div>
+ </div>
+ <div id="uncle" class="brothers men"> uncle </div>
+ </div>
+
+ <form id="commaParent" title="commas,are,good">
+ <input type="hidden" id="commaChild" name="foo" value="#commaOne,#commaTwo" />
+ <input type="hidden" id="commaTwo" name="foo2" value="oops" />
+ </form>
+ <div id="counted_container"><div class="is_counted"></div></div>
+</div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/string_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/string_test.html
new file mode 100644
index 0000000000..74564fc0ce
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/string_test.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | String</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+ <script src="../fixtures/string.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="../string_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>String</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/tmp/unit_test.html b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/unit_test.html
new file mode 100644
index 0000000000..b684225ba5
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/tmp/unit_test.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Prototype Unit test file | Unittest</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ var eventResults = {};
+ var originalElement = window.Element;
+ </script>
+ <script src="../../../dist/prototype.js" type="text/javascript"></script>
+ <script src="../../lib/unittest.js" type="text/javascript"></script>
+ <link rel="stylesheet" href="../../test.css" type="text/css" />
+
+
+
+
+
+ <script src="../unittest_test.js" type="text/javascript"></script>
+</head>
+<body>
+<h1>Prototype Unit test file</h1>
+<h2>Unittest</h2>
+
+<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify -->
+
+<!-- Log output start -->
+<div id="testlog"></div>
+<!-- Log output end -->
+
+<!-- HTML Fixtures start -->
+<div id="testlog_2"> </div>
+
+<!-- Test elements follow -->
+<div id="test_1" class="a bbbbbbbbbbbb cccccccccc dddd"> </div>
+
+<div id="test_2"> <span> </span>
+
+
+
+<div><div></div> </div><span> </span>
+</div>
+
+<ul id="tlist"><li id="tlist_1">x1</li><li id="tlist_2">x2</li></ul>
+<ul id="tlist2"><li class="a" id="tlist2_1">x1</li><li id="tlist2_2">x2</li></ul>
+
+<div id="testmoveby" style="background-color:#333;width:100px;">XXXX</div>
+
+<div id="testcss1">testcss1<span id="testcss1_span" style="display:none;">blah</span></div><div id="testcss2">testcss1</div>
+
+<!-- HTML Fixtures end -->
+</body>
+</html>
+<script type="text/javascript" charset="utf-8">
+ eventResults.endOfDocument = true;
+</script>
diff --git a/dom/tests/mochitest/ajax/prototype/test/unit/unittest_test.js b/dom/tests/mochitest/ajax/prototype/test/unit/unittest_test.js
new file mode 100644
index 0000000000..e7afdd60bf
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test/unit/unittest_test.js
@@ -0,0 +1,143 @@
+var testObj = {
+ isNice: function() {
+ return true;
+ },
+ isBroken: function() {
+ return false;
+ }
+}
+
+new Test.Unit.Runner({
+
+ testBuildMessage: function() {
+ this.assertEqual("'foo' 'bar'", this.buildMessage('', '? ?', 'foo', 'bar'))
+ },
+
+ testAssertEqual: function() {
+ this.assertEqual(0, 0);
+ this.assertEqual(0, 0, "test");
+
+ this.assertEqual(0,'0');
+ this.assertEqual(65.0, 65);
+
+ this.assertEqual("a", "a");
+ this.assertEqual("a", "a", "test");
+
+ this.assertNotEqual(0, 1);
+ this.assertNotEqual("a","b");
+ this.assertNotEqual({},{});
+ this.assertNotEqual([],[]);
+ this.assertNotEqual([],{});
+ },
+
+ testAssertEnumEqual: function() {
+ this.assertEnumEqual([], []);
+ this.assertEnumEqual(['a', 'b'], ['a', 'b']);
+ this.assertEnumEqual(['1', '2'], [1, 2]);
+ this.assertEnumNotEqual(['1', '2'], [1, 2, 3]);
+ },
+
+ testAssertHashEqual: function() {
+ this.assertHashEqual({}, {});
+ this.assertHashEqual({a:'b'}, {a:'b'});
+ this.assertHashEqual({a:'b', c:'d'}, {c:'d', a:'b'});
+ this.assertHashNotEqual({a:'b', c:'d'}, {c:'d', a:'boo!'});
+ },
+
+ testAssertRespondsTo: function() {
+ this.assertRespondsTo('isNice', testObj);
+ this.assertRespondsTo('isBroken', testObj);
+ },
+
+ testAssertIdentical: function() {
+ this.assertIdentical(0, 0);
+ this.assertIdentical(0, 0, "test");
+ this.assertIdentical(1, 1);
+ this.assertIdentical('a', 'a');
+ this.assertIdentical('a', 'a', "test");
+ this.assertIdentical('', '');
+ this.assertIdentical(undefined, undefined);
+ this.assertIdentical(null, null);
+ this.assertIdentical(true, true);
+ this.assertIdentical(false, false);
+
+ var obj = {a:'b'};
+ this.assertIdentical(obj, obj);
+
+ this.assertNotIdentical({1:2,3:4},{1:2,3:4});
+
+ this.assertIdentical(1, 1.0); // both are typeof == 'number'
+
+ this.assertNotIdentical(1, '1');
+ this.assertNotIdentical(1, '1.0');
+ },
+
+ testAssertNullAndAssertUndefined: function() {
+ this.assertNull(null);
+ this.assertNotNull(undefined);
+ this.assertNotNull(0);
+ this.assertNotNull('');
+ this.assertNotUndefined(null);
+ this.assertUndefined(undefined);
+ this.assertNotUndefined(0);
+ this.assertNotUndefined('');
+ this.assertNullOrUndefined(null);
+ this.assertNullOrUndefined(undefined);
+ this.assertNotNullOrUndefined(0);
+ this.assertNotNullOrUndefined('');
+ },
+
+ testAssertMatch: function() {
+ this.assertMatch(/knowmad.jpg$/, 'http://script.aculo.us/images/knowmad.jpg');
+ this.assertMatch(/Fuc/, 'Thomas Fuchs');
+ this.assertMatch(/^\$(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/, '$19.95');
+ this.assertMatch(/(\d{3}\) ?)|(\d{3}[- \.])?\d{3}[- \.]\d{4}(\s(x\d+)?){0,1}$/, '704-343-9330');
+ this.assertMatch(/^(?:(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.)(?:0?2\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8]))))$/, '2001-06-16');
+ this.assertMatch(/^((0?[123456789])|(1[012]))\s*:\s*([012345]\d)(\s*:\s*([012345]\d))?\s*[ap]m\s*-\s*((0?[123456789])|(1[012]))\s*:\s*([012345]\d)(\s*:\s*([012345]\d))?\s*[ap]m$/i, '2:00PM-2:15PM');
+ this.assertNoMatch(/zubar/, 'foo bar');
+ },
+
+ testAssertInstanceOf: function() {
+ this.assertInstanceOf(String, new String);
+ this.assertInstanceOf(RegExp, /foo/);
+ this.assertNotInstanceOf(String, {});
+ },
+
+ testAssertVisible: function() {
+ this.assertVisible('testcss1');
+ this.assertNotVisible('testcss1_span');
+ //this.assertNotVisible('testcss2', "Due to a Safari bug, this test fails in Safari.");
+
+ Element.hide('testcss1');
+ this.assertNotVisible('testcss1');
+ this.assertNotVisible('testcss1_span');
+ Element.show('testcss1');
+ this.assertVisible('testcss1');
+ this.assertNotVisible('testcss1_span');
+
+ Element.show('testcss1_span');
+ this.assertVisible('testcss1_span');
+ Element.hide('testcss1');
+ this.assertNotVisible('testcss1_span'); // hidden by parent
+ },
+
+ testAssertElementsMatch: function() {
+ this.assertElementsMatch($$('#tlist'), '#tlist');
+ this.assertElementMatches($('tlist'), '#tlist');
+ }
+});
+
+/* This test was disabled in bug 486256, because we don't support having two
+ * Runners in one file.
+ */
+/*
+new Test.Unit.Runner({
+ testDummy: function() {
+ this.assert(true);
+ },
+
+ testMultipleTestRunner: function() {
+ this.assertEqual('passed', $('testlog_2').down('td', 1).innerHTML);
+ }
+}, {testLog: 'testlog_2'});
+*/
diff --git a/dom/tests/mochitest/ajax/prototype/test_Prototype.html b/dom/tests/mochitest/ajax/prototype/test_Prototype.html
new file mode 100644
index 0000000000..a650949955
--- /dev/null
+++ b/dom/tests/mochitest/ajax/prototype/test_Prototype.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Prototype</title>
+ <script src="/MochiKit/Base.js"></script>
+ <script src="/MochiKit/Async.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="../lib/AJAX_setup.js"></script>
+ <link rel="stylesheet" type="text/css" href="../lib/test.css" />
+</head>
+<body>
+ <iframe width="100%" height="500" id="testframe" src=""></iframe>
+</body>
+</html>
+