/* 
 | 
 * QUnit - A JavaScript Unit Testing Framework 
 | 
 *  
 | 
 * http://docs.jquery.com/QUnit 
 | 
 * 
 | 
 * Copyright (c) 2011 John Resig, Jörn Zaefferer 
 | 
 * Dual licensed under the MIT (MIT-LICENSE.txt) 
 | 
 * or GPL (GPL-LICENSE.txt) licenses. 
 | 
 */ 
 | 
  
 | 
(function(window) { 
 | 
  
 | 
var defined = { 
 | 
    setTimeout: typeof window.setTimeout !== "undefined", 
 | 
    sessionStorage: (function() { 
 | 
        try { 
 | 
            return !!sessionStorage.getItem; 
 | 
        } catch(e){ 
 | 
            return false; 
 | 
        } 
 | 
  })() 
 | 
} 
 | 
  
 | 
var testId = 0; 
 | 
  
 | 
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { 
 | 
    this.name = name; 
 | 
    this.testName = testName; 
 | 
    this.expected = expected; 
 | 
    this.testEnvironmentArg = testEnvironmentArg; 
 | 
    this.async = async; 
 | 
    this.callback = callback; 
 | 
    this.assertions = []; 
 | 
}; 
 | 
Test.prototype = { 
 | 
    init: function() { 
 | 
        var tests = id("qunit-tests"); 
 | 
        if (tests) { 
 | 
            var b = document.createElement("strong"); 
 | 
                b.innerHTML = "Running " + this.name; 
 | 
            var li = document.createElement("li"); 
 | 
                li.appendChild( b ); 
 | 
                li.id = this.id = "test-output" + testId++; 
 | 
            tests.appendChild( li ); 
 | 
        } 
 | 
    }, 
 | 
    setup: function() { 
 | 
        if (this.module != config.previousModule) { 
 | 
            if ( config.previousModule ) { 
 | 
                QUnit.moduleDone( { 
 | 
                    name: config.previousModule, 
 | 
                    failed: config.moduleStats.bad, 
 | 
                    passed: config.moduleStats.all - config.moduleStats.bad, 
 | 
                    total: config.moduleStats.all 
 | 
                } ); 
 | 
            } 
 | 
            config.previousModule = this.module; 
 | 
            config.moduleStats = { all: 0, bad: 0 }; 
 | 
            QUnit.moduleStart( { 
 | 
                name: this.module 
 | 
            } ); 
 | 
        } 
 | 
  
 | 
        config.current = this; 
 | 
        this.testEnvironment = extend({ 
 | 
            setup: function() {}, 
 | 
            teardown: function() {} 
 | 
        }, this.moduleTestEnvironment); 
 | 
        if (this.testEnvironmentArg) { 
 | 
            extend(this.testEnvironment, this.testEnvironmentArg); 
 | 
        } 
 | 
  
 | 
        QUnit.testStart( { 
 | 
            name: this.testName 
 | 
        } ); 
 | 
  
 | 
        // allow utility functions to access the current test environment 
 | 
        // TODO why?? 
 | 
        QUnit.current_testEnvironment = this.testEnvironment; 
 | 
         
 | 
        try { 
 | 
            if ( !config.pollution ) { 
 | 
                saveGlobal(); 
 | 
            } 
 | 
  
 | 
            this.testEnvironment.setup.call(this.testEnvironment); 
 | 
        } catch(e) { 
 | 
            QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); 
 | 
        } 
 | 
    }, 
 | 
    run: function() { 
 | 
        if ( this.async ) { 
 | 
            QUnit.stop(); 
 | 
        } 
 | 
  
 | 
        if ( config.notrycatch ) { 
 | 
            this.callback.call(this.testEnvironment); 
 | 
            return; 
 | 
        } 
 | 
        try { 
 | 
            this.callback.call(this.testEnvironment); 
 | 
        } catch(e) { 
 | 
            fail("Test " + this.testName + " died, exception and test follows", e, this.callback); 
 | 
            QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); 
 | 
            // else next test will carry the responsibility 
 | 
            saveGlobal(); 
 | 
  
 | 
            // Restart the tests if they're blocking 
 | 
            if ( config.blocking ) { 
 | 
                start(); 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
    teardown: function() { 
 | 
        try { 
 | 
            checkPollution(); 
 | 
            this.testEnvironment.teardown.call(this.testEnvironment); 
 | 
        } catch(e) { 
 | 
            QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); 
 | 
        } 
 | 
    }, 
 | 
    finish: function() { 
 | 
        if ( this.expected && this.expected != this.assertions.length ) { 
 | 
            QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); 
 | 
        } 
 | 
         
 | 
        var good = 0, bad = 0, 
 | 
            tests = id("qunit-tests"); 
 | 
  
 | 
        config.stats.all += this.assertions.length; 
 | 
        config.moduleStats.all += this.assertions.length; 
 | 
  
 | 
        if ( tests ) { 
 | 
            var ol  = document.createElement("ol"); 
 | 
  
 | 
            for ( var i = 0; i < this.assertions.length; i++ ) { 
 | 
                var assertion = this.assertions[i]; 
 | 
  
 | 
                var li = document.createElement("li"); 
 | 
                li.className = assertion.result ? "pass" : "fail"; 
 | 
                li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); 
 | 
                ol.appendChild( li ); 
 | 
  
 | 
                if ( assertion.result ) { 
 | 
                    good++; 
 | 
                } else { 
 | 
                    bad++; 
 | 
                    config.stats.bad++; 
 | 
                    config.moduleStats.bad++; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            // store result when possible 
 | 
            defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad); 
 | 
  
 | 
            if (bad == 0) { 
 | 
                ol.style.display = "none"; 
 | 
            } 
 | 
  
 | 
            var b = document.createElement("strong"); 
 | 
            b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.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() == "span" || target.nodeName.toLowerCase() == "b" ) { 
 | 
                    target = target.parentNode; 
 | 
                } 
 | 
                if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 
 | 
                    window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, "")); 
 | 
                } 
 | 
            }); 
 | 
  
 | 
            var li = id(this.id); 
 | 
            li.className = bad ? "fail" : "pass"; 
 | 
            li.style.display = resultDisplayStyle(!bad); 
 | 
            li.removeChild( li.firstChild ); 
 | 
            li.appendChild( b ); 
 | 
            li.appendChild( ol ); 
 | 
  
 | 
        } else { 
 | 
            for ( var i = 0; i < this.assertions.length; i++ ) { 
 | 
                if ( !this.assertions[i].result ) { 
 | 
                    bad++; 
 | 
                    config.stats.bad++; 
 | 
                    config.moduleStats.bad++; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        try { 
 | 
            QUnit.reset(); 
 | 
        } catch(e) { 
 | 
            fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); 
 | 
        } 
 | 
  
 | 
        QUnit.testDone( { 
 | 
            name: this.testName, 
 | 
            failed: bad, 
 | 
            passed: this.assertions.length - bad, 
 | 
            total: this.assertions.length 
 | 
        } ); 
 | 
    }, 
 | 
     
 | 
    queue: function() { 
 | 
        var test = this; 
 | 
        synchronize(function() { 
 | 
            test.init(); 
 | 
        }); 
 | 
        function run() { 
 | 
            // each of these can by async 
 | 
            synchronize(function() { 
 | 
                test.setup(); 
 | 
            }); 
 | 
            synchronize(function() { 
 | 
                test.run(); 
 | 
            }); 
 | 
            synchronize(function() { 
 | 
                test.teardown(); 
 | 
            }); 
 | 
            synchronize(function() { 
 | 
                test.finish(); 
 | 
            }); 
 | 
        } 
 | 
        // defer when previous test run passed, if storage is available 
 | 
        var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName); 
 | 
        if (bad) { 
 | 
            run(); 
 | 
        } else { 
 | 
            synchronize(run); 
 | 
        }; 
 | 
    } 
 | 
     
 | 
} 
 | 
  
 | 
