2010-08-30 09:15:38 -04:00
/ *
* QUnit - A JavaScript Unit Testing Framework
*
* http : //docs.jquery.com/QUnit
*
* Copyright ( c ) 2009 John Resig , Jörn Zaefferer
* Dual licensed under the MIT ( MIT - LICENSE . txt )
* and GPL ( GPL - LICENSE . txt ) licenses .
* /
( function ( window ) {
2010-11-27 20:42:22 -05:00
var defined = {
setTimeout : typeof window . setTimeout !== "undefined"
}
2010-08-30 09:15:38 -04:00
var QUnit = {
// call on start of module test to prepend name to all tests
module : function ( name , testEnvironment ) {
config . currentModule = name ;
synchronize ( function ( ) {
2010-11-27 20:42:22 -05:00
if ( config . previousModule ) {
2010-08-30 09:15:38 -04:00
QUnit . moduleDone ( config . currentModule , config . moduleStats . bad , config . moduleStats . all ) ;
}
2010-11-27 20:42:22 -05:00
config . previousModule = config . currentModule ;
2010-08-30 09:15:38 -04:00
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 = '<span class="test-name">' + testName + '</span>' , testEnvironment , 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 ;
}
synchronize ( function ( ) {
testEnvironment = extend ( {
setup : function ( ) { } ,
teardown : function ( ) { }
} , config . moduleTestEnvironment ) ;
if ( testEnvironmentArg ) {
extend ( testEnvironment , testEnvironmentArg ) ;
}
QUnit . testStart ( testName , testEnvironment ) ;
// allow utility functions to access the current test environment
QUnit . current _testEnvironment = testEnvironment ;
config . assertions = [ ] ;
config . expected = expected ;
var tests = id ( "qunit-tests" ) ;
if ( tests ) {
var b = document . createElement ( "strong" ) ;
b . innerHTML = "Running " + name ;
var li = document . createElement ( "li" ) ;
li . appendChild ( b ) ;
li . id = "current-test-output" ;
2010-11-27 20:42:22 -05:00
tests . appendChild ( li ) ;
2010-08-30 09:15:38 -04:00
}
try {
if ( ! config . pollution ) {
saveGlobal ( ) ;
}
testEnvironment . setup . call ( testEnvironment ) ;
} catch ( e ) {
QUnit . ok ( false , "Setup failed on " + name + ": " + e . message ) ;
}
} ) ;
synchronize ( function ( ) {
if ( async ) {
QUnit . stop ( ) ;
}
try {
callback . call ( testEnvironment ) ;
} catch ( e ) {
fail ( "Test " + name + " died, exception and test follows" , e , callback ) ;
2010-11-27 20:42:22 -05:00
QUnit . ok ( false , "Died on test #" + ( config . assertions . length + 1 ) + ": " + e . message + " - " + QUnit . jsDump . parse ( e ) ) ;
2010-08-30 09:15:38 -04:00
// else next test will carry the responsibility
saveGlobal ( ) ;
// Restart the tests if they're blocking
if ( config . blocking ) {
start ( ) ;
}
}
} ) ;
synchronize ( function ( ) {
try {
checkPollution ( ) ;
testEnvironment . teardown . call ( testEnvironment ) ;
} catch ( e ) {
QUnit . ok ( false , "Teardown failed on " + name + ": " + e . message ) ;
}
} ) ;
synchronize ( function ( ) {
if ( config . expected && config . expected != config . assertions . length ) {
QUnit . ok ( false , "Expected " + config . expected + " assertions, but " + config . assertions . length + " were run" ) ;
}
2010-11-27 20:42:22 -05:00
2010-08-30 09:15:38 -04:00
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" ) ;
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" ;
2010-11-27 20:42:22 -05:00
li . innerHTML = assertion . message || ( assertion . result ? "okay" : "failed" ) ;
2010-08-30 09:15:38 -04:00
ol . appendChild ( li ) ;
if ( assertion . result ) {
good ++ ;
} else {
bad ++ ;
config . stats . bad ++ ;
config . moduleStats . bad ++ ;
}
}
if ( bad == 0 ) {
ol . style . display = "none" ;
}
var b = document . createElement ( "strong" ) ;
2010-10-26 19:47:24 -04:00
b . innerHTML = name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + config . assertions . length + ")</b>" ;
2010-08-30 09:15:38 -04:00
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 ( "current-test-output" ) ;
li . id = "" ;
li . className = bad ? "fail" : "pass" ;
2010-11-27 20:42:22 -05:00
li . style . display = resultDisplayStyle ( ! bad ) ;
2010-08-30 09:15:38 -04:00
li . removeChild ( li . firstChild ) ;
li . appendChild ( b ) ;
li . appendChild ( ol ) ;
if ( bad ) {
var toolbar = id ( "qunit-testrunner-toolbar" ) ;
if ( toolbar ) {
toolbar . style . display = "block" ;
id ( "qunit-filter-pass" ) . disabled = null ;
}
}
} else {
for ( var i = 0 ; i < config . assertions . length ; i ++ ) {
if ( ! config . assertions [ i ] . result ) {
bad ++ ;
config . stats . bad ++ ;
config . moduleStats . bad ++ ;
}
}
}
2010-11-27 20:42:22 -05:00
try {
QUnit . reset ( ) ;
} catch ( e ) {
fail ( "reset() failed, following Test " + name + ", exception and reset fn follows" , e , QUnit . reset ) ;
2010-08-30 09:15:38 -04:00
}
2010-11-27 20:42:22 -05:00
QUnit . testDone ( testName , bad , config . assertions . length ) ;
2010-08-30 09:15:38 -04:00
} ) ;
synchronize ( done ) ;
} ,
/ * *
* 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 . expected = asserts ;
} ,
/ * *
* Asserts true .
* @ example ok ( "asdfasdf" . length > 5 , "There must be at least 5 chars" ) ;
* /
ok : function ( a , msg ) {
2010-11-27 20:42:22 -05:00
a = ! ! a ;
var details = {
result : a ,
message : msg
} ;
2010-08-30 09:15:38 -04:00
msg = escapeHtml ( msg ) ;
2010-11-27 20:42:22 -05:00
QUnit . log ( a , msg , details ) ;
2010-08-30 09:15:38 -04:00
config . assertions . push ( {
2010-11-27 20:42:22 -05:00
result : a ,
2010-08-30 09:15:38 -04:00
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 ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( expected == actual , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
notEqual : function ( actual , expected , message ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( expected != actual , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
deepEqual : function ( actual , expected , message ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( QUnit . equiv ( actual , expected ) , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
notDeepEqual : function ( actual , expected , message ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( ! QUnit . equiv ( actual , expected ) , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
strictEqual : function ( actual , expected , message ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( expected === actual , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
notStrictEqual : function ( actual , expected , message ) {
2010-11-27 20:42:22 -05:00
QUnit . push ( expected !== actual , actual , expected , message ) ;
2010-08-30 09:15:38 -04:00
} ,
raises : function ( fn , message ) {
try {
fn ( ) ;
ok ( false , message ) ;
}
catch ( e ) {
ok ( true , message ) ;
}
} ,
start : function ( ) {
// A slight delay, to avoid any current callbacks
2010-11-27 20:42:22 -05:00
if ( defined . setTimeout ) {
2010-08-30 09:15:38 -04:00
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 ;
2010-11-27 20:42:22 -05:00
if ( timeout && defined . setTimeout ) {
2010-08-30 09:15:38 -04:00
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 ] . 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 ,
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 ) ;
}
} ,
/ * *
* Resets the test setup . Useful for tests that modify the DOM .
2010-11-27 20:42:22 -05:00
*
* If jQuery is available , uses jQuery ' s html ( ) , otherwise just innerHTML .
2010-08-30 09:15:38 -04:00
* /
reset : function ( ) {
if ( window . jQuery ) {
2010-11-27 20:42:22 -05:00
jQuery ( "#main, #qunit-fixture" ) . html ( config . fixture ) ;
} else {
var main = id ( 'main' ) || id ( 'qunit-fixture' ) ;
if ( main ) {
main . innerHTML = config . fixture ;
}
2010-08-30 09:15:38 -04:00
}
} ,
/ * *
* 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 ;
} ,
2010-11-27 20:42:22 -05:00
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>' ;
}
output += "</table>" ;
QUnit . log ( result , message , details ) ;
config . assertions . push ( {
result : ! ! result ,
message : output
} ) ;
} ,
2010-08-30 09:15:38 -04:00
// Logging callbacks
begin : function ( ) { } ,
done : function ( failures , total ) { } ,
log : function ( result , message ) { } ,
testStart : function ( name , testEnvironment ) { } ,
testDone : function ( name , failures , total ) { } ,
moduleStart : function ( name , testEnvironment ) { } ,
moduleDone : function ( name , failures , total ) { }
} ) ;
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 ) {
2010-11-27 20:42:22 -05:00
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>' ;
}
}
2010-08-30 09:15:38 -04:00
}
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 main = id ( 'main' ) || id ( 'qunit-fixture' ) ;
if ( main ) {
config . fixture = main . innerHTML ;
}
if ( config . autostart ) {
QUnit . start ( ) ;
}
} ) ;
function done ( ) {
if ( config . doneTimer && window . clearTimeout ) {
window . clearTimeout ( config . doneTimer ) ;
config . doneTimer = null ;
}
if ( config . queue . length ) {
2010-11-27 20:42:22 -05:00
if ( defined . setTimeout ) {
config . doneTimer = window . setTimeout ( function ( ) {
if ( ! config . queue . length ) {
done ( ) ;
} else {
synchronize ( done ) ;
}
} , 13 ) ;
}
2010-08-30 09:15:38 -04:00
return ;
}
config . autorun = true ;
// Log the last module results
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 ;
}
2010-11-27 20:42:22 -05:00
function resultDisplayStyle ( passed ) {
return passed && id ( "qunit-filter-pass" ) && id ( "qunit-filter-pass" ) . checked ? 'none' : '' ;
}
2010-08-30 09:15:38 -04:00
function escapeHtml ( s ) {
2010-11-27 20:42:22 -05:00
if ( ! s ) {
return "" ;
}
s = s + "" ;
2010-08-30 09:15:38 -04:00
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 {
2010-11-27 20:42:22 -05:00
window . setTimeout ( process , 13 ) ;
2010-08-30 09:15:38 -04:00
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 ++ ;
}
}
// 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" ;
2010-11-27 20:42:22 -05:00
} else if ( typeof obj . setInterval !== undefined && typeof obj . document !== "undefined" && typeof obj . nodeType === "undefined" ) {
2010-08-30 09:15:38 -04:00
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 += '(' ;
2010-11-27 20:42:22 -05:00
ret = [ ret , QUnit . jsDump . parse ( fn , 'functionArgs' ) , '){' ] . join ( '' ) ;
return join ( ret , QUnit . jsDump . parse ( fn , 'functionCode' ) , '}' ) ;
2010-08-30 09:15:38 -04:00
} ,
array : array ,
nodelist : array ,
arguments : array ,
object : function ( map ) {
var ret = [ ] ;
2010-11-27 20:42:22 -05:00
QUnit . jsDump . up ( ) ;
2010-08-30 09:15:38 -04:00
for ( var key in map )
2010-11-27 20:42:22 -05:00
ret . push ( QUnit . jsDump . parse ( key , 'key' ) + ': ' + QUnit . jsDump . parse ( map [ key ] ) ) ;
QUnit . jsDump . down ( ) ;
2010-08-30 09:15:38 -04:00
return join ( '{' , ret , '}' ) ;
} ,
node : function ( node ) {
2010-11-27 20:42:22 -05:00
var open = QUnit . jsDump . HTML ? '<' : '<' ,
close = QUnit . jsDump . HTML ? '>' : '>' ;
2010-08-30 09:15:38 -04:00
var tag = node . nodeName . toLowerCase ( ) ,
ret = open + tag ;
2010-11-27 20:42:22 -05:00
for ( var a in QUnit . jsDump . DOMAttrs ) {
var val = node [ QUnit . jsDump . DOMAttrs [ a ] ] ;
2010-08-30 09:15:38 -04:00
if ( val )
2010-11-27 20:42:22 -05:00
ret += ' ' + a + '=' + QUnit . jsDump . parse ( val , 'attribute' ) ;
2010-08-30 09:15:38 -04:00
}
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 )
2010-11-27 20:42:22 -05:00
indentChar : ' ' , //indentation unit
multiline : true //if true, items in a collection, are separated by a \n, else just a space.
2010-08-30 09:15:38 -04:00
} ;
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 ;
2010-11-27 20:42:22 -05:00
} ;
2010-08-30 09:15:38 -04:00
} ) ( ) ;
} ) ( this ) ;