(function (window) { var QUnit = { init: function () { config = { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: +new Date, updateRate: 1000, blocking: false, autorun: false, assertions: [], filters: [], queue: [] }; var tests = id("qunit-tests"), banner = id("qunit-banner"), result = id("qunit-testresult"); if (tests) { tests.innerHTML = "" } if (banner) { banner.className = "" } if (result) { result.parentNode.removeChild(result) } }, module: function (name, testEnvironment) { config.currentModule = name; synchronize(function () { if (config.currentModule) { QUnit.moduleDone(config.currentModule, config.moduleStats.bad, config.moduleStats.all) } config.currentModule = name; config.moduleTestEnvironment = testEnvironment; config.moduleStats = { all: 0, bad: 0 }; QUnit.moduleStart(name, testEnvironment) }) }, asyncTest: function (testName, expected, callback) { if (arguments.length === 2) { callback = expected; expected = 0 } QUnit.test(testName, expected, callback, true) }, test: function (testName, expected, callback, async) { var name = testName, testEnvironment, testEnvironmentArg; if (arguments.length === 2) { callback = expected; expected = null } if (expected && typeof expected === 'object') { testEnvironmentArg = expected; expected = null } if (config.currentModule) { name = config.currentModule + " module: " + name } if (!validTest(name)) { return } synchronize(function () { QUnit.testStart(testName); testEnvironment = extend({ setup: function () { }, teardown: function () { } }, config.moduleTestEnvironment); if (testEnvironmentArg) { extend(testEnvironment, testEnvironmentArg) } QUnit.current_testEnvironment = testEnvironment; config.assertions = []; config.expected = expected; try { if (!config.pollution) { saveGlobal() } testEnvironment.setup.call(testEnvironment) } catch (e) { QUnit.ok(false, "Setup failed on " + name + ": " + e.message) } if (async) { QUnit.stop() } try { callback.call(testEnvironment) } catch (e) { fail("Test " + name + " died, exception and test follows", e, callback); QUnit.ok(false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message); saveGlobal(); if (config.blocking) { start() } } }); synchronize(function () { try { checkPollution(); testEnvironment.teardown.call(testEnvironment) } catch (e) { QUnit.ok(false, "Teardown failed on " + name + ": " + e.message) } try { QUnit.reset() } catch (e) { fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset) } if (config.expected && config.expected != config.assertions.length) { QUnit.ok(false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run") } var good = 0, bad = 0, tests = id("qunit-tests"); config.stats.all += config.assertions.length; config.moduleStats.all += config.assertions.length; if (tests) { var ol = document.createElement("ol"); ol.style.display = "none"; for (var i = 0; i < config.assertions.length; i++) { var assertion = config.assertions[i]; var li = document.createElement("li"); li.className = assertion.result ? "pass" : "fail"; li.appendChild(document.createTextNode(assertion.message || "(no message)")); ol.appendChild(li); if (assertion.result) { good++ } else { bad++; config.stats.bad++; config.moduleStats.bad++ } } var b = document.createElement("strong"); b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>"; addEvent(b, "click", function () { var next = b.nextSibling, display = next.style.display; next.style.display = display === "none" ? "block" : "none" }); addEvent(b, "dblclick", function (e) { var target = e && e.target ? e.target : window.event.srcElement; if (target.nodeName.toLowerCase() === "strong") { var text = "", node = target.firstChild; while (node.nodeType === 3) { text += node.nodeValue; node = node.nextSibling } text = text.replace(/(^\s*|\s*$)/g, ""); if (window.location) { window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text) } } }); var li = document.createElement("li"); li.className = bad ? "fail" : "pass"; li.appendChild(b); li.appendChild(ol); tests.appendChild(li); if (bad) { var toolbar = id("qunit-testrunner-toolbar"); if (toolbar) { toolbar.style.display = "block"; id("qunit-filter-pass").disabled = null; id("qunit-filter-missing").disabled = null } } } else { for (var i = 0; i < config.assertions.length; i++) { if (!config.assertions[i].result) { bad++; config.stats.bad++; config.moduleStats.bad++ } } } QUnit.testDone(testName, bad, config.assertions.length); if (!window.setTimeout && !config.queue.length) { done() } }); if (window.setTimeout && !config.doneTimer) { config.doneTimer = window.setTimeout(function () { if (!config.queue.length) { done() } else { synchronize(done) } }, 13) } }, expect: function (asserts) { config.expected = asserts }, ok: function (a, msg) { QUnit.log(a, msg); config.assertions.push({ result: !!a, message: msg }) }, equal: function (actual, expected, message) { push(expected == actual, actual, expected, message) }, notEqual: function (actual, expected, message) { push(expected != actual, actual, expected, message) }, deepEqual: function (a, b, message) { push(QUnit.equiv(a, b), a, b, message) }, notDeepEqual: function (a, b, message) { push(!QUnit.equiv(a, b), a, b, message) }, strictEqual: function (actual, expected, message) { push(expected === actual, actual, expected, message) }, notStrictEqual: function (actual, expected, message) { push(expected !== actual, actual, expected, message) }, start: function () { if (window.setTimeout) { window.setTimeout(function () { if (config.timeout) { clearTimeout(config.timeout) } config.blocking = false; process() }, 13) } else { config.blocking = false; process() } }, stop: function (timeout) { config.blocking = true; if (timeout && window.setTimeout) { config.timeout = window.setTimeout(function () { QUnit.ok(false, "Test timed out"); QUnit.start() }, timeout) } }, reset: function () { if (window.jQuery) { jQuery("#main").html(config.fixture); jQuery.event.global = {}; jQuery.ajaxSettings = extend({}, config.ajaxSettings) } }, triggerEvent: function (elem, type, event) { if (document.createEvent) { event = document.createEvent("MouseEvents"); event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); elem.dispatchEvent(event) } else if (elem.fireEvent) { elem.fireEvent("on" + type) } }, is: function (type, obj) { return Object.prototype.toString.call(obj) === "[object " + type + "]" }, done: function (failures, total) { }, log: function (result, message) { }, testStart: function (name) { }, testDone: function (name, failures, total) { }, moduleStart: function (name, testEnvironment) { }, moduleDone: function (name, failures, total) { } }; QUnit.equals = QUnit.equal; QUnit.same = QUnit.deepEqual; var config = { queue: [], blocking: true }; (function () { var location = window.location || { search: "", protocol: "file:" }, GETParams = location.search.slice(1).split('&'); for (var i = 0; i < GETParams.length; i++) { GETParams[i] = decodeURIComponent(GETParams[i]); if (GETParams[i] === "noglobals") { GETParams.splice(i, 1); i--; config.noglobals = true } else if (GETParams[i].search('=') > -1) { GETParams.splice(i, 1); i-- } } config.filters = GETParams; QUnit.isLocal = !!(location.protocol === 'file:') })(); if (typeof exports === "undefined" || typeof require === "undefined") { extend(window, QUnit); window.QUnit = QUnit } else { extend(exports, QUnit); exports.QUnit = QUnit } if (typeof document === "undefined" || document.readyState === "complete") { config.autorun = true } addEvent(window, "load", function () { var oldconfig = extend({}, config); QUnit.init(); extend(config, oldconfig); config.blocking = false; var userAgent = id("qunit-userAgent"); if (userAgent) { userAgent.innerHTML = navigator.userAgent } var toolbar = id("qunit-testrunner-toolbar"); if (toolbar) { toolbar.style.display = "none"; var filter = document.createElement("input"); filter.type = "checkbox"; filter.id = "qunit-filter-pass"; filter.disabled = true; addEvent(filter, "click", function () { var li = document.getElementsByTagName("li"); for (var i = 0; i < li.length; i++) { if (li[i].className.indexOf("pass") > -1) { li[i].style.display = filter.checked ? "none" : "" } } }); toolbar.appendChild(filter); var label = document.createElement("label"); label.setAttribute("for", "qunit-filter-pass"); label.innerHTML = "Hide passed tests"; toolbar.appendChild(label); var missing = document.createElement("input"); missing.type = "checkbox"; missing.id = "qunit-filter-missing"; missing.disabled = true; addEvent(missing, "click", function () { var li = document.getElementsByTagName("li"); for (var i = 0; i < li.length; i++) { if (li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > -1) { li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block" } } }); toolbar.appendChild(missing); label = document.createElement("label"); label.setAttribute("for", "qunit-filter-missing"); label.innerHTML = "Hide missing tests (untested code is broken code)"; toolbar.appendChild(label) } var main = id('main'); if (main) { config.fixture = main.innerHTML } if (window.jQuery) { config.ajaxSettings = window.jQuery.ajaxSettings } QUnit.start() }); function done() { if (config.doneTimer && window.clearTimeout) { window.clearTimeout(config.doneTimer); config.doneTimer = null } if (config.queue.length) { config.doneTimer = window.setTimeout(function () { if (!config.queue.length) { done() } else { synchronize(done) } }, 13); return } config.autorun = true; if (config.currentModule) { QUnit.moduleDone(config.currentModule, config.moduleStats.bad, config.moduleStats.all) } var banner = id("qunit-banner"), tests = id("qunit-tests"), html = ['Tests completed in ', +new Date - config.started, ' milliseconds.<br/>', '<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad, '</span> failed.'].join(''); if (banner) { banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass") } if (tests) { var result = id("qunit-testresult"); if (!result) { result = document.createElement("p"); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore(result, tests.nextSibling) } result.innerHTML = html } QUnit.done(config.stats.bad, config.stats.all) } function validTest(name) { var i = config.filters.length, run = false; if (!i) { return true } while (i--) { var filter = config.filters[i], not = filter.charAt(0) == '!'; if (not) { filter = filter.slice(1) } if (name.indexOf(filter) !== -1) { return !not } if (not) { run = true } } return run } function push(result, actual, expected, message) { message = message || (result ? "okay" : "failed"); QUnit.ok(result, result ? message + ": " + QUnit.jsDump.parse(expected) : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual)) } function synchronize(callback) { config.queue.push(callback); if (config.autorun && !config.blocking) { process() } } function process() { var start = (new Date()).getTime(); while (config.queue.length && !config.blocking) { if (config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate)) { config.queue.shift()() } else { setTimeout(process, 13); break } } } function saveGlobal() { config.pollution = []; if (config.noglobals) { for (var key in window) { config.pollution.push(key) } } } function checkPollution(name) { var old = config.pollution; saveGlobal(); var newGlobals = diff(old, config.pollution); if (newGlobals.length > 0) { ok(false, "Introduced global variable(s): " + newGlobals.join(", ")); config.expected++ } var deletedGlobals = diff(config.pollution, old); if (deletedGlobals.length > 0) { ok(false, "Deleted global variable(s): " + deletedGlobals.join(", ")); config.expected++ } } function diff(a, b) { var result = a.slice(); for (var i = 0; i < result.length; i++) { for (var j = 0; j < b.length; j++) { if (result[i] === b[j]) { result.splice(i, 1); i--; break } } } return result } function fail(message, exception, callback) { if (typeof console !== "undefined" && console.error && console.warn) { console.error(message); console.error(exception); console.warn(callback.toString()) } else if (window.opera && opera.postError) { opera.postError(message, exception, callback.toString) } } function extend(a, b) { for (var prop in b) { a[prop] = b[prop] } return a } function addEvent(elem, type, fn) { if (elem.addEventListener) { elem.addEventListener(type, fn, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, fn) } else { fn() } } function id(name) { return !!(typeof document !== "undefined" && document && document.getElementById) && document.getElementById(name) } QUnit.equiv = function () { var innerEquiv; var callers = []; var parents = []; function hoozit(o) { if (QUnit.is("String", o)) { return "string" } else if (QUnit.is("Boolean", o)) { return "boolean" } else if (QUnit.is("Number", o)) { if (isNaN(o)) { return "nan" } else { return "number" } } else if (typeof o === "undefined") { return "undefined" } else if (o === null) { return "null" } else if (QUnit.is("Array", o)) { return "array" } else if (QUnit.is("Date", o)) { return "date" } else if (QUnit.is("RegExp", o)) { return "regexp" } else if (typeof o === "object") { return "object" } else if (QUnit.is("Function", o)) { return "function" } else { return undefined } } function bindCallbacks(o, callbacks, args) { var prop = hoozit(o); if (prop) { if (hoozit(callbacks[prop]) === "function") { return callbacks[prop].apply(callbacks, args) } else { return callbacks[prop] } } } var callbacks = function () { function useStrictEquality(b, a) { if (b instanceof a.constructor || a instanceof b.constructor) { return a == b } else { return a === b } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function (b) { return isNaN(b) }, "date": function (b, a) { return hoozit(b) === "date" && a.valueOf() === b.valueOf() }, "regexp": function (b, a) { return hoozit(b) === "regexp" && a.source === b.source && a.global === b.global && a.ignoreCase === b.ignoreCase && a.multiline === b.multiline }, "function": function () { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined" }, "array": function (b, a) { var i, j, loop; var len; if (!(hoozit(b) === "array")) { return false } len = a.length; if (len !== b.length) { return false } parents.push(a); for (i = 0; i < len; i++) { loop = false; for (j = 0; j < parents.length; j++) { if (parents[j] === a[i]) { loop = true } } if (!loop && !innerEquiv(a[i], b[i])) { parents.pop(); return false } } parents.pop(); return true }, "object": function (b, a) { var i, j, loop; var eq = true; var aProperties = [], bProperties = []; if (a.constructor !== b.constructor) { return false } callers.push(a.constructor); parents.push(a); for (i in a) { loop = false; for (j = 0; j < parents.length; j++) { if (parents[j] === a[i]) loop = true } aProperties.push(i); if (!loop && !innerEquiv(a[i], b[i])) { eq = false; break } } callers.pop(); parents.pop(); for (i in b) { bProperties.push(i) } return eq && innerEquiv(aProperties.sort(), bProperties.sort()) } } } (); innerEquiv = function () { var args = Array.prototype.slice.apply(arguments); if (args.length < 2) { return true } return (function (a, b) { if (a === b) { return true } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) { return false } else { return bindCallbacks(a, callbacks, [b, a]) } })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length - 1)) }; return innerEquiv } (); QUnit.jsDump = (function () { function quote(str) { return '"' + str.toString().replace(/"/g, '\\"') + '"' }; function literal(o) { return o + '' }; function join(pre, arr, post) { var s = jsDump.separator(), base = jsDump.indent(), inner = jsDump.indent(1); if (arr.join) arr = arr.join(',' + s + inner); if (!arr) return pre + post; return [pre, inner + arr, base + post].join(s) }; function array(arr) { var i = arr.length, ret = Array(i); this.up(); while (i--) ret[i] = this.parse(arr[i]); this.down(); return join('[', ret, ']') }; var reName = /^function (\w+)/; var jsDump = { parse: function (obj, type) { var parser = this.parsers[type || this.typeOf(obj)]; type = typeof parser; return type == 'function' ? parser.call(this, obj) : type == 'string' ? parser : this.parsers.error }, typeOf: function (obj) { var type; if (obj === null) { type = "null" } else if (typeof obj === "undefined") { type = "undefined" } else if (QUnit.is("RegExp", obj)) { type = "regexp" } else if (QUnit.is("Date", obj)) { type = "date" } else if (QUnit.is("Function", obj)) { type = "function" } else if (obj.setInterval && obj.document && !obj.nodeType) { type = "window" } else if (obj.nodeType === 9) { type = "document" } else if (obj.nodeType) { type = "node" } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) { type = "array" } else { type = typeof obj } return type }, separator: function () { return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ' }, indent: function (extra) { if (!this.multiline) return ''; var chr = this.indentChar; if (this.HTML) chr = chr.replace(/\t/g, '   ').replace(/ /g, '&nbsp;'); return Array(this._depth_ + (extra || 0)).join(chr) }, up: function (a) { this._depth_ += a || 1 }, down: function (a) { this._depth_ -= a || 1 }, setParser: function (name, parser) { this.parsers[name] = parser }, quote: quote, literal: literal, join: join, _depth_: 1, parsers: { window: '[Window]', document: '[Document]', error: '[ERROR]', unknown: '[Unknown]', 'null': 'null', undefined: 'undefined', 'function': function (fn) { var ret = 'function', name = 'name' in fn ? fn.name : (reName.exec(fn) || [])[1]; if (name) ret += ' ' + name; ret += '('; ret = [ret, this.parse(fn, 'functionArgs'), '){'].join(''); return join(ret, this.parse(fn, 'functionCode'), '}') }, array: array, nodelist: array, arguments: array, object: function (map) { var ret = []; this.up(); for (var key in map) ret.push(this.parse(key, 'key') + ': ' + this.parse(map[key])); this.down(); return join('{', ret, '}') }, node: function (node) { var open = this.HTML ? '&lt;' : '<', close = this.HTML ? '&gt;' : '>'; var tag = node.nodeName.toLowerCase(), ret = open + tag; for (var a in this.DOMAttrs) { var val = node[this.DOMAttrs[a]]; if (val) ret += ' ' + a + '=' + this.parse(val, 'attribute') } return ret + close + open + '/' + tag + close }, functionArgs: function (fn) { var l = fn.length; if (!l) return ''; var args = Array(l); while (l--) args[l] = String.fromCharCode(97 + l); return ' ' + args.join(', ') + ' ' }, key: quote, functionCode: '[code]', attribute: quote, string: quote, date: quote, regexp: literal, number: literal, 'boolean': literal }, DOMAttrs: { id: 'id', name: 'name', 'class': 'className' }, HTML: false, indentChar: '   ', multiline: false }; return jsDump })() })(this);