var QUnit = { 
 | 
  
 | 
    // call on start of module test to prepend name to all tests 
 | 
    module: function(name, testEnvironment) { 
 | 
        config.currentModule = name; 
 | 
        config.currentModuleTestEnviroment = 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 = '<span class="test-name">' + testName + '</span>', testEnvironmentArg; 
 | 
  
 | 
        if ( arguments.length === 2 ) { 
 | 
            callback = expected; 
 | 
            expected = null; 
 | 
        } 
 | 
        // is 2nd argument a testEnvironment? 
 | 
        if ( expected && typeof expected === 'object') { 
 | 
            testEnvironmentArg =  expected; 
 | 
            expected = null; 
 | 
        } 
 | 
  
 | 
        if ( config.currentModule ) { 
 | 
            name = '<span class="module-name">' + config.currentModule + "</span>: " + name; 
 | 
        } 
 | 
  
 | 
        if ( !validTest(config.currentModule + ": " + testName) ) { 
 | 
            return; 
 | 
        } 
 | 
         
 | 
        var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); 
 | 
        test.module = config.currentModule; 
 | 
        test.moduleTestEnvironment = config.currentModuleTestEnviroment; 
 | 
        test.queue(); 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 
 | 
     */ 
 | 
    expect: function(asserts) { 
 | 
        config.current.expected = asserts; 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Asserts true. 
 | 
     * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 
 | 
     */ 
 | 
    ok: function(a, msg) { 
 | 
        a = !!a; 
 | 
        var details = { 
 | 
            result: a, 
 | 
            message: msg 
 | 
        }; 
 | 
        msg = escapeHtml(msg); 
 | 
        QUnit.log(details); 
 | 
        config.current.assertions.push({ 
 | 
            result: a, 
 | 
            message: msg 
 | 
        }); 
 | 
    }, 
 | 
  
 | 
    /** 
 | 
     * Checks that the first two arguments are equal, with an optional message. 
 | 
     * Prints out both actual and expected values. 
 | 
     * 
 | 
     * Prefered to ok( actual == expected, message ) 
 | 
     * 
 | 
     * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); 
 | 
     * 
 | 
     * @param Object actual 
 | 
     * @param Object expected 
 | 
     * @param String message (optional) 
 | 
     */ 
 | 
    equal: function(actual, expected, message) { 
 | 
        QUnit.push(expected == actual, actual, expected, message); 
 | 
    }, 
 | 
  
 | 
    notEqual: function(actual, expected, message) { 
 | 
        QUnit.push(expected != actual, actual, expected, message); 
 | 
    }, 
 | 
     
 | 
    deepEqual: function(actual, expected, message) { 
 | 
        QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); 
 | 
    }, 
 | 
  
 | 
    notDeepEqual: function(actual, expected, message) { 
 | 
        QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); 
 | 
    }, 
 | 
  
 | 
    strictEqual: function(actual, expected, message) { 
 | 
        QUnit.push(expected === actual, actual, expected, message); 
 | 
    }, 
 | 
  
 | 
    notStrictEqual: function(actual, expected, message) { 
 | 
        QUnit.push(expected !== actual, actual, expected, message); 
 | 
    }, 
 | 
  
 | 
    raises: function(block, expected, message) { 
 | 
        var actual, ok = false; 
 | 
     
 | 
        if (typeof expected === 'string') { 
 | 
            message = expected; 
 | 
            expected = null; 
 | 
        } 
 | 
     
 | 
        try { 
 | 
            block(); 
 | 
        } catch (e) { 
 | 
            actual = e; 
 | 
        } 
 | 
     
 | 
        if (actual) { 
 | 
            // we don't want to validate thrown error 
 | 
            if (!expected) { 
 | 
                ok = true; 
 | 
            // expected is a regexp     
 | 
            } else if (QUnit.objectType(expected) === "regexp") { 
 | 
                ok = expected.test(actual); 
 | 
            // expected is a constructor     
 | 
            } else if (actual instanceof expected) { 
 | 
                ok = true; 
 | 
            // expected is a validation function which returns true is validation passed     
 | 
            } else if (expected.call({}, actual) === true) { 
 | 
                ok = true; 
 | 
            } 
 | 
        } 
 | 
             
 | 
        QUnit.ok(ok, message); 
 | 
    }, 
 | 
  
 | 
    start: function() { 
 | 
        config.semaphore--; 
 | 
        if (config.semaphore > 0) { 
 | 
            // don't start until equal number of stop-calls 
 | 
            return; 
 | 
        } 
 | 
        if (config.semaphore < 0) { 
 | 
            // ignore if start is called more often then stop 
 | 
            config.semaphore = 0; 
 | 
        } 
 | 
        // A slight delay, to avoid any current callbacks 
 | 
        if ( defined.setTimeout ) { 
 | 
            window.setTimeout(function() { 
 | 
                if ( config.timeout ) { 
 | 
                    clearTimeout(config.timeout); 
 | 
                } 
 | 
  
 | 
                config.blocking = false; 
 | 
                process(); 
 | 
            }, 13); 
 | 
        } else { 
 | 
            config.blocking = false; 
 | 
            process(); 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    stop: function(timeout) { 
 | 
        config.semaphore++; 
 | 
        config.blocking = true; 
 | 
  
 | 
        if ( timeout && defined.setTimeout ) { 
 | 
            clearTimeout(config.timeout); 
 | 
            config.timeout = window.setTimeout(function() { 
 | 
                QUnit.ok( false, "Test timed out" ); 
 | 
                QUnit.start(); 
 | 
            }, timeout); 
 | 
        } 
 | 
    } 
 | 
  
 | 
}; 
 | 
  
 | 
// Backwards compatibility, deprecated 
 | 
QUnit.equals = QUnit.equal; 
 | 
QUnit.same = QUnit.deepEqual; 
 | 
  
 | 
// Maintain internal state 
 | 
var config = { 
 | 
    // The queue of tests to run 
 | 
    queue: [], 
 | 
  
 | 
    // block until document ready 
 | 
    blocking: true 
 | 
}; 
 | 
  
 | 
// Load paramaters 
 | 
(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] === "notrycatch" ) { 
 | 
            GETParams.splice( i, 1 ); 
 | 
            i--; 
 | 
            config.notrycatch = true; 
 | 
        } else if ( GETParams[i].search('=') > -1 ) { 
 | 
            GETParams.splice( i, 1 ); 
 | 
            i--; 
 | 
        } 
 | 
    } 
 | 
     
 | 
    // restrict modules/tests by get parameters 
 | 
    config.filters = GETParams; 
 | 
     
 | 
    // Figure out if we're running the tests from a server or not 
 | 
    QUnit.isLocal = !!(location.protocol === 'file:'); 
 | 
})(); 
 | 
  
 | 
// Expose the API as global variables, unless an 'exports' 
 | 
// object exists, in that case we assume we're in CommonJS 
 | 
if ( typeof exports === "undefined" || typeof require === "undefined" ) { 
 | 
    extend(window, QUnit); 
 | 
    window.QUnit = QUnit; 
 | 
} else { 
 | 
    extend(exports, QUnit); 
 | 
    exports.QUnit = QUnit; 
 | 
} 
 | 
  
 | 
// define these after exposing globals to keep them in these QUnit namespace only 
 | 
extend(QUnit, { 
 | 
    config: config, 
 | 
  
 | 
    // Initialize the configuration options 
 | 
    init: function() { 
 | 
        extend(config, { 
 | 
            stats: { all: 0, bad: 0 }, 
 | 
            moduleStats: { all: 0, bad: 0 }, 
 | 
            started: +new Date, 
 | 
            updateRate: 1000, 
 | 
            blocking: false, 
 | 
            autostart: true, 
 | 
            autorun: false, 
 | 
            filters: [], 
 | 
            queue: [], 
 | 
            semaphore: 0 
 | 
        }); 
 | 
  
 | 
        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 ); 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Resets the test setup. Useful for tests that modify the DOM. 
 | 
     *  
 | 
     * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. 
 | 
     */ 
 | 
    reset: function() { 
 | 
        if ( window.jQuery ) { 
 | 
            jQuery( "#main, #qunit-fixture" ).html( config.fixture ); 
 | 
        } else { 
 | 
            var main = id( 'main' ) || id( 'qunit-fixture' ); 
 | 
            if ( main ) { 
 | 
                main.innerHTML = config.fixture; 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    /** 
 | 
     * Trigger an event on an element. 
 | 
     * 
 | 
     * @example triggerEvent( document.body, "click" ); 
 | 
     * 
 | 
     * @param DOMElement elem 
 | 
     * @param String type 
 | 
     */ 
 | 
    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); 
 | 
        } 
 | 
    }, 
 | 
     
 | 
    // Safe object type checking 
 | 
    is: function( type, obj ) { 
 | 
        return QUnit.objectType( obj ) == type; 
 | 
    }, 
 | 
     
 | 
    objectType: function( obj ) { 
 | 
        if (typeof obj === "undefined") { 
 | 
                return "undefined"; 
 | 
  
 | 
        // consider: typeof null === object 
 | 
        } 
 | 
        if (obj === null) { 
 | 
                return "null"; 
 | 
        } 
 | 
  
 | 
        var type = Object.prototype.toString.call( obj ) 
 | 
            .match(/^\[object\s(.*)\]$/)[1] || ''; 
 | 
  
 | 
        switch (type) { 
 | 
                case 'Number': 
 | 
                        if (isNaN(obj)) { 
 | 
                                return "nan"; 
 | 
                        } else { 
 | 
                                return "number"; 
 | 
                        } 
 | 
                case 'String': 
 | 
                case 'Boolean': 
 | 
                case 'Array': 
 | 
                case 'Date': 
 | 
                case 'RegExp': 
 | 
                case 'Function': 
 | 
                        return type.toLowerCase(); 
 | 
        } 
 | 
        if (typeof obj === "object") { 
 | 
                return "object"; 
 | 
        } 
 | 
        return undefined; 
 | 
    }, 
 | 
     
 | 
    push: function(result, actual, expected, message) { 
 | 
        var details = { 
 | 
            result: result, 
 | 
            message: message, 
 | 
            actual: actual, 
 | 
            expected: expected 
 | 
        }; 
 | 
         
 | 
        message = escapeHtml(message) || (result ? "okay" : "failed"); 
 | 
        message = '<span class="test-message">' + message + "</span>"; 
 | 
        expected = escapeHtml(QUnit.jsDump.parse(expected)); 
 | 
        actual = escapeHtml(QUnit.jsDump.parse(actual)); 
 | 
        var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>'; 
 | 
        if (actual != expected) { 
 | 
            output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>'; 
 | 
            output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>'; 
 | 
        } 
 | 
        if (!result) { 
 | 
            var source = sourceFromStacktrace(); 
 | 
            if (source) { 
 | 
                details.source = source; 
 | 
                output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>'; 
 | 
            } 
 | 
        } 
 | 
        output += "</table>"; 
 | 
         
 | 
        QUnit.log(details); 
 | 
         
 | 
        config.current.assertions.push({ 
 | 
            result: !!result, 
 | 
            message: output 
 | 
        }); 
 | 
    }, 
 | 
     
 | 
    // Logging callbacks; all receive a single argument with the listed properties 
 | 
    // run test/logs.html for any related changes 
 | 
    begin: function() {}, 
 | 
    // done: { failed, passed, total, runtime } 
 | 
    done: function() {}, 
 | 
    // log: { result, actual, expected, message } 
 | 
    log: function() {}, 
 | 
    // testStart: { name } 
 | 
    testStart: function() {}, 
 | 
    // testDone: { name, failed, passed, total } 
 | 
    testDone: function() {}, 
 | 
    // moduleStart: { name } 
 | 
    moduleStart: function() {}, 
 | 
    // moduleDone: { name, failed, passed, total } 
 | 
    moduleDone: function() {} 
 | 
}); 
 | 
  
 | 
if ( typeof document === "undefined" || document.readyState === "complete" ) { 
 | 
    config.autorun = true; 
 | 
} 
 | 
  
 | 
addEvent(window, "load", function() { 
 | 
    QUnit.begin({}); 
 | 
     
 | 
    // Initialize the config, saving the execution queue 
 | 
    var oldconfig = extend({}, config); 
 | 
    QUnit.init(); 
 | 
    extend(config, oldconfig); 
 | 
  
 | 
    config.blocking = false; 
 | 
  
 | 
    var userAgent = id("qunit-userAgent"); 
 | 
    if ( userAgent ) { 
 | 
        userAgent.innerHTML = navigator.userAgent; 
 | 
    } 
 | 
    var banner = id("qunit-header"); 
 | 
    if ( banner ) { 
 | 
        var paramsIndex = location.href.lastIndexOf(location.search); 
 | 
        if ( paramsIndex > -1 ) { 
 | 
            var mainPageLocation = location.href.slice(0, paramsIndex); 
 | 
            if ( mainPageLocation == location.href ) { 
 | 
                banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> '; 
 | 
            } else { 
 | 
                var testName = decodeURIComponent(location.search.slice(1)); 
 | 
                banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> › <a href="">' + testName + '</a>'; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
     
 | 
    var toolbar = id("qunit-testrunner-toolbar"); 
 | 
    if ( toolbar ) { 
 | 
        var filter = document.createElement("input"); 
 | 
        filter.type = "checkbox"; 
 | 
        filter.id = "qunit-filter-pass"; 
 | 
        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" : ""; 
 | 
                } 
 | 
            } 
 | 
            if ( defined.sessionStorage ) { 
 | 
                sessionStorage.setItem("qunit-filter-passed-tests", filter.checked ? "true" : ""); 
 | 
            } 
 | 
        }); 
 | 
        if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { 
 | 
            filter.checked = true; 
 | 
        } 
 | 
        toolbar.appendChild( filter ); 
 | 
  
 | 
        var label = document.createElement("label"); 
 | 
        label.setAttribute("for", "qunit-filter-pass"); 
 | 
        label.innerHTML = "Hide passed tests"; 
 | 
        toolbar.appendChild( label ); 
 | 
    } 
 | 
  
 | 
    var main = id('main') || id('qunit-fixture'); 
 | 
    if ( main ) { 
 | 
        config.fixture = main.innerHTML; 
 | 
    } 
 | 
  
 | 
    if (config.autostart) { 
 | 
        QUnit.start(); 
 | 
    } 
 | 
}); 
 | 
  
 | 
function done() { 
 | 
    config.autorun = true; 
 | 
  
 | 
    // Log the last module results 
 | 
    if ( config.currentModule ) { 
 | 
        QUnit.moduleDone( { 
 | 
            name: config.currentModule, 
 | 
            failed: config.moduleStats.bad, 
 | 
            passed: config.moduleStats.all - config.moduleStats.bad, 
 | 
            total: config.moduleStats.all 
 | 
        } ); 
 | 
    } 
 | 
  
 | 
    var banner = id("qunit-banner"), 
 | 
        tests = id("qunit-tests"), 
 | 
        runtime = +new Date - config.started, 
 | 
        passed = config.stats.all - config.stats.bad, 
 | 
        html = [ 
 | 
            'Tests completed in ', 
 | 
            runtime, 
 | 
            ' milliseconds.<br/>', 
 | 
            '<span class="passed">', 
 | 
            passed, 
 | 
            '</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( { 
 | 
        failed: config.stats.bad, 
 | 
        passed: passed,  
 | 
        total: config.stats.all, 
 | 
        runtime: runtime 
 | 
    } ); 
 | 
} 
 | 
  
 | 
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; 
 | 
} 
 | 
  
 | 
// so far supports only Firefox, Chrome and Opera (buggy) 
 | 
// could be extended in the future to use something like https://github.com/csnover/TraceKit 
 | 
function sourceFromStacktrace() { 
 | 
    try { 
 | 
        throw new Error(); 
 | 
    } catch ( e ) { 
 | 
        if (e.stacktrace) { 
 | 
            // Opera 
 | 
            return e.stacktrace.split("\n")[6]; 
 | 
        } else if (e.stack) { 
 | 
            // Firefox, Chrome 
 | 
            return e.stack.split("\n")[4]; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
function resultDisplayStyle(passed) { 
 | 
    return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : ''; 
 | 
} 
 | 
  
 | 
function escapeHtml(s) { 
 | 
    if (!s) { 
 | 
        return ""; 
 | 
    } 
 | 
    s = s + ""; 
 | 
    return s.replace(/[\&"<>\\]/g, function(s) { 
 | 
        switch(s) { 
 | 
            case "&": return "&"; 
 | 
            case "\\": return "\\\\"; 
 | 
            case '"': return '\"'; 
 | 
            case "<": return "<"; 
 | 
            case ">": return ">"; 
 | 
            default: return s; 
 | 
        } 
 | 
    }); 
 | 
} 
 | 
  
 | 
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 { 
 | 
            window.setTimeout( process, 13 ); 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
  if (!config.blocking && !config.queue.length) { 
 | 
    done(); 
 | 
  } 
 | 
} 
 | 
  
 | 
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.current.expected++; 
 | 
    } 
 | 
  
 | 
    var deletedGlobals = diff( config.pollution, old ); 
 | 
    if ( deletedGlobals.length > 0 ) { 
 | 
        ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); 
 | 
        config.current.expected++; 
 | 
    } 
 | 
} 
 | 
  
 | 
// returns a new Array with the elements that are in a but not in b 
 | 
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 ); 
 | 
} 
 | 
  
 | 
// Test for equality any JavaScript type. 
 | 
// Discussions and reference: http://philrathe.com/articles/equiv 
 | 
// Test suites: http://philrathe.com/tests/equiv 
 | 
// Author: Philippe Rathé <prathe@gmail.com> 
 | 
QUnit.equiv = function () { 
 | 
  
 | 
    var innerEquiv; // the real equiv function 
 | 
    var callers = []; // stack to decide between skip/abort functions 
 | 
    var parents = []; // stack to avoiding loops from circular referencing 
 | 
  
 | 
    // Call the o related callback with the given arguments. 
 | 
    function bindCallbacks(o, callbacks, args) { 
 | 
        var prop = QUnit.objectType(o); 
 | 
        if (prop) { 
 | 
            if (QUnit.objectType(callbacks[prop]) === "function") { 
 | 
                return callbacks[prop].apply(callbacks, args); 
 | 
            } else { 
 | 
                return callbacks[prop]; // or undefined 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
     
 | 
    var callbacks = function () { 
 | 
  
 | 
        // for string, boolean, number and null 
 | 
        function useStrictEquality(b, a) { 
 | 
            if (b instanceof a.constructor || a instanceof b.constructor) { 
 | 
                // to catch short annotaion VS 'new' annotation of a declaration 
 | 
                // e.g. var i = 1; 
 | 
                //      var j = new Number(1); 
 | 
                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 QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); 
 | 
            }, 
 | 
  
 | 
            "regexp": function (b, a) { 
 | 
                return QUnit.objectType(b) === "regexp" && 
 | 
                    a.source === b.source && // the regex itself 
 | 
                    a.global === b.global && // and its modifers (gmi) ... 
 | 
                    a.ignoreCase === b.ignoreCase && 
 | 
                    a.multiline === b.multiline; 
 | 
            }, 
 | 
  
 | 
            // - skip when the property is a method of an instance (OOP) 
 | 
            // - abort otherwise, 
 | 
            //   initial === would have catch identical references anyway 
 | 
            "function": function () { 
 | 
                var caller = callers[callers.length - 1]; 
 | 
                return caller !== Object && 
 | 
                        typeof caller !== "undefined"; 
 | 
            }, 
 | 
  
 | 
            "array": function (b, a) { 
 | 
                var i, j, loop; 
 | 
                var len; 
 | 
  
 | 
                // b could be an object literal here 
 | 
                if ( ! (QUnit.objectType(b) === "array")) { 
 | 
                    return false; 
 | 
                }    
 | 
                 
 | 
                len = a.length; 
 | 
                if (len !== b.length) { // safe and faster 
 | 
                    return false; 
 | 
                } 
 | 
                 
 | 
                //track reference to avoid circular references 
 | 
                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;//dont rewalk array 
 | 
                        } 
 | 
                    } 
 | 
                    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; // unless we can proove it 
 | 
                var aProperties = [], bProperties = []; // collection of strings 
 | 
  
 | 
                // comparing constructors is more strict than using instanceof 
 | 
                if ( a.constructor !== b.constructor) { 
 | 
                    return false; 
 | 
                } 
 | 
  
 | 
                // stack constructor before traversing properties 
 | 
                callers.push(a.constructor); 
 | 
                //track reference to avoid circular references 
 | 
                parents.push(a); 
 | 
                 
 | 
                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep 
 | 
                    loop = false; 
 | 
                    for(j=0;j<parents.length;j++){ 
 | 
                        if(parents[j] === a[i]) 
 | 
                            loop = true; //don't go down the same path twice 
 | 
                    } 
 | 
                    aProperties.push(i); // collect a's properties 
 | 
  
 | 
                    if (!loop && ! innerEquiv(a[i], b[i])) { 
 | 
                        eq = false; 
 | 
                        break; 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                callers.pop(); // unstack, we are done 
 | 
                parents.pop(); 
 | 
  
 | 
                for (i in b) { 
 | 
                    bProperties.push(i); // collect b's properties 
 | 
                } 
 | 
  
 | 
                // Ensures identical properties name 
 | 
                return eq && innerEquiv(aProperties.sort(), bProperties.sort()); 
 | 
            } 
 | 
        }; 
 | 
    }(); 
 | 
  
 | 
    innerEquiv = function () { // can take multiple arguments 
 | 
        var args = Array.prototype.slice.apply(arguments); 
 | 
        if (args.length < 2) { 
 | 
            return true; // end transition 
 | 
        } 
 | 
  
 | 
        return (function (a, b) { 
 | 
            if (a === b) { 
 | 
                return true; // catch the most you can 
 | 
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) { 
 | 
                return false; // don't lose time with error prone cases 
 | 
            } else { 
 | 
                return bindCallbacks(a, callbacks, [b, a]); 
 | 
            } 
 | 
  
 | 
        // apply transition with (1..n) arguments 
 | 
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); 
 | 
    }; 
 | 
  
 | 
    return innerEquiv; 
 | 
  
 | 
}(); 
 | 
  
 | 
/** 
 | 
 * jsDump 
 | 
 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 
 | 
 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) 
 | 
 * Date: 5/15/2008 
 | 
 * @projectDescription Advanced and extensible data dumping for Javascript. 
 | 
 * @version 1.0.0 
 | 
 * @author Ariel Flesler 
 | 
 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} 
 | 
 */ 
 | 
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 ) { //type is used mostly internally, you can fix a (custom)type in advance 
 | 
            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 (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { 
 | 
                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 ? ' ' : ' '; 
 | 
        }, 
 | 
        indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 
 | 
            if ( !this.multiline ) 
 | 
                return ''; 
 | 
            var chr = this.indentChar; 
 | 
            if ( this.HTML ) 
 | 
                chr = chr.replace(/\t/g,'   ').replace(/ /g,' '); 
 | 
            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; 
 | 
        }, 
 | 
        // The next 3 are exposed so you can use them 
 | 
        quote:quote,  
 | 
        literal:literal, 
 | 
        join:join, 
 | 
        // 
 | 
        _depth_: 1, 
 | 
        // This is the list of parsers, to modify them, use jsDump.setParser 
 | 
        parsers:{ 
 | 
            window: '[Window]', 
 | 
            document: '[Document]', 
 | 
            error:'[ERROR]', //when no parser is found, shouldn't happen 
 | 
            unknown: '[Unknown]', 
 | 
            'null':'null', 
 | 
            undefined:'undefined', 
 | 
            'function':function( fn ) { 
 | 
                var ret = 'function', 
 | 
                    name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 
 | 
                if ( name ) 
 | 
                    ret += ' ' + name; 
 | 
                ret += '('; 
 | 
                 
 | 
                ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); 
 | 
                return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); 
 | 
            }, 
 | 
            array: array, 
 | 
            nodelist: array, 
 | 
            arguments: array, 
 | 
            object:function( map ) { 
 | 
                var ret = [ ]; 
 | 
                QUnit.jsDump.up(); 
 | 
                for ( var key in map ) 
 | 
                    ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); 
 | 
                QUnit.jsDump.down(); 
 | 
                return join( '{', ret, '}' ); 
 | 
            }, 
 | 
            node:function( node ) { 
 | 
                var open = QUnit.jsDump.HTML ? '<' : '<', 
 | 
                    close = QUnit.jsDump.HTML ? '>' : '>'; 
 | 
                     
 | 
                var tag = node.nodeName.toLowerCase(), 
 | 
                    ret = open + tag; 
 | 
                     
 | 
                for ( var a in QUnit.jsDump.DOMAttrs ) { 
 | 
                    var val = node[QUnit.jsDump.DOMAttrs[a]]; 
 | 
                    if ( val ) 
 | 
                        ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); 
 | 
                } 
 | 
                return ret + close + open + '/' + tag + close; 
 | 
            }, 
 | 
            functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function 
 | 
                var l = fn.length; 
 | 
                if ( !l ) return '';                 
 | 
                 
 | 
                var args = Array(l); 
 | 
                while ( l-- ) 
 | 
                    args[l] = String.fromCharCode(97+l);//97 is 'a' 
 | 
                return ' ' + args.join(', ') + ' '; 
 | 
            }, 
 | 
            key:quote, //object calls it internally, the key part of an item in a map 
 | 
            functionCode:'[code]', //function calls it internally, it's the content of the function 
 | 
            attribute:quote, //node calls it internally, it's an html attribute value 
 | 
            string:quote, 
 | 
            date:quote, 
 | 
            regexp:literal, //regex 
 | 
            number:literal, 
 | 
            'boolean':literal 
 | 
        }, 
 | 
        DOMAttrs:{//attributes to dump from nodes, name=>realName 
 | 
            id:'id', 
 | 
            name:'name', 
 | 
            'class':'className' 
 | 
        }, 
 | 
        HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) 
 | 
        indentChar:'  ',//indentation unit 
 | 
        multiline:true //if true, items in a collection, are separated by a \n, else just a space. 
 | 
    }; 
 | 
  
 | 
    return jsDump; 
 | 
})(); 
 | 
  
 | 
// from Sizzle.js 
 | 
function getText( elems ) { 
 | 
    var ret = "", elem; 
 | 
  
 | 
    for ( var i = 0; elems[i]; i++ ) { 
 | 
        elem = elems[i]; 
 | 
  
 | 
        // Get the text from text nodes and CDATA nodes 
 | 
        if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 
 | 
            ret += elem.nodeValue; 
 | 
  
 | 
        // Traverse everything else, except comment nodes 
 | 
        } else if ( elem.nodeType !== 8 ) { 
 | 
            ret += getText( elem.childNodes ); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return ret; 
 | 
}; 
 | 
  
 | 
/* 
 | 
 * Javascript Diff Algorithm 
 | 
 *  By John Resig (http://ejohn.org/) 
 | 
 *  Modified by Chu Alan "sprite" 
 | 
 * 
 | 
 * Released under the MIT license. 
 | 
 * 
 | 
 * More Info: 
 | 
 *  http://ejohn.org/projects/javascript-diff-algorithm/ 
 | 
 *   
 | 
 * Usage: QUnit.diff(expected, actual) 
 | 
 *  
 | 
 * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" 
 | 
 */ 
 | 
QUnit.diff = (function() { 
 | 
    function diff(o, n){ 
 | 
        var ns = new Object(); 
 | 
        var os = new Object(); 
 | 
         
 | 
        for (var i = 0; i < n.length; i++) { 
 | 
            if (ns[n[i]] == null)  
 | 
                ns[n[i]] = { 
 | 
                    rows: new Array(), 
 | 
                    o: null 
 | 
                }; 
 | 
            ns[n[i]].rows.push(i); 
 | 
        } 
 | 
         
 | 
        for (var i = 0; i < o.length; i++) { 
 | 
            if (os[o[i]] == null)  
 | 
                os[o[i]] = { 
 | 
                    rows: new Array(), 
 | 
                    n: null 
 | 
                }; 
 | 
            os[o[i]].rows.push(i); 
 | 
        } 
 | 
         
 | 
        for (var i in ns) { 
 | 
            if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { 
 | 
                n[ns[i].rows[0]] = { 
 | 
                    text: n[ns[i].rows[0]], 
 | 
                    row: os[i].rows[0] 
 | 
                }; 
 | 
                o[os[i].rows[0]] = { 
 | 
                    text: o[os[i].rows[0]], 
 | 
                    row: ns[i].rows[0] 
 | 
                }; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        for (var i = 0; i < n.length - 1; i++) { 
 | 
            if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && 
 | 
            n[i + 1] == o[n[i].row + 1]) { 
 | 
                n[i + 1] = { 
 | 
                    text: n[i + 1], 
 | 
                    row: n[i].row + 1 
 | 
                }; 
 | 
                o[n[i].row + 1] = { 
 | 
                    text: o[n[i].row + 1], 
 | 
                    row: i + 1 
 | 
                }; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        for (var i = n.length - 1; i > 0; i--) { 
 | 
            if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && 
 | 
            n[i - 1] == o[n[i].row - 1]) { 
 | 
                n[i - 1] = { 
 | 
                    text: n[i - 1], 
 | 
                    row: n[i].row - 1 
 | 
                }; 
 | 
                o[n[i].row - 1] = { 
 | 
                    text: o[n[i].row - 1], 
 | 
                    row: i - 1 
 | 
                }; 
 | 
            } 
 | 
        } 
 | 
         
 | 
        return { 
 | 
            o: o, 
 | 
            n: n 
 | 
        }; 
 | 
    } 
 | 
     
 | 
    return function(o, n){ 
 | 
        o = o.replace(/\s+$/, ''); 
 | 
        n = n.replace(/\s+$/, ''); 
 | 
        var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); 
 | 
  
 | 
        var str = ""; 
 | 
         
 | 
        var oSpace = o.match(/\s+/g); 
 | 
        if (oSpace == null) { 
 | 
            oSpace = [" "]; 
 | 
        } 
 | 
        else { 
 | 
            oSpace.push(" "); 
 | 
        } 
 | 
        var nSpace = n.match(/\s+/g); 
 | 
        if (nSpace == null) { 
 | 
            nSpace = [" "]; 
 | 
        } 
 | 
        else { 
 | 
            nSpace.push(" "); 
 | 
        } 
 | 
         
 | 
        if (out.n.length == 0) { 
 | 
            for (var i = 0; i < out.o.length; i++) { 
 | 
                str += '<del>' + out.o[i] + oSpace[i] + "</del>"; 
 | 
            } 
 | 
        } 
 | 
        else { 
 | 
            if (out.n[0].text == null) { 
 | 
                for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 
 | 
                    str += '<del>' + out.o[n] + oSpace[n] + "</del>"; 
 | 
                } 
 | 
            } 
 | 
             
 | 
            for (var i = 0; i < out.n.length; i++) { 
 | 
                if (out.n[i].text == null) { 
 | 
                    str += '<ins>' + out.n[i] + nSpace[i] + "</ins>"; 
 | 
                } 
 | 
                else { 
 | 
                    var pre = ""; 
 | 
                     
 | 
                    for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { 
 | 
                        pre += '<del>' + out.o[n] + oSpace[n] + "</del>"; 
 | 
                    } 
 | 
                    str += " " + out.n[i].text + nSpace[i] + pre; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
         
 | 
        return str; 
 | 
    }; 
 | 
})(); 
 | 
  
 | 
})(this); 
 |