| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334 | /*! * QUnit 1.23.1 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2016-04-12T17:29Z */( function( global ) {var QUnit = {};var Date = global.Date;var now = Date.now || function() {	return new Date().getTime();};var setTimeout = global.setTimeout;var clearTimeout = global.clearTimeout;// Store a local window from the global to allow direct references.var window = global.window;var defined = {	document: window && window.document !== undefined,	setTimeout: setTimeout !== undefined,	sessionStorage: ( function() {		var x = "qunit-test-string";		try {			sessionStorage.setItem( x, x );			sessionStorage.removeItem( x );			return true;		} catch ( e ) {			return false;		}	}() )};var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" );var globalStartCalled = false;var runStarted = false;var toString = Object.prototype.toString,	hasOwn = Object.prototype.hasOwnProperty;// Returns a new Array with the elements that are in a but not in bfunction diff( a, b ) {	var i, j,		result = a.slice();	for ( i = 0; i < result.length; i++ ) {		for ( j = 0; j < b.length; j++ ) {			if ( result[ i ] === b[ j ] ) {				result.splice( i, 1 );				i--;				break;			}		}	}	return result;}// From jquery.jsfunction inArray( elem, array ) {	if ( array.indexOf ) {		return array.indexOf( elem );	}	for ( var i = 0, length = array.length; i < length; i++ ) {		if ( array[ i ] === elem ) {			return i;		}	}	return -1;}/** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */function objectValues ( obj ) {	var key, val,		vals = QUnit.is( "array", obj ) ? [] : {};	for ( key in obj ) {		if ( hasOwn.call( obj, key ) ) {			val = obj[ key ];			vals[ key ] = val === Object( val ) ? objectValues( val ) : val;		}	}	return vals;}function extend( a, b, undefOnly ) {	for ( var prop in b ) {		if ( hasOwn.call( b, prop ) ) {			// Avoid "Member not found" error in IE8 caused by messing with window.constructor			// This block runs on every environment, so `global` is being used instead of `window`			// to avoid errors on node.			if ( prop !== "constructor" || a !== global ) {				if ( b[ prop ] === undefined ) {					delete a[ prop ];				} else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {					a[ prop ] = b[ prop ];				}			}		}	}	return a;}function objectType( obj ) {	if ( typeof obj === "undefined" ) {		return "undefined";	}	// Consider: typeof null === object	if ( obj === null ) {		return "null";	}	var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),		type = match && match[ 1 ];	switch ( type ) {		case "Number":			if ( isNaN( obj ) ) {				return "nan";			}			return "number";		case "String":		case "Boolean":		case "Array":		case "Set":		case "Map":		case "Date":		case "RegExp":		case "Function":		case "Symbol":			return type.toLowerCase();	}	if ( typeof obj === "object" ) {		return "object";	}}// Safe object type checkingfunction is( type, obj ) {	return QUnit.objectType( obj ) === type;}// Doesn't support IE6 to IE9, it will return undefined on these browsers// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stackfunction extractStacktrace( e, offset ) {	offset = offset === undefined ? 4 : offset;	var stack, include, i;	if ( e.stack ) {		stack = e.stack.split( "\n" );		if ( /^error$/i.test( stack[ 0 ] ) ) {			stack.shift();		}		if ( fileName ) {			include = [];			for ( i = offset; i < stack.length; i++ ) {				if ( stack[ i ].indexOf( fileName ) !== -1 ) {					break;				}				include.push( stack[ i ] );			}			if ( include.length ) {				return include.join( "\n" );			}		}		return stack[ offset ];	// Support: Safari <=6 only	} else if ( e.sourceURL ) {		// Exclude useless self-reference for generated Error objects		if ( /qunit.js$/.test( e.sourceURL ) ) {			return;		}		// For actual exceptions, this is useful		return e.sourceURL + ":" + e.line;	}}function sourceFromStacktrace( offset ) {	var error = new Error();	// Support: Safari <=7 only, IE <=10 - 11 only	// Not all browsers generate the `stack` property for `new Error()`, see also #636	if ( !error.stack ) {		try {			throw error;		} catch ( err ) {			error = err;		}	}	return extractStacktrace( error, offset );}/** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */var config = {	// The queue of tests to run	queue: [],	// Block until document ready	blocking: true,	// By default, run previously failed tests first	// very useful in combination with "Hide passed tests" checked	reorder: true,	// By default, modify document.title when suite is done	altertitle: true,	// HTML Reporter: collapse every test except the first failing test	// If false, all failing tests will be expanded	collapse: true,	// By default, scroll to top of the page when suite is done	scrolltop: true,	// Depth up-to which object will be dumped	maxDepth: 5,	// When enabled, all tests must call expect()	requireExpects: false,	// Placeholder for user-configurable form-exposed URL parameters	urlConfig: [],	// Set of all modules.	modules: [],	// Stack of nested modules	moduleStack: [],	// The first unnamed module	currentModule: {		name: "",		tests: []	},	callbacks: {}};// Push a loose unnamed module to the modules collectionconfig.modules.push( config.currentModule );var loggingCallbacks = {};// Register logging callbacksfunction registerLoggingCallbacks( obj ) {	var i, l, key,		callbackNames = [ "begin", "done", "log", "testStart", "testDone",			"moduleStart", "moduleDone" ];	function registerLoggingCallback( key ) {		var loggingCallback = function( callback ) {			if ( objectType( callback ) !== "function" ) {				throw new Error(					"QUnit logging methods require a callback function as their first parameters."				);			}			config.callbacks[ key ].push( callback );		};		// DEPRECATED: This will be removed on QUnit 2.0.0+		// Stores the registered functions allowing restoring		// at verifyLoggingCallbacks() if modified		loggingCallbacks[ key ] = loggingCallback;		return loggingCallback;	}	for ( i = 0, l = callbackNames.length; i < l; i++ ) {		key = callbackNames[ i ];		// Initialize key collection of logging callback		if ( objectType( config.callbacks[ key ] ) === "undefined" ) {			config.callbacks[ key ] = [];		}		obj[ key ] = registerLoggingCallback( key );	}}function runLoggingCallbacks( key, args ) {	var i, l, callbacks;	callbacks = config.callbacks[ key ];	for ( i = 0, l = callbacks.length; i < l; i++ ) {		callbacks[ i ]( args );	}}// DEPRECATED: This will be removed on 2.0.0+// This function verifies if the loggingCallbacks were modified by the user// If so, it will restore it, assign the given callback and print a console warningfunction verifyLoggingCallbacks() {	var loggingCallback, userCallback;	for ( loggingCallback in loggingCallbacks ) {		if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {			userCallback = QUnit[ loggingCallback ];			// Restore the callback function			QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];			// Assign the deprecated given callback			QUnit[ loggingCallback ]( userCallback );			if ( global.console && global.console.warn ) {				global.console.warn(					"QUnit." + loggingCallback + " was replaced with a new value.\n" +					"Please, check out the documentation on how to apply logging callbacks.\n" +					"Reference: https://api.qunitjs.com/category/callbacks/"				);			}		}	}}( function() {	if ( !defined.document ) {		return;	}	// `onErrorFnPrev` initialized at top of scope	// Preserve other handlers	var onErrorFnPrev = window.onerror;	// Cover uncaught exceptions	// Returning true will suppress the default browser handler,	// returning false will let it run.	window.onerror = function( error, filePath, linerNr ) {		var ret = false;		if ( onErrorFnPrev ) {			ret = onErrorFnPrev( error, filePath, linerNr );		}		// Treat return value as window.onerror itself does,		// Only do our handling if not suppressed.		if ( ret !== true ) {			if ( QUnit.config.current ) {				if ( QUnit.config.current.ignoreGlobalErrors ) {					return true;				}				QUnit.pushFailure( error, filePath + ":" + linerNr );			} else {				QUnit.test( "global failure", extend( function() {					QUnit.pushFailure( error, filePath + ":" + linerNr );				}, { validTest: true } ) );			}			return false;		}		return ret;	};}() );// Figure out if we're running the tests from a server or notQUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );// Expose the current QUnit versionQUnit.version = "1.23.1";extend( QUnit, {	// Call on start of module test to prepend name to all tests	module: function( name, testEnvironment, executeNow ) {		var module, moduleFns;		var currentModule = config.currentModule;		if ( arguments.length === 2 ) {			if ( objectType( testEnvironment ) === "function" ) {				executeNow = testEnvironment;				testEnvironment = undefined;			}		}		// DEPRECATED: handles setup/teardown functions,		// beforeEach and afterEach should be used instead		if ( testEnvironment && testEnvironment.setup ) {			testEnvironment.beforeEach = testEnvironment.setup;			delete testEnvironment.setup;		}		if ( testEnvironment && testEnvironment.teardown ) {			testEnvironment.afterEach = testEnvironment.teardown;			delete testEnvironment.teardown;		}		module = createModule();		moduleFns = {			beforeEach: setHook( module, "beforeEach" ),			afterEach: setHook( module, "afterEach" )		};		if ( objectType( executeNow ) === "function" ) {			config.moduleStack.push( module );			setCurrentModule( module );			executeNow.call( module.testEnvironment, moduleFns );			config.moduleStack.pop();			module = module.parentModule || currentModule;		}		setCurrentModule( module );		function createModule() {			var parentModule = config.moduleStack.length ?				config.moduleStack.slice( -1 )[ 0 ] : null;			var moduleName = parentModule !== null ?				[ parentModule.name, name ].join( " > " ) : name;			var module = {				name: moduleName,				parentModule: parentModule,				tests: [],				moduleId: generateHash( moduleName )			};			var env = {};			if ( parentModule ) {				extend( env, parentModule.testEnvironment );				delete env.beforeEach;				delete env.afterEach;			}			extend( env, testEnvironment );			module.testEnvironment = env;			config.modules.push( module );			return module;		}		function setCurrentModule( module ) {			config.currentModule = module;		}	},	// DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.	asyncTest: asyncTest,	test: test,	skip: skip,	only: only,	// DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.	// In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.	start: function( count ) {		var globalStartAlreadyCalled = globalStartCalled;		if ( !config.current ) {			globalStartCalled = true;			if ( runStarted ) {				throw new Error( "Called start() outside of a test context while already started" );			} else if ( globalStartAlreadyCalled || count > 1 ) {				throw new Error( "Called start() outside of a test context too many times" );			} else if ( config.autostart ) {				throw new Error( "Called start() outside of a test context when " +					"QUnit.config.autostart was true" );			} else if ( !config.pageLoaded ) {				// The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it				config.autostart = true;				return;			}		} else {			// If a test is running, adjust its semaphore			config.current.semaphore -= count || 1;			// If semaphore is non-numeric, throw error			if ( isNaN( config.current.semaphore ) ) {				config.current.semaphore = 0;				QUnit.pushFailure(					"Called start() with a non-numeric decrement.",					sourceFromStacktrace( 2 )				);				return;			}			// Don't start until equal number of stop-calls			if ( config.current.semaphore > 0 ) {				return;			}			// Throw an Error if start is called more often than stop			if ( config.current.semaphore < 0 ) {				config.current.semaphore = 0;				QUnit.pushFailure(					"Called start() while already started (test's semaphore was 0 already)",					sourceFromStacktrace( 2 )				);				return;			}		}		resumeProcessing();	},	// DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.	stop: function( count ) {		// If there isn't a test running, don't allow QUnit.stop() to be called		if ( !config.current ) {			throw new Error( "Called stop() outside of a test context" );		}		// If a test is running, adjust its semaphore		config.current.semaphore += count || 1;		pauseProcessing();	},	config: config,	is: is,	objectType: objectType,	extend: extend,	load: function() {		config.pageLoaded = true;		// Initialize the configuration options		extend( config, {			stats: { all: 0, bad: 0 },			moduleStats: { all: 0, bad: 0 },			started: 0,			updateRate: 1000,			autostart: true,			filter: ""		}, true );		config.blocking = false;		if ( config.autostart ) {			resumeProcessing();		}	},	stack: function( offset ) {		offset = ( offset || 0 ) + 2;		return sourceFromStacktrace( offset );	}} );registerLoggingCallbacks( QUnit );function begin() {	var i, l,		modulesLog = [];	// If the test run hasn't officially begun yet	if ( !config.started ) {		// Record the time of the test run's beginning		config.started = now();		verifyLoggingCallbacks();		// Delete the loose unnamed module if unused.		if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {			config.modules.shift();		}		// Avoid unnecessary information by not logging modules' test environments		for ( i = 0, l = config.modules.length; i < l; i++ ) {			modulesLog.push( {				name: config.modules[ i ].name,				tests: config.modules[ i ].tests			} );		}		// The test run is officially beginning now		runLoggingCallbacks( "begin", {			totalTests: Test.count,			modules: modulesLog		} );	}	config.blocking = false;	process( true );}function process( last ) {	function next() {		process( last );	}	var start = now();	config.depth = ( config.depth || 0 ) + 1;	while ( config.queue.length && !config.blocking ) {		if ( !defined.setTimeout || config.updateRate <= 0 ||				( ( now() - start ) < config.updateRate ) ) {			if ( config.current ) {				// Reset async tracking for each phase of the Test lifecycle				config.current.usedAsync = false;			}			config.queue.shift()();		} else {			setTimeout( next, 13 );			break;		}	}	config.depth--;	if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {		done();	}}function pauseProcessing() {	config.blocking = true;	if ( config.testTimeout && defined.setTimeout ) {		clearTimeout( config.timeout );		config.timeout = setTimeout( function() {			if ( config.current ) {				config.current.semaphore = 0;				QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );			} else {				throw new Error( "Test timed out" );			}			resumeProcessing();		}, config.testTimeout );	}}function resumeProcessing() {	runStarted = true;	// A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)	if ( defined.setTimeout ) {		setTimeout( function() {			if ( config.current && config.current.semaphore > 0 ) {				return;			}			if ( config.timeout ) {				clearTimeout( config.timeout );			}			begin();		}, 13 );	} else {		begin();	}}function done() {	var runtime, passed;	config.autorun = true;	// Log the last module results	if ( config.previousModule ) {		runLoggingCallbacks( "moduleDone", {			name: config.previousModule.name,			tests: config.previousModule.tests,			failed: config.moduleStats.bad,			passed: config.moduleStats.all - config.moduleStats.bad,			total: config.moduleStats.all,			runtime: now() - config.moduleStats.started		} );	}	delete config.previousModule;	runtime = now() - config.started;	passed = config.stats.all - config.stats.bad;	runLoggingCallbacks( "done", {		failed: config.stats.bad,		passed: passed,		total: config.stats.all,		runtime: runtime	} );}function setHook( module, hookName ) {	if ( module.testEnvironment === undefined ) {		module.testEnvironment = {};	}	return function( callback ) {		module.testEnvironment[ hookName ] = callback;	};}var focused = false;var priorityCount = 0;var unitSampler;function Test( settings ) {	var i, l;	++Test.count;	extend( this, settings );	this.assertions = [];	this.semaphore = 0;	this.usedAsync = false;	this.module = config.currentModule;	this.stack = sourceFromStacktrace( 3 );	// Register unique strings	for ( i = 0, l = this.module.tests; i < l.length; i++ ) {		if ( this.module.tests[ i ].name === this.testName ) {			this.testName += " ";		}	}	this.testId = generateHash( this.module.name, this.testName );	this.module.tests.push( {		name: this.testName,		testId: this.testId	} );	if ( settings.skip ) {		// Skipped tests will fully ignore any sent callback		this.callback = function() {};		this.async = false;		this.expected = 0;	} else {		this.assert = new Assert( this );	}}Test.count = 0;Test.prototype = {	before: function() {		if (			// Emit moduleStart when we're switching from one module to another			this.module !== config.previousModule ||				// They could be equal (both undefined) but if the previousModule property doesn't				// yet exist it means this is the first test in a suite that isn't wrapped in a				// module, in which case we'll just emit a moduleStart event for 'undefined'.				// Without this, reporters can get testStart before moduleStart  which is a problem.				!hasOwn.call( config, "previousModule" )		) {			if ( hasOwn.call( config, "previousModule" ) ) {				runLoggingCallbacks( "moduleDone", {					name: config.previousModule.name,					tests: config.previousModule.tests,					failed: config.moduleStats.bad,					passed: config.moduleStats.all - config.moduleStats.bad,					total: config.moduleStats.all,					runtime: now() - config.moduleStats.started				} );			}			config.previousModule = this.module;			config.moduleStats = { all: 0, bad: 0, started: now() };			runLoggingCallbacks( "moduleStart", {				name: this.module.name,				tests: this.module.tests			} );		}		config.current = this;		if ( this.module.testEnvironment ) {			delete this.module.testEnvironment.beforeEach;			delete this.module.testEnvironment.afterEach;		}		this.testEnvironment = extend( {}, this.module.testEnvironment );		this.started = now();		runLoggingCallbacks( "testStart", {			name: this.testName,			module: this.module.name,			testId: this.testId		} );		if ( !config.pollution ) {			saveGlobal();		}	},	run: function() {		var promise;		config.current = this;		if ( this.async ) {			QUnit.stop();		}		this.callbackStarted = now();		if ( config.notrycatch ) {			runTest( this );			return;		}		try {			runTest( this );		} catch ( e ) {			this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +				this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );			// Else next test will carry the responsibility			saveGlobal();			// Restart the tests if they're blocking			if ( config.blocking ) {				QUnit.start();			}		}		function runTest( test ) {			promise = test.callback.call( test.testEnvironment, test.assert );			test.resolvePromise( promise );		}	},	after: function() {		checkPollution();	},	queueHook: function( hook, hookName ) {		var promise,			test = this;		return function runHook() {			config.current = test;			if ( config.notrycatch ) {				callHook();				return;			}			try {				callHook();			} catch ( error ) {				test.pushFailure( hookName + " failed on " + test.testName + ": " +				( error.message || error ), extractStacktrace( error, 0 ) );			}			function callHook() {				promise = hook.call( test.testEnvironment, test.assert );				test.resolvePromise( promise, hookName );			}		};	},	// Currently only used for module level hooks, can be used to add global level ones	hooks: function( handler ) {		var hooks = [];		function processHooks( test, module ) {			if ( module.parentModule ) {				processHooks( test, module.parentModule );			}			if ( module.testEnvironment &&				QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) {				hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) );			}		}		// Hooks are ignored on skipped tests		if ( !this.skip ) {			processHooks( this, this.module );		}		return hooks;	},	finish: function() {		config.current = this;		if ( config.requireExpects && this.expected === null ) {			this.pushFailure( "Expected number of assertions to be defined, but expect() was " +				"not called.", this.stack );		} else if ( this.expected !== null && this.expected !== this.assertions.length ) {			this.pushFailure( "Expected " + this.expected + " assertions, but " +				this.assertions.length + " were run", this.stack );		} else if ( this.expected === null && !this.assertions.length ) {			this.pushFailure( "Expected at least one assertion, but none were run - call " +				"expect(0) to accept zero assertions.", this.stack );		}		var i,			bad = 0;		this.runtime = now() - this.started;		config.stats.all += this.assertions.length;		config.moduleStats.all += this.assertions.length;		for ( i = 0; i < this.assertions.length; i++ ) {			if ( !this.assertions[ i ].result ) {				bad++;				config.stats.bad++;				config.moduleStats.bad++;			}		}		runLoggingCallbacks( "testDone", {			name: this.testName,			module: this.module.name,			skipped: !!this.skip,			failed: bad,			passed: this.assertions.length - bad,			total: this.assertions.length,			runtime: this.runtime,			// HTML Reporter use			assertions: this.assertions,			testId: this.testId,			// Source of Test			source: this.stack,			// DEPRECATED: this property will be removed in 2.0.0, use runtime instead			duration: this.runtime		} );		// QUnit.reset() is deprecated and will be replaced for a new		// fixture reset function on QUnit 2.0/2.1.		// It's still called here for backwards compatibility handling		QUnit.reset();		config.current = undefined;	},	queue: function() {		var priority,			test = this;		if ( !this.valid() ) {			return;		}		function run() {			// Each of these can by async			synchronize( [				function() {					test.before();				},				test.hooks( "beforeEach" ),				function() {					test.run();				},				test.hooks( "afterEach" ).reverse(),				function() {					test.after();				},				function() {					test.finish();				}			] );		}		// Prioritize previously failed tests, detected from sessionStorage		priority = QUnit.config.reorder && defined.sessionStorage &&				+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );		return synchronize( run, priority, config.seed );	},	pushResult: function( resultInfo ) {		// Destructure of resultInfo = { result, actual, expected, message, negative }		var source,			details = {				module: this.module.name,				name: this.testName,				result: resultInfo.result,				message: resultInfo.message,				actual: resultInfo.actual,				expected: resultInfo.expected,				testId: this.testId,				negative: resultInfo.negative || false,				runtime: now() - this.started			};		if ( !resultInfo.result ) {			source = sourceFromStacktrace();			if ( source ) {				details.source = source;			}		}		runLoggingCallbacks( "log", details );		this.assertions.push( {			result: !!resultInfo.result,			message: resultInfo.message		} );	},	pushFailure: function( message, source, actual ) {		if ( !( this instanceof Test ) ) {			throw new Error( "pushFailure() assertion outside test context, was " +				sourceFromStacktrace( 2 ) );		}		var details = {				module: this.module.name,				name: this.testName,				result: false,				message: message || "error",				actual: actual || null,				testId: this.testId,				runtime: now() - this.started			};		if ( source ) {			details.source = source;		}		runLoggingCallbacks( "log", details );		this.assertions.push( {			result: false,			message: message		} );	},	resolvePromise: function( promise, phase ) {		var then, message,			test = this;		if ( promise != null ) {			then = promise.then;			if ( QUnit.objectType( then ) === "function" ) {				QUnit.stop();				then.call(					promise,					function() { QUnit.start(); },					function( error ) {						message = "Promise rejected " +							( !phase ? "during" : phase.replace( /Each$/, "" ) ) +							" " + test.testName + ": " + ( error.message || error );						test.pushFailure( message, extractStacktrace( error, 0 ) );						// Else next test will carry the responsibility						saveGlobal();						// Unblock						QUnit.start();					}				);			}		}	},	valid: function() {		var filter = config.filter,			regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),			module = config.module && config.module.toLowerCase(),			fullName = ( this.module.name + ": " + this.testName );		function moduleChainNameMatch( testModule ) {			var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;			if ( testModuleName === module ) {				return true;			} else if ( testModule.parentModule ) {				return moduleChainNameMatch( testModule.parentModule );			} else {				return false;			}		}		function moduleChainIdMatch( testModule ) {			return inArray( testModule.moduleId, config.moduleId ) > -1 ||				testModule.parentModule && moduleChainIdMatch( testModule.parentModule );		}		// Internally-generated tests are always valid		if ( this.callback && this.callback.validTest ) {			return true;		}		if ( config.moduleId && config.moduleId.length > 0 &&			!moduleChainIdMatch( this.module ) ) {			return false;		}		if ( config.testId && config.testId.length > 0 &&			inArray( this.testId, config.testId ) < 0 ) {			return false;		}		if ( module && !moduleChainNameMatch( this.module ) ) {			return false;		}		if ( !filter ) {			return true;		}		return regexFilter ?			this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) :			this.stringFilter( filter, fullName );	},	regexFilter: function( exclude, pattern, flags, fullName ) {		var regex = new RegExp( pattern, flags );		var match = regex.test( fullName );		return match !== exclude;	},	stringFilter: function( filter, fullName ) {		filter = filter.toLowerCase();		fullName = fullName.toLowerCase();		var include = filter.charAt( 0 ) !== "!";		if ( !include ) {			filter = filter.slice( 1 );		}		// If the filter matches, we need to honour include		if ( fullName.indexOf( filter ) !== -1 ) {			return include;		}		// Otherwise, do the opposite		return !include;	}};// Resets the test setup. Useful for tests that modify the DOM./*DEPRECATED: Use multiple tests instead of resetting inside a test.Use testStart or testDone for custom cleanup.This method will throw an error in 2.0, and will be removed in 2.1*/QUnit.reset = function() {	// Return on non-browser environments	// This is necessary to not break on node tests	if ( !defined.document ) {		return;	}	var fixture = defined.document && document.getElementById &&			document.getElementById( "qunit-fixture" );	if ( fixture ) {		fixture.innerHTML = config.fixture;	}};QUnit.pushFailure = function() {	if ( !QUnit.config.current ) {		throw new Error( "pushFailure() assertion outside test context, in " +			sourceFromStacktrace( 2 ) );	}	// Gets current test obj	var currentTest = QUnit.config.current;	return currentTest.pushFailure.apply( currentTest, arguments );};// Based on Java's String.hashCode, a simple but not// rigorously collision resistant hashing functionfunction generateHash( module, testName ) {	var hex,		i = 0,		hash = 0,		str = module + "\x1C" + testName,		len = str.length;	for ( ; i < len; i++ ) {		hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );		hash |= 0;	}	// Convert the possibly negative integer hash code into an 8 character hex string, which isn't	// strictly necessary but increases user understanding that the id is a SHA-like hash	hex = ( 0x100000000 + hash ).toString( 16 );	if ( hex.length < 8 ) {		hex = "0000000" + hex;	}	return hex.slice( -8 );}function synchronize( callback, priority, seed ) {	var last = !priority,		index;	if ( QUnit.objectType( callback ) === "array" ) {		while ( callback.length ) {			synchronize( callback.shift() );		}		return;	}	if ( priority ) {		config.queue.splice( priorityCount++, 0, callback );	} else if ( seed ) {		if ( !unitSampler ) {			unitSampler = unitSamplerGenerator( seed );		}		// Insert into a random position after all priority items		index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );		config.queue.splice( priorityCount + index, 0, callback );	} else {		config.queue.push( callback );	}	if ( config.autorun && !config.blocking ) {		process( last );	}}function unitSamplerGenerator( seed ) {	// 32-bit xorshift, requires only a nonzero seed	// http://excamera.com/sphinx/article-xorshift.html	var sample = parseInt( generateHash( seed ), 16 ) || -1;	return function() {		sample ^= sample << 13;		sample ^= sample >>> 17;		sample ^= sample << 5;		// ECMAScript has no unsigned number type		if ( sample < 0 ) {			sample += 0x100000000;		}		return sample / 0x100000000;	};}function saveGlobal() {	config.pollution = [];	if ( config.noglobals ) {		for ( var key in global ) {			if ( hasOwn.call( global, key ) ) {				// In Opera sometimes DOM element ids show up here, ignore them				if ( /^qunit-test-output/.test( key ) ) {					continue;				}				config.pollution.push( key );			}		}	}}function checkPollution() {	var newGlobals,		deletedGlobals,		old = config.pollution;	saveGlobal();	newGlobals = diff( config.pollution, old );	if ( newGlobals.length > 0 ) {		QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );	}	deletedGlobals = diff( old, config.pollution );	if ( deletedGlobals.length > 0 ) {		QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );	}}// Will be exposed as QUnit.asyncTestfunction asyncTest( testName, expected, callback ) {	if ( arguments.length === 2 ) {		callback = expected;		expected = null;	}	QUnit.test( testName, expected, callback, true );}// Will be exposed as QUnit.testfunction test( testName, expected, callback, async ) {	if ( focused )  { return; }	var newTest;	if ( arguments.length === 2 ) {		callback = expected;		expected = null;	}	newTest = new Test( {		testName: testName,		expected: expected,		async: async,		callback: callback	} );	newTest.queue();}// Will be exposed as QUnit.skipfunction skip( testName ) {	if ( focused )  { return; }	var test = new Test( {		testName: testName,		skip: true	} );	test.queue();}// Will be exposed as QUnit.onlyfunction only( testName, expected, callback, async ) {	var newTest;	if ( focused )  { return; }	QUnit.config.queue.length = 0;	focused = true;	if ( arguments.length === 2 ) {		callback = expected;		expected = null;	}	newTest = new Test( {		testName: testName,		expected: expected,		async: async,		callback: callback	} );	newTest.queue();}function Assert( testContext ) {	this.test = testContext;}// Assert helpersQUnit.assert = Assert.prototype = {	// Specify the number of expected assertions to guarantee that failed test	// (no assertions are run at all) don't slip through.	expect: function( asserts ) {		if ( arguments.length === 1 ) {			this.test.expected = asserts;		} else {			return this.test.expected;		}	},	// Increment this Test's semaphore counter, then return a function that	// decrements that counter a maximum of once.	async: function( count ) {		var test = this.test,			popped = false,			acceptCallCount = count;		if ( typeof acceptCallCount === "undefined" ) {			acceptCallCount = 1;		}		test.semaphore += 1;		test.usedAsync = true;		pauseProcessing();		return function done() {			if ( popped ) {				test.pushFailure( "Too many calls to the `assert.async` callback",					sourceFromStacktrace( 2 ) );				return;			}			acceptCallCount -= 1;			if ( acceptCallCount > 0 ) {				return;			}			test.semaphore -= 1;			popped = true;			resumeProcessing();		};	},	// Exports test.push() to the user API	// Alias of pushResult.	push: function( result, actual, expected, message, negative ) {		var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert;		return currentAssert.pushResult( {			result: result,			actual: actual,			expected: expected,			message: message,			negative: negative		} );	},	pushResult: function( resultInfo ) {		// Destructure of resultInfo = { result, actual, expected, message, negative }		var assert = this,			currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;		// Backwards compatibility fix.		// Allows the direct use of global exported assertions and QUnit.assert.*		// Although, it's use is not recommended as it can leak assertions		// to other tests from async tests, because we only get a reference to the current test,		// not exactly the test where assertion were intended to be called.		if ( !currentTest ) {			throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );		}		if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {			currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",				sourceFromStacktrace( 2 ) );			// Allow this assertion to continue running anyway...		}		if ( !( assert instanceof Assert ) ) {			assert = currentTest.assert;		}		return assert.test.pushResult( resultInfo );	},	ok: function( result, message ) {		message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +			QUnit.dump.parse( result ) );		this.pushResult( {			result: !!result,			actual: result,			expected: true,			message: message		} );	},	notOk: function( result, message ) {		message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +			QUnit.dump.parse( result ) );		this.pushResult( {			result: !result,			actual: result,			expected: false,			message: message		} );	},	equal: function( actual, expected, message ) {		/*jshint eqeqeq:false */		this.pushResult( {			result: expected == actual,			actual: actual,			expected: expected,			message: message		} );	},	notEqual: function( actual, expected, message ) {		/*jshint eqeqeq:false */		this.pushResult( {			result: expected != actual,			actual: actual,			expected: expected,			message: message,			negative: true		} );	},	propEqual: function( actual, expected, message ) {		actual = objectValues( actual );		expected = objectValues( expected );		this.pushResult( {			result: QUnit.equiv( actual, expected ),			actual: actual,			expected: expected,			message: message		} );	},	notPropEqual: function( actual, expected, message ) {		actual = objectValues( actual );		expected = objectValues( expected );		this.pushResult( {			result: !QUnit.equiv( actual, expected ),			actual: actual,			expected: expected,			message: message,			negative: true		} );	},	deepEqual: function( actual, expected, message ) {		this.pushResult( {			result: QUnit.equiv( actual, expected ),			actual: actual,			expected: expected,			message: message		} );	},	notDeepEqual: function( actual, expected, message ) {		this.pushResult( {			result: !QUnit.equiv( actual, expected ),			actual: actual,			expected: expected,			message: message,			negative: true		} );	},	strictEqual: function( actual, expected, message ) {		this.pushResult( {			result: expected === actual,			actual: actual,			expected: expected,			message: message		} );	},	notStrictEqual: function( actual, expected, message ) {		this.pushResult( {			result: expected !== actual,			actual: actual,			expected: expected,			message: message,			negative: true		} );	},	"throws": function( block, expected, message ) {		var actual, expectedType,			expectedOutput = expected,			ok = false,			currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;		// 'expected' is optional unless doing string comparison		if ( message == null && typeof expected === "string" ) {			message = expected;			expected = null;		}		currentTest.ignoreGlobalErrors = true;		try {			block.call( currentTest.testEnvironment );		} catch ( e ) {			actual = e;		}		currentTest.ignoreGlobalErrors = false;		if ( actual ) {			expectedType = QUnit.objectType( expected );			// We don't want to validate thrown error			if ( !expected ) {				ok = true;				expectedOutput = null;			// Expected is a regexp			} else if ( expectedType === "regexp" ) {				ok = expected.test( errorString( actual ) );			// Expected is a string			} else if ( expectedType === "string" ) {				ok = expected === errorString( actual );			// Expected is a constructor, maybe an Error constructor			} else if ( expectedType === "function" && actual instanceof expected ) {				ok = true;			// Expected is an Error object			} else if ( expectedType === "object" ) {				ok = actual instanceof expected.constructor &&					actual.name === expected.name &&					actual.message === expected.message;			// Expected is a validation function which returns true if validation passed			} else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {				expectedOutput = null;				ok = true;			}		}		currentTest.assert.pushResult( {			result: ok,			actual: actual,			expected: expectedOutput,			message: message		} );	}};// Provide an alternative to assert.throws(), for environments that consider throws a reserved word// Known to us are: Closure Compiler, Narwhal( function() {	/*jshint sub:true */	Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation}() );function errorString( error ) {	var name, message,		resultErrorString = error.toString();	if ( resultErrorString.substring( 0, 7 ) === "[object" ) {		name = error.name ? error.name.toString() : "Error";		message = error.message ? error.message.toString() : "";		if ( name && message ) {			return name + ": " + message;		} else if ( name ) {			return name;		} else if ( message ) {			return message;		} else {			return "Error";		}	} else {		return resultErrorString;	}}// Test for equality any JavaScript type.// Author: Philippe Rathé <[email protected]>QUnit.equiv = ( function() {	// Stack to decide between skip/abort functions	var callers = [];	// Stack to avoiding loops from circular referencing	var parents = [];	var parentsB = [];	var getProto = Object.getPrototypeOf || function( obj ) {		/*jshint proto: true */		return obj.__proto__;	};	function useStrictEquality( b, a ) {		// To catch short annotation VS 'new' annotation of a declaration. e.g.:		// `var i = 1;`		// `var j = new Number(1);`		if ( typeof a === "object" ) {			a = a.valueOf();		}		if ( typeof b === "object" ) {			b = b.valueOf();		}		return a === b;	}	function compareConstructors( a, b ) {		var protoA = getProto( a );		var protoB = getProto( b );		// Comparing constructors is more strict than using `instanceof`		if ( a.constructor === b.constructor ) {			return true;		}		// Ref #851		// If the obj prototype descends from a null constructor, treat it		// as a null prototype.		if ( protoA && protoA.constructor === null ) {			protoA = null;		}		if ( protoB && protoB.constructor === null ) {			protoB = null;		}		// Allow objects with no prototype to be equivalent to		// objects with Object as their constructor.		if ( ( protoA === null && protoB === Object.prototype ) ||				( protoB === null && protoA === Object.prototype ) ) {			return true;		}		return false;	}	function getRegExpFlags( regexp ) {		return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];	}	var callbacks = {		"string": useStrictEquality,		"boolean": useStrictEquality,		"number": useStrictEquality,		"null": useStrictEquality,		"undefined": useStrictEquality,		"symbol": useStrictEquality,		"date": useStrictEquality,		"nan": function() {			return true;		},		"regexp": function( b, a ) {			return a.source === b.source &&				// Include flags in the comparison				getRegExpFlags( a ) === getRegExpFlags( b );		},		// - 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, len, loop, aCircular, bCircular;			len = a.length;			if ( len !== b.length ) {				// Safe and faster				return false;			}			// Track reference to avoid circular references			parents.push( a );			parentsB.push( b );			for ( i = 0; i < len; i++ ) {				loop = false;				for ( j = 0; j < parents.length; j++ ) {					aCircular = parents[ j ] === a[ i ];					bCircular = parentsB[ j ] === b[ i ];					if ( aCircular || bCircular ) {						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {							loop = true;						} else {							parents.pop();							parentsB.pop();							return false;						}					}				}				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {					parents.pop();					parentsB.pop();					return false;				}			}			parents.pop();			parentsB.pop();			return true;		},		"set": function( b, a ) {			var innerEq,				outerEq = true;			if ( a.size !== b.size ) {				return false;			}			a.forEach( function( aVal ) {				innerEq = false;				b.forEach( function( bVal ) {					if ( innerEquiv( bVal, aVal ) ) {						innerEq = true;					}				} );				if ( !innerEq ) {					outerEq = false;				}			} );			return outerEq;		},		"map": function( b, a ) {			var innerEq,				outerEq = true;			if ( a.size !== b.size ) {				return false;			}			a.forEach( function( aVal, aKey ) {				innerEq = false;				b.forEach( function( bVal, bKey ) {					if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {						innerEq = true;					}				} );				if ( !innerEq ) {					outerEq = false;				}			} );			return outerEq;		},		"object": function( b, a ) {			var i, j, loop, aCircular, bCircular;			// Default to true			var eq = true;			var aProperties = [];			var bProperties = [];			if ( compareConstructors( a, b ) === false ) {				return false;			}			// Stack constructor before traversing properties			callers.push( a.constructor );			// Track reference to avoid circular references			parents.push( a );			parentsB.push( b );			// Be strict: don't ensure hasOwnProperty and go deep			for ( i in a ) {				loop = false;				for ( j = 0; j < parents.length; j++ ) {					aCircular = parents[ j ] === a[ i ];					bCircular = parentsB[ j ] === b[ i ];					if ( aCircular || bCircular ) {						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {							loop = true;						} else {							eq = false;							break;						}					}				}				aProperties.push( i );				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {					eq = false;					break;				}			}			parents.pop();			parentsB.pop();			// Unstack, we are done			callers.pop();			for ( i in b ) {				// Collect b's properties				bProperties.push( i );			}			// Ensures identical properties name			return eq && innerEquiv( aProperties.sort(), bProperties.sort() );		}	};	function typeEquiv( a, b ) {		var type = QUnit.objectType( a );		return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );	}	// The real equiv function	function innerEquiv( a, b ) {		// We're done when there's nothing more to compare		if ( arguments.length < 2 ) {			return true;		}		// Require type-specific equality		return ( a === b || typeEquiv( a, b ) ) &&			// ...across all consecutive argument pairs			( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );	}	return innerEquiv;}() );// Based on jsDump by Ariel Flesler// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.htmlQUnit.dump = ( function() {	function quote( str ) {		return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\"";	}	function literal( o ) {		return o + "";	}	function join( pre, arr, post ) {		var s = dump.separator(),			base = dump.indent(),			inner = dump.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, stack ) {		var i = arr.length,			ret = new Array( i );		if ( dump.maxDepth && dump.depth > dump.maxDepth ) {			return "[object Array]";		}		this.up();		while ( i-- ) {			ret[ i ] = this.parse( arr[ i ], undefined, stack );		}		this.down();		return join( "[", ret, "]" );	}	var reName = /^function (\w+)/,		dump = {			// The objType is used mostly internally, you can fix a (custom) type in advance			parse: function( obj, objType, stack ) {				stack = stack || [];				var res, parser, parserType,					inStack = inArray( obj, stack );				if ( inStack !== -1 ) {					return "recursion(" + ( inStack - stack.length ) + ")";				}				objType = objType || this.typeOf( obj  );				parser = this.parsers[ objType ];				parserType = typeof parser;				if ( parserType === "function" ) {					stack.push( obj );					res = parser.call( this, obj, stack );					stack.pop();					return res;				}				return ( parserType === "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 !== undefined &&						obj.document !== undefined &&						obj.nodeType === undefined ) {					type = "window";				} else if ( obj.nodeType === 9 ) {					type = "document";				} else if ( obj.nodeType ) {					type = "node";				} else if (					// Native arrays					toString.call( obj ) === "[object Array]" ||					// NodeList objects					( typeof obj.length === "number" && obj.item !== undefined &&					( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&					obj[ 0 ] === undefined ) ) )				) {					type = "array";				} else if ( obj.constructor === Error.prototype.constructor ) {					type = "error";				} else {					type = typeof obj;				}				return type;			},			separator: function() {				return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";			},			// Extra can be a number, shortcut for increasing-calling-decreasing			indent: function( extra ) {				if ( !this.multiline ) {					return "";				}				var chr = this.indentChar;				if ( this.HTML ) {					chr = chr.replace( /\t/g, "   " ).replace( / /g, " " );				}				return new 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,			maxDepth: QUnit.config.maxDepth,			// This is the list of parsers, to modify them, use dump.setParser			parsers: {				window: "[Window]",				document: "[Document]",				error: function( error ) {					return "Error(\"" + error.message + "\")";				},				unknown: "[Unknown]",				"null": "null",				"undefined": "undefined",				"function": function( fn ) {					var ret = "function",						// Functions never have name in IE						name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];					if ( name ) {						ret += " " + name;					}					ret += "(";					ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );					return join( ret, dump.parse( fn, "functionCode" ), "}" );				},				array: array,				nodelist: array,				"arguments": array,				object: function( map, stack ) {					var keys, key, val, i, nonEnumerableProperties,						ret = [];					if ( dump.maxDepth && dump.depth > dump.maxDepth ) {						return "[object Object]";					}					dump.up();					keys = [];					for ( key in map ) {						keys.push( key );					}					// Some properties are not always enumerable on Error objects.					nonEnumerableProperties = [ "message", "name" ];					for ( i in nonEnumerableProperties ) {						key = nonEnumerableProperties[ i ];						if ( key in map && inArray( key, keys ) < 0 ) {							keys.push( key );						}					}					keys.sort();					for ( i = 0; i < keys.length; i++ ) {						key = keys[ i ];						val = map[ key ];						ret.push( dump.parse( key, "key" ) + ": " +							dump.parse( val, undefined, stack ) );					}					dump.down();					return join( "{", ret, "}" );				},				node: function( node ) {					var len, i, val,						open = dump.HTML ? "<" : "<",						close = dump.HTML ? ">" : ">",						tag = node.nodeName.toLowerCase(),						ret = open + tag,						attrs = node.attributes;					if ( attrs ) {						for ( i = 0, len = attrs.length; i < len; i++ ) {							val = attrs[ i ].nodeValue;							// IE6 includes all attributes in .attributes, even ones not explicitly							// set. Those have values like undefined, null, 0, false, "" or							// "inherit".							if ( val && val !== "inherit" ) {								ret += " " + attrs[ i ].nodeName + "=" +									dump.parse( val, "attribute" );							}						}					}					ret += close;					// Show content of TextNode or CDATASection					if ( node.nodeType === 3 || node.nodeType === 4 ) {						ret += node.nodeValue;					}					return ret + open + "/" + tag + close;				},				// Function calls it internally, it's the arguments part of the function				functionArgs: function( fn ) {					var args,						l = fn.length;					if ( !l ) {						return "";					}					args = new Array( l );					while ( l-- ) {						// 97 is 'a'						args[ l ] = String.fromCharCode( 97 + l );					}					return " " + args.join( ", " ) + " ";				},				// Object calls it internally, the key part of an item in a map				key: quote,				// Function calls it internally, it's the content of the function				functionCode: "[code]",				// Node calls it internally, it's a html attribute value				attribute: quote,				string: quote,				date: quote,				regexp: literal,				number: literal,				"boolean": literal			},			// If true, entities are escaped ( <, >, \t, space and \n )			HTML: false,			// Indentation unit			indentChar: "  ",			// If true, items in a collection, are separated by a \n, else just a space.			multiline: true		};	return dump;}() );// Back compatQUnit.jsDump = QUnit.dump;// Deprecated// Extend assert methods to QUnit for Backwards compatibility( function() {	var i,		assertions = Assert.prototype;	function applyCurrent( current ) {		return function() {			var assert = new Assert( QUnit.config.current );			current.apply( assert, arguments );		};	}	for ( i in assertions ) {		QUnit[ i ] = applyCurrent( assertions[ i ] );	}}() );// For browser, export only select globalsif ( defined.document ) {	( function() {		var i, l,			keys = [				"test",				"module",				"expect",				"asyncTest",				"start",				"stop",				"ok",				"notOk",				"equal",				"notEqual",				"propEqual",				"notPropEqual",				"deepEqual",				"notDeepEqual",				"strictEqual",				"notStrictEqual",				"throws",				"raises"			];		for ( i = 0, l = keys.length; i < l; i++ ) {			window[ keys[ i ] ] = QUnit[ keys[ i ] ];		}	}() );	window.QUnit = QUnit;}// For nodejsif ( typeof module !== "undefined" && module && module.exports ) {	module.exports = QUnit;	// For consistency with CommonJS environments' exports	module.exports.QUnit = QUnit;}// For CommonJS with exports, but without module.exports, like Rhinoif ( typeof exports !== "undefined" && exports ) {	exports.QUnit = QUnit;}if ( typeof define === "function" && define.amd ) {	define( function() {		return QUnit;	} );	QUnit.config.autostart = false;}// Get a reference to the global object, like window in browsers}( ( function() {	return this;}() ) ) );( function() {// Only interact with URLs via window.locationvar location = typeof window !== "undefined" && window.location;if ( !location ) {	return;}var urlParams = getUrlParams();QUnit.urlParams = urlParams;// Match module/test by inclusion in an arrayQUnit.config.moduleId = [].concat( urlParams.moduleId || [] );QUnit.config.testId = [].concat( urlParams.testId || [] );// Exact case-insensitive match of the module nameQUnit.config.module = urlParams.module;// Regular expression or case-insenstive substring match against "moduleName: testName"QUnit.config.filter = urlParams.filter;// Test order randomizationif ( urlParams.seed === true ) {	// Generate a random seed if the option is specified without a value	QUnit.config.seed = Math.random().toString( 36 ).slice( 2 );} else if ( urlParams.seed ) {	QUnit.config.seed = urlParams.seed;}// Add URL-parameter-mapped config values with UI form rendering dataQUnit.config.urlConfig.push(	{		id: "hidepassed",		label: "Hide passed tests",		tooltip: "Only show tests and assertions that fail. Stored as query-strings."	},	{		id: "noglobals",		label: "Check for Globals",		tooltip: "Enabling this will test if any test introduces new properties on the " +			"global object (`window` in Browsers). Stored as query-strings."	},	{		id: "notrycatch",		label: "No try-catch",		tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +			"exceptions in IE reasonable. Stored as query-strings."	});QUnit.begin( function() {	var i, option,		urlConfig = QUnit.config.urlConfig;	for ( i = 0; i < urlConfig.length; i++ ) {		// Options can be either strings or objects with nonempty "id" properties		option = QUnit.config.urlConfig[ i ];		if ( typeof option !== "string" ) {			option = option.id;		}		if ( QUnit.config[ option ] === undefined ) {			QUnit.config[ option ] = urlParams[ option ];		}	}} );function getUrlParams() {	var i, param, name, value;	var urlParams = {};	var params = location.search.slice( 1 ).split( "&" );	var length = params.length;	for ( i = 0; i < length; i++ ) {		if ( params[ i ] ) {			param = params[ i ].split( "=" );			name = decodeURIComponent( param[ 0 ] );			// Allow just a key to turn on a flag, e.g., test.html?noglobals			value = param.length === 1 ||				decodeURIComponent( param.slice( 1 ).join( "=" ) ) ;			if ( urlParams[ name ] ) {				urlParams[ name ] = [].concat( urlParams[ name ], value );			} else {				urlParams[ name ] = value;			}		}	}	return urlParams;}// Don't load the HTML Reporter on non-browser environmentsif ( typeof window === "undefined" || !window.document ) {	return;}// Deprecated QUnit.init - Ref #530// Re-initialize the configuration optionsQUnit.init = function() {	var config = QUnit.config;	config.stats = { all: 0, bad: 0 };	config.moduleStats = { all: 0, bad: 0 };	config.started = 0;	config.updateRate = 1000;	config.blocking = false;	config.autostart = true;	config.autorun = false;	config.filter = "";	config.queue = [];	appendInterface();};var config = QUnit.config,	document = window.document,	collapseNext = false,	hasOwn = Object.prototype.hasOwnProperty,	unfilteredUrl = setUrl( { filter: undefined, module: undefined,		moduleId: undefined, testId: undefined } ),	defined = {		sessionStorage: ( function() {			var x = "qunit-test-string";			try {				sessionStorage.setItem( x, x );				sessionStorage.removeItem( x );				return true;			} catch ( e ) {				return false;			}		}() )	},	modulesList = [];/*** Escape text for attribute or text content.*/function escapeText( s ) {	if ( !s ) {		return "";	}	s = s + "";	// Both single quotes and double quotes (for attributes)	return s.replace( /['"<>&]/g, function( s ) {		switch ( s ) {		case "'":			return "'";		case "\"":			return """;		case "<":			return "<";		case ">":			return ">";		case "&":			return "&";		}	} );}/** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */function addEvent( elem, type, fn ) {	if ( elem.addEventListener ) {		// Standards-based browsers		elem.addEventListener( type, fn, false );	} else if ( elem.attachEvent ) {		// Support: IE <9		elem.attachEvent( "on" + type, function() {			var event = window.event;			if ( !event.target ) {				event.target = event.srcElement || document;			}			fn.call( elem, event );		} );	}}/** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */function addEvents( elems, type, fn ) {	var i = elems.length;	while ( i-- ) {		addEvent( elems[ i ], type, fn );	}}function hasClass( elem, name ) {	return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;}function addClass( elem, name ) {	if ( !hasClass( elem, name ) ) {		elem.className += ( elem.className ? " " : "" ) + name;	}}function toggleClass( elem, name, force ) {	if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {		addClass( elem, name );	} else {		removeClass( elem, name );	}}function removeClass( elem, name ) {	var set = " " + elem.className + " ";	// Class name may appear multiple times	while ( set.indexOf( " " + name + " " ) >= 0 ) {		set = set.replace( " " + name + " ", " " );	}	// Trim for prettiness	elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );}function id( name ) {	return document.getElementById && document.getElementById( name );}function getUrlConfigHtml() {	var i, j, val,		escaped, escapedTooltip,		selection = false,		urlConfig = config.urlConfig,		urlConfigHtml = "";	for ( i = 0; i < urlConfig.length; i++ ) {		// Options can be either strings or objects with nonempty "id" properties		val = config.urlConfig[ i ];		if ( typeof val === "string" ) {			val = {				id: val,				label: val			};		}		escaped = escapeText( val.id );		escapedTooltip = escapeText( val.tooltip );		if ( !val.value || typeof val.value === "string" ) {			urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +				"' name='" + escaped + "' type='checkbox'" +				( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +				( config[ val.id ] ? " checked='checked'" : "" ) +				" title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +				"' title='" + escapedTooltip + "'>" + val.label + "</label>";		} else {			urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +				"' title='" + escapedTooltip + "'>" + val.label +				": </label><select id='qunit-urlconfig-" + escaped +				"' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";			if ( QUnit.is( "array", val.value ) ) {				for ( j = 0; j < val.value.length; j++ ) {					escaped = escapeText( val.value[ j ] );					urlConfigHtml += "<option value='" + escaped + "'" +						( config[ val.id ] === val.value[ j ] ?							( selection = true ) && " selected='selected'" : "" ) +						">" + escaped + "</option>";				}			} else {				for ( j in val.value ) {					if ( hasOwn.call( val.value, j ) ) {						urlConfigHtml += "<option value='" + escapeText( j ) + "'" +							( config[ val.id ] === j ?								( selection = true ) && " selected='selected'" : "" ) +							">" + escapeText( val.value[ j ] ) + "</option>";					}				}			}			if ( config[ val.id ] && !selection ) {				escaped = escapeText( config[ val.id ] );				urlConfigHtml += "<option value='" + escaped +					"' selected='selected' disabled='disabled'>" + escaped + "</option>";			}			urlConfigHtml += "</select>";		}	}	return urlConfigHtml;}// Handle "click" events on toolbar checkboxes and "change" for select menus.// Updates the URL with the new state of `config.urlConfig` values.function toolbarChanged() {	var updatedUrl, value, tests,		field = this,		params = {};	// Detect if field is a select menu or a checkbox	if ( "selectedIndex" in field ) {		value = field.options[ field.selectedIndex ].value || undefined;	} else {		value = field.checked ? ( field.defaultValue || true ) : undefined;	}	params[ field.name ] = value;	updatedUrl = setUrl( params );	// Check if we can apply the change without a page refresh	if ( "hidepassed" === field.name && "replaceState" in window.history ) {		QUnit.urlParams[ field.name ] = value;		config[ field.name ] = value || false;		tests = id( "qunit-tests" );		if ( tests ) {			toggleClass( tests, "hidepass", value || false );		}		window.history.replaceState( null, "", updatedUrl );	} else {		window.location = updatedUrl;	}}function setUrl( params ) {	var key, arrValue, i,		querystring = "?",		location = window.location;	params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );	for ( key in params ) {		// Skip inherited or undefined properties		if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {			// Output a parameter for each value of this key (but usually just one)			arrValue = [].concat( params[ key ] );			for ( i = 0; i < arrValue.length; i++ ) {				querystring += encodeURIComponent( key );				if ( arrValue[ i ] !== true ) {					querystring += "=" + encodeURIComponent( arrValue[ i ] );				}				querystring += "&";			}		}	}	return location.protocol + "//" + location.host +		location.pathname + querystring.slice( 0, -1 );}function applyUrlParams() {	var selectedModule,		modulesList = id( "qunit-modulefilter" ),		filter = id( "qunit-filter-input" ).value;	selectedModule = modulesList ?		decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :		undefined;	window.location = setUrl( {		module: ( selectedModule === "" ) ? undefined : selectedModule,		filter: ( filter === "" ) ? undefined : filter,		// Remove moduleId and testId filters		moduleId: undefined,		testId: undefined	} );}function toolbarUrlConfigContainer() {	var urlConfigContainer = document.createElement( "span" );	urlConfigContainer.innerHTML = getUrlConfigHtml();	addClass( urlConfigContainer, "qunit-url-config" );	// For oldIE support:	// * Add handlers to the individual elements instead of the container	// * Use "click" instead of "change" for checkboxes	addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );	addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );	return urlConfigContainer;}function toolbarLooseFilter() {	var filter = document.createElement( "form" ),		label = document.createElement( "label" ),		input = document.createElement( "input" ),		button = document.createElement( "button" );	addClass( filter, "qunit-filter" );	label.innerHTML = "Filter: ";	input.type = "text";	input.value = config.filter || "";	input.name = "filter";	input.id = "qunit-filter-input";	button.innerHTML = "Go";	label.appendChild( input );	filter.appendChild( label );	filter.appendChild( button );	addEvent( filter, "submit", function( ev ) {		applyUrlParams();		if ( ev && ev.preventDefault ) {			ev.preventDefault();		}		return false;	} );	return filter;}function toolbarModuleFilterHtml() {	var i,		moduleFilterHtml = "";	if ( !modulesList.length ) {		return false;	}	moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +		"<select id='qunit-modulefilter' name='modulefilter'><option value='' " +		( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +		">< All Modules ></option>";	for ( i = 0; i < modulesList.length; i++ ) {		moduleFilterHtml += "<option value='" +			escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +			( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +			">" + escapeText( modulesList[ i ] ) + "</option>";	}	moduleFilterHtml += "</select>";	return moduleFilterHtml;}function toolbarModuleFilter() {	var toolbar = id( "qunit-testrunner-toolbar" ),		moduleFilter = document.createElement( "span" ),		moduleFilterHtml = toolbarModuleFilterHtml();	if ( !toolbar || !moduleFilterHtml ) {		return false;	}	moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );	moduleFilter.innerHTML = moduleFilterHtml;	addEvent( moduleFilter.lastChild, "change", applyUrlParams );	toolbar.appendChild( moduleFilter );}function appendToolbar() {	var toolbar = id( "qunit-testrunner-toolbar" );	if ( toolbar ) {		toolbar.appendChild( toolbarUrlConfigContainer() );		toolbar.appendChild( toolbarLooseFilter() );		toolbarModuleFilter();	}}function appendHeader() {	var header = id( "qunit-header" );	if ( header ) {		header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +			"</a> ";	}}function appendBanner() {	var banner = id( "qunit-banner" );	if ( banner ) {		banner.className = "";	}}function appendTestResults() {	var tests = id( "qunit-tests" ),		result = id( "qunit-testresult" );	if ( result ) {		result.parentNode.removeChild( result );	}	if ( tests ) {		tests.innerHTML = "";		result = document.createElement( "p" );		result.id = "qunit-testresult";		result.className = "result";		tests.parentNode.insertBefore( result, tests );		result.innerHTML = "Running...<br /> ";	}}function storeFixture() {	var fixture = id( "qunit-fixture" );	if ( fixture ) {		config.fixture = fixture.innerHTML;	}}function appendFilteredTest() {	var testId = QUnit.config.testId;	if ( !testId || testId.length <= 0 ) {		return "";	}	return "<div id='qunit-filteredTest'>Rerunning selected tests: " +		escapeText( testId.join( ", " ) ) +		" <a id='qunit-clearFilter' href='" +		escapeText( unfilteredUrl ) +		"'>Run all tests</a></div>";}function appendUserAgent() {	var userAgent = id( "qunit-userAgent" );	if ( userAgent ) {		userAgent.innerHTML = "";		userAgent.appendChild(			document.createTextNode(				"QUnit " + QUnit.version + "; " + navigator.userAgent			)		);	}}function appendInterface() {	var qunit = id( "qunit" );	if ( qunit ) {		qunit.innerHTML =			"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +			"<h2 id='qunit-banner'></h2>" +			"<div id='qunit-testrunner-toolbar'></div>" +			appendFilteredTest() +			"<h2 id='qunit-userAgent'></h2>" +			"<ol id='qunit-tests'></ol>";	}	appendHeader();	appendBanner();	appendTestResults();	appendUserAgent();	appendToolbar();}function appendTestsList( modules ) {	var i, l, x, z, test, moduleObj;	for ( i = 0, l = modules.length; i < l; i++ ) {		moduleObj = modules[ i ];		for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {			test = moduleObj.tests[ x ];			appendTest( test.name, test.testId, moduleObj.name );		}	}}function appendTest( name, testId, moduleName ) {	var title, rerunTrigger, testBlock, assertList,		tests = id( "qunit-tests" );	if ( !tests ) {		return;	}	title = document.createElement( "strong" );	title.innerHTML = getNameHtml( name, moduleName );	rerunTrigger = document.createElement( "a" );	rerunTrigger.innerHTML = "Rerun";	rerunTrigger.href = setUrl( { testId: testId } );	testBlock = document.createElement( "li" );	testBlock.appendChild( title );	testBlock.appendChild( rerunTrigger );	testBlock.id = "qunit-test-output-" + testId;	assertList = document.createElement( "ol" );	assertList.className = "qunit-assert-list";	testBlock.appendChild( assertList );	tests.appendChild( testBlock );}// HTML Reporter initialization and loadQUnit.begin( function( details ) {	var i, moduleObj, tests;	// Sort modules by name for the picker	for ( i = 0; i < details.modules.length; i++ ) {		moduleObj = details.modules[ i ];		if ( moduleObj.name ) {			modulesList.push( moduleObj.name );		}	}	modulesList.sort( function( a, b ) {		return a.localeCompare( b );	} );	// Capture fixture HTML from the page	storeFixture();	// Initialize QUnit elements	appendInterface();	appendTestsList( details.modules );	tests = id( "qunit-tests" );	if ( tests && config.hidepassed ) {		addClass( tests, "hidepass" );	}} );QUnit.done( function( details ) {	var i, key,		banner = id( "qunit-banner" ),		tests = id( "qunit-tests" ),		html = [			"Tests completed in ",			details.runtime,			" milliseconds.<br />",			"<span class='passed'>",			details.passed,			"</span> assertions of <span class='total'>",			details.total,			"</span> passed, <span class='failed'>",			details.failed,			"</span> failed."		].join( "" );	if ( banner ) {		banner.className = details.failed ? "qunit-fail" : "qunit-pass";	}	if ( tests ) {		id( "qunit-testresult" ).innerHTML = html;	}	if ( config.altertitle && document.title ) {		// Show ✖ for good, ✔ for bad suite result in title		// use escape sequences in case file gets loaded with non-utf-8-charset		document.title = [			( details.failed ? "\u2716" : "\u2714" ),			document.title.replace( /^[\u2714\u2716] /i, "" )		].join( " " );	}	// Clear own sessionStorage items if all tests passed	if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {		for ( i = 0; i < sessionStorage.length; i++ ) {			key = sessionStorage.key( i++ );			if ( key.indexOf( "qunit-test-" ) === 0 ) {				sessionStorage.removeItem( key );			}		}	}	// Scroll back to top to show results	if ( config.scrolltop && window.scrollTo ) {		window.scrollTo( 0, 0 );	}} );function getNameHtml( name, module ) {	var nameHtml = "";	if ( module ) {		nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";	}	nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";	return nameHtml;}QUnit.testStart( function( details ) {	var running, testBlock, bad;	testBlock = id( "qunit-test-output-" + details.testId );	if ( testBlock ) {		testBlock.className = "running";	} else {		// Report later registered tests		appendTest( details.name, details.testId, details.module );	}	running = id( "qunit-testresult" );	if ( running ) {		bad = QUnit.config.reorder && defined.sessionStorage &&			+sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );		running.innerHTML = ( bad ?			"Rerunning previously failed test: <br />" :			"Running: <br />" ) +			getNameHtml( details.name, details.module );	}} );function stripHtml( string ) {	// Strip tags, html entity and whitespaces	return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\"/g, "" ).replace( /\s+/g, "" );}QUnit.log( function( details ) {	var assertList, assertLi,		message, expected, actual, diff,		showDiff = false,		testItem = id( "qunit-test-output-" + details.testId );	if ( !testItem ) {		return;	}	message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );	message = "<span class='test-message'>" + message + "</span>";	message += "<span class='runtime'>@ " + details.runtime + " ms</span>";	// The pushFailure doesn't provide details.expected	// when it calls, it's implicit to also not show expected and diff stuff	// Also, we need to check details.expected existence, as it can exist and be undefined	if ( !details.result && hasOwn.call( details, "expected" ) ) {		if ( details.negative ) {			expected = "NOT " + QUnit.dump.parse( details.expected );		} else {			expected = QUnit.dump.parse( details.expected );		}		actual = QUnit.dump.parse( details.actual );		message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +			escapeText( expected ) +			"</pre></td></tr>";		if ( actual !== expected ) {			message += "<tr class='test-actual'><th>Result: </th><td><pre>" +				escapeText( actual ) + "</pre></td></tr>";			// Don't show diff if actual or expected are booleans			if ( !( /^(true|false)$/.test( actual ) ) &&					!( /^(true|false)$/.test( expected ) ) ) {				diff = QUnit.diff( expected, actual );				showDiff = stripHtml( diff ).length !==					stripHtml( expected ).length +					stripHtml( actual ).length;			}			// Don't show diff if expected and actual are totally different			if ( showDiff ) {				message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +					diff + "</pre></td></tr>";			}		} else if ( expected.indexOf( "[object Array]" ) !== -1 ||				expected.indexOf( "[object Object]" ) !== -1 ) {			message += "<tr class='test-message'><th>Message: </th><td>" +				"Diff suppressed as the depth of object is more than current max depth (" +				QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +				" run with a higher max depth or <a href='" +				escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +				"Rerun</a> without max depth.</p></td></tr>";		} else {			message += "<tr class='test-message'><th>Message: </th><td>" +				"Diff suppressed as the expected and actual results have an equivalent" +				" serialization</td></tr>";		}		if ( details.source ) {			message += "<tr class='test-source'><th>Source: </th><td><pre>" +				escapeText( details.source ) + "</pre></td></tr>";		}		message += "</table>";	// This occurs when pushFailure is set and we have an extracted stack trace	} else if ( !details.result && details.source ) {		message += "<table>" +			"<tr class='test-source'><th>Source: </th><td><pre>" +			escapeText( details.source ) + "</pre></td></tr>" +			"</table>";	}	assertList = testItem.getElementsByTagName( "ol" )[ 0 ];	assertLi = document.createElement( "li" );	assertLi.className = details.result ? "pass" : "fail";	assertLi.innerHTML = message;	assertList.appendChild( assertLi );} );QUnit.testDone( function( details ) {	var testTitle, time, testItem, assertList,		good, bad, testCounts, skipped, sourceName,		tests = id( "qunit-tests" );	if ( !tests ) {		return;	}	testItem = id( "qunit-test-output-" + details.testId );	assertList = testItem.getElementsByTagName( "ol" )[ 0 ];	good = details.passed;	bad = details.failed;	// Store result when possible	if ( config.reorder && defined.sessionStorage ) {		if ( bad ) {			sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );		} else {			sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );		}	}	if ( bad === 0 ) {		// Collapse the passing tests		addClass( assertList, "qunit-collapsed" );	} else if ( bad && config.collapse && !collapseNext ) {		// Skip collapsing the first failing test		collapseNext = true;	} else {		// Collapse remaining tests		addClass( assertList, "qunit-collapsed" );	}	// The testItem.firstChild is the test name	testTitle = testItem.firstChild;	testCounts = bad ?		"<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :		"";	testTitle.innerHTML += " <b class='counts'>(" + testCounts +		details.assertions.length + ")</b>";	if ( details.skipped ) {		testItem.className = "skipped";		skipped = document.createElement( "em" );		skipped.className = "qunit-skipped-label";		skipped.innerHTML = "skipped";		testItem.insertBefore( skipped, testTitle );	} else {		addEvent( testTitle, "click", function() {			toggleClass( assertList, "qunit-collapsed" );		} );		testItem.className = bad ? "fail" : "pass";		time = document.createElement( "span" );		time.className = "runtime";		time.innerHTML = details.runtime + " ms";		testItem.insertBefore( time, assertList );	}	// Show the source of the test when showing assertions	if ( details.source ) {		sourceName = document.createElement( "p" );		sourceName.innerHTML = "<strong>Source: </strong>" + details.source;		addClass( sourceName, "qunit-source" );		if ( bad === 0 ) {			addClass( sourceName, "qunit-collapsed" );		}		addEvent( testTitle, "click", function() {			toggleClass( sourceName, "qunit-collapsed" );		} );		testItem.appendChild( sourceName );	}} );// Avoid readyState issue with phantomjs// Ref: #818var notPhantom = ( function( p ) {	return !( p && p.version && p.version.major > 0 );} )( window.phantom );if ( notPhantom && document.readyState === "complete" ) {	QUnit.load();} else {	addEvent( window, "load", QUnit.load );}/* * This file is a modified version of google-diff-match-patch's JavaScript implementation * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), * modifications are licensed as more fully set forth in LICENSE.txt. * * The original source of google-diff-match-patch is attributable and licensed as follows: * * Copyright 2006 Google Inc. * https://code.google.com/p/google-diff-match-patch/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * More Info: *  https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * */QUnit.diff = ( function() {	function DiffMatchPatch() {	}	//  DIFF FUNCTIONS	/**	 * The data structure representing a diff is an array of tuples:	 * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]	 * which means: delete 'Hello', add 'Goodbye' and keep ' world.'	 */	var DIFF_DELETE = -1,		DIFF_INSERT = 1,		DIFF_EQUAL = 0;	/**	 * Find the differences between two texts.  Simplifies the problem by stripping	 * any common prefix or suffix off the texts before diffing.	 * @param {string} text1 Old string to be diffed.	 * @param {string} text2 New string to be diffed.	 * @param {boolean=} optChecklines Optional speedup flag. If present and false,	 *     then don't run a line-level diff first to identify the changed areas.	 *     Defaults to true, which does a faster, slightly less optimal diff.	 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.	 */	DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) {		var deadline, checklines, commonlength,			commonprefix, commonsuffix, diffs;		// The diff must be complete in up to 1 second.		deadline = ( new Date() ).getTime() + 1000;		// Check for null inputs.		if ( text1 === null || text2 === null ) {			throw new Error( "Null input. (DiffMain)" );		}		// Check for equality (speedup).		if ( text1 === text2 ) {			if ( text1 ) {				return [					[ DIFF_EQUAL, text1 ]				];			}			return [];		}		if ( typeof optChecklines === "undefined" ) {			optChecklines = true;		}		checklines = optChecklines;		// Trim off common prefix (speedup).		commonlength = this.diffCommonPrefix( text1, text2 );		commonprefix = text1.substring( 0, commonlength );		text1 = text1.substring( commonlength );		text2 = text2.substring( commonlength );		// Trim off common suffix (speedup).		commonlength = this.diffCommonSuffix( text1, text2 );		commonsuffix = text1.substring( text1.length - commonlength );		text1 = text1.substring( 0, text1.length - commonlength );		text2 = text2.substring( 0, text2.length - commonlength );		// Compute the diff on the middle block.		diffs = this.diffCompute( text1, text2, checklines, deadline );		// Restore the prefix and suffix.		if ( commonprefix ) {			diffs.unshift( [ DIFF_EQUAL, commonprefix ] );		}		if ( commonsuffix ) {			diffs.push( [ DIFF_EQUAL, commonsuffix ] );		}		this.diffCleanupMerge( diffs );		return diffs;	};	/**	 * Reduce the number of edits by eliminating operationally trivial equalities.	 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.	 */	DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {		var changes, equalities, equalitiesLength, lastequality,			pointer, preIns, preDel, postIns, postDel;		changes = false;		equalities = []; // Stack of indices where equalities are found.		equalitiesLength = 0; // Keeping our own length var is faster in JS.		/** @type {?string} */		lastequality = null;		// Always equal to diffs[equalities[equalitiesLength - 1]][1]		pointer = 0; // Index of current position.		// Is there an insertion operation before the last equality.		preIns = false;		// Is there a deletion operation before the last equality.		preDel = false;		// Is there an insertion operation after the last equality.		postIns = false;		// Is there a deletion operation after the last equality.		postDel = false;		while ( pointer < diffs.length ) {			// Equality found.			if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) {				if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) {					// Candidate found.					equalities[ equalitiesLength++ ] = pointer;					preIns = postIns;					preDel = postDel;					lastequality = diffs[ pointer ][ 1 ];				} else {					// Not a candidate, and can never become one.					equalitiesLength = 0;					lastequality = null;				}				postIns = postDel = false;			// An insertion or deletion.			} else {				if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {					postDel = true;				} else {					postIns = true;				}				/*				 * Five types to be split:				 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>				 * <ins>A</ins>X<ins>C</ins><del>D</del>				 * <ins>A</ins><del>B</del>X<ins>C</ins>				 * <ins>A</del>X<ins>C</ins><del>D</del>				 * <ins>A</ins><del>B</del>X<del>C</del>				 */				if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||						( ( lastequality.length < 2 ) &&						( preIns + preDel + postIns + postDel ) === 3 ) ) ) {					// Duplicate record.					diffs.splice(						equalities[ equalitiesLength - 1 ],						0,						[ DIFF_DELETE, lastequality ]					);					// Change second copy to insert.					diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;					equalitiesLength--; // Throw away the equality we just deleted;					lastequality = null;					if ( preIns && preDel ) {						// No changes made which could affect previous entry, keep going.						postIns = postDel = true;						equalitiesLength = 0;					} else {						equalitiesLength--; // Throw away the previous equality.						pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;						postIns = postDel = false;					}					changes = true;				}			}			pointer++;		}		if ( changes ) {			this.diffCleanupMerge( diffs );		}	};	/**	 * Convert a diff array into a pretty HTML report.	 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.	 * @param {integer} string to be beautified.	 * @return {string} HTML representation.	 */	DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {		var op, data, x,			html = [];		for ( x = 0; x < diffs.length; x++ ) {			op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal)			data = diffs[ x ][ 1 ]; // Text of change.			switch ( op ) {			case DIFF_INSERT:				html[ x ] = "<ins>" + escapeText( data ) + "</ins>";				break;			case DIFF_DELETE:				html[ x ] = "<del>" + escapeText( data ) + "</del>";				break;			case DIFF_EQUAL:				html[ x ] = "<span>" + escapeText( data ) + "</span>";				break;			}		}		return html.join( "" );	};	/**	 * Determine the common prefix of two strings.	 * @param {string} text1 First string.	 * @param {string} text2 Second string.	 * @return {number} The number of characters common to the start of each	 *     string.	 */	DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {		var pointermid, pointermax, pointermin, pointerstart;		// Quick check for common null cases.		if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {			return 0;		}		// Binary search.		// Performance analysis: https://neil.fraser.name/news/2007/10/09/		pointermin = 0;		pointermax = Math.min( text1.length, text2.length );		pointermid = pointermax;		pointerstart = 0;		while ( pointermin < pointermid ) {			if ( text1.substring( pointerstart, pointermid ) ===					text2.substring( pointerstart, pointermid ) ) {				pointermin = pointermid;				pointerstart = pointermin;			} else {				pointermax = pointermid;			}			pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );		}		return pointermid;	};	/**	 * Determine the common suffix of two strings.	 * @param {string} text1 First string.	 * @param {string} text2 Second string.	 * @return {number} The number of characters common to the end of each string.	 */	DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {		var pointermid, pointermax, pointermin, pointerend;		// Quick check for common null cases.		if ( !text1 ||				!text2 ||				text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {			return 0;		}		// Binary search.		// Performance analysis: https://neil.fraser.name/news/2007/10/09/		pointermin = 0;		pointermax = Math.min( text1.length, text2.length );		pointermid = pointermax;		pointerend = 0;		while ( pointermin < pointermid ) {			if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===					text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {				pointermin = pointermid;				pointerend = pointermin;			} else {				pointermax = pointermid;			}			pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );		}		return pointermid;	};	/**	 * Find the differences between two texts.  Assumes that the texts do not	 * have any common prefix or suffix.	 * @param {string} text1 Old string to be diffed.	 * @param {string} text2 New string to be diffed.	 * @param {boolean} checklines Speedup flag.  If false, then don't run a	 *     line-level diff first to identify the changed areas.	 *     If true, then run a faster, slightly less optimal diff.	 * @param {number} deadline Time when the diff should be complete by.	 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.	 * @private	 */	DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {		var diffs, longtext, shorttext, i, hm,			text1A, text2A, text1B, text2B,			midCommon, diffsA, diffsB;		if ( !text1 ) {			// Just add some text (speedup).			return [				[ DIFF_INSERT, text2 ]			];		}		if ( !text2 ) {			// Just delete some text (speedup).			return [				[ DIFF_DELETE, text1 ]			];		}		longtext = text1.length > text2.length ? text1 : text2;		shorttext = text1.length > text2.length ? text2 : text1;		i = longtext.indexOf( shorttext );		if ( i !== -1 ) {			// Shorter text is inside the longer text (speedup).			diffs = [				[ DIFF_INSERT, longtext.substring( 0, i ) ],				[ DIFF_EQUAL, shorttext ],				[ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]			];			// Swap insertions for deletions if diff is reversed.			if ( text1.length > text2.length ) {				diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;			}			return diffs;		}		if ( shorttext.length === 1 ) {			// Single character string.			// After the previous speedup, the character can't be an equality.			return [				[ DIFF_DELETE, text1 ],				[ DIFF_INSERT, text2 ]			];		}		// Check to see if the problem can be split in two.		hm = this.diffHalfMatch( text1, text2 );		if ( hm ) {			// A half-match was found, sort out the return data.			text1A = hm[ 0 ];			text1B = hm[ 1 ];			text2A = hm[ 2 ];			text2B = hm[ 3 ];			midCommon = hm[ 4 ];			// Send both pairs off for separate processing.			diffsA = this.DiffMain( text1A, text2A, checklines, deadline );			diffsB = this.DiffMain( text1B, text2B, checklines, deadline );			// Merge the results.			return diffsA.concat( [				[ DIFF_EQUAL, midCommon ]			], diffsB );		}		if ( checklines && text1.length > 100 && text2.length > 100 ) {			return this.diffLineMode( text1, text2, deadline );		}		return this.diffBisect( text1, text2, deadline );	};	/**	 * Do the two texts share a substring which is at least half the length of the	 * longer text?	 * This speedup can produce non-minimal diffs.	 * @param {string} text1 First string.	 * @param {string} text2 Second string.	 * @return {Array.<string>} Five element Array, containing the prefix of	 *     text1, the suffix of text1, the prefix of text2, the suffix of	 *     text2 and the common middle.  Or null if there was no match.	 * @private	 */	DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) {		var longtext, shorttext, dmp,			text1A, text2B, text2A, text1B, midCommon,			hm1, hm2, hm;		longtext = text1.length > text2.length ? text1 : text2;		shorttext = text1.length > text2.length ? text2 : text1;		if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) {			return null; // Pointless.		}		dmp = this; // 'this' becomes 'window' in a closure.		/**		 * Does a substring of shorttext exist within longtext such that the substring		 * is at least half the length of longtext?		 * Closure, but does not reference any external variables.		 * @param {string} longtext Longer string.		 * @param {string} shorttext Shorter string.		 * @param {number} i Start index of quarter length substring within longtext.		 * @return {Array.<string>} Five element Array, containing the prefix of		 *     longtext, the suffix of longtext, the prefix of shorttext, the suffix		 *     of shorttext and the common middle.  Or null if there was no match.		 * @private		 */		function diffHalfMatchI( longtext, shorttext, i ) {			var seed, j, bestCommon, prefixLength, suffixLength,				bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;			// Start with a 1/4 length substring at position i as a seed.			seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );			j = -1;			bestCommon = "";			while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) {				prefixLength = dmp.diffCommonPrefix( longtext.substring( i ),					shorttext.substring( j ) );				suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ),					shorttext.substring( 0, j ) );				if ( bestCommon.length < suffixLength + prefixLength ) {					bestCommon = shorttext.substring( j - suffixLength, j ) +						shorttext.substring( j, j + prefixLength );					bestLongtextA = longtext.substring( 0, i - suffixLength );					bestLongtextB = longtext.substring( i + prefixLength );					bestShorttextA = shorttext.substring( 0, j - suffixLength );					bestShorttextB = shorttext.substring( j + prefixLength );				}			}			if ( bestCommon.length * 2 >= longtext.length ) {				return [ bestLongtextA, bestLongtextB,					bestShorttextA, bestShorttextB, bestCommon				];			} else {				return null;			}		}		// First check if the second quarter is the seed for a half-match.		hm1 = diffHalfMatchI( longtext, shorttext,			Math.ceil( longtext.length / 4 ) );		// Check again based on the third quarter.		hm2 = diffHalfMatchI( longtext, shorttext,			Math.ceil( longtext.length / 2 ) );		if ( !hm1 && !hm2 ) {			return null;		} else if ( !hm2 ) {			hm = hm1;		} else if ( !hm1 ) {			hm = hm2;		} else {			// Both matched.  Select the longest.			hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;		}		// A half-match was found, sort out the return data.		text1A, text1B, text2A, text2B;		if ( text1.length > text2.length ) {			text1A = hm[ 0 ];			text1B = hm[ 1 ];			text2A = hm[ 2 ];			text2B = hm[ 3 ];		} else {			text2A = hm[ 0 ];			text2B = hm[ 1 ];			text1A = hm[ 2 ];			text1B = hm[ 3 ];		}		midCommon = hm[ 4 ];		return [ text1A, text1B, text2A, text2B, midCommon ];	};	/**	 * Do a quick line-level diff on both strings, then rediff the parts for	 * greater accuracy.	 * This speedup can produce non-minimal diffs.	 * @param {string} text1 Old string to be diffed.	 * @param {string} text2 New string to be diffed.	 * @param {number} deadline Time when the diff should be complete by.	 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.	 * @private	 */	DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {		var a, diffs, linearray, pointer, countInsert,			countDelete, textInsert, textDelete, j;		// Scan the text on a line-by-line basis first.		a = this.diffLinesToChars( text1, text2 );		text1 = a.chars1;		text2 = a.chars2;		linearray = a.lineArray;		diffs = this.DiffMain( text1, text2, false, deadline );		// Convert the diff back to original text.		this.diffCharsToLines( diffs, linearray );		// Eliminate freak matches (e.g. blank lines)		this.diffCleanupSemantic( diffs );		// Rediff any replacement blocks, this time character-by-character.		// Add a dummy entry at the end.		diffs.push( [ DIFF_EQUAL, "" ] );		pointer = 0;		countDelete = 0;		countInsert = 0;		textDelete = "";		textInsert = "";		while ( pointer < diffs.length ) {			switch ( diffs[ pointer ][ 0 ] ) {			case DIFF_INSERT:				countInsert++;				textInsert += diffs[ pointer ][ 1 ];				break;			case DIFF_DELETE:				countDelete++;				textDelete += diffs[ pointer ][ 1 ];				break;			case DIFF_EQUAL:				// Upon reaching an equality, check for prior redundancies.				if ( countDelete >= 1 && countInsert >= 1 ) {					// Delete the offending records and add the merged ones.					diffs.splice( pointer - countDelete - countInsert,						countDelete + countInsert );					pointer = pointer - countDelete - countInsert;					a = this.DiffMain( textDelete, textInsert, false, deadline );					for ( j = a.length - 1; j >= 0; j-- ) {						diffs.splice( pointer, 0, a[ j ] );					}					pointer = pointer + a.length;				}				countInsert = 0;				countDelete = 0;				textDelete = "";				textInsert = "";				break;			}			pointer++;		}		diffs.pop(); // Remove the dummy entry at the end.		return diffs;	};	/**	 * Find the 'middle snake' of a diff, split the problem in two	 * and return the recursively constructed diff.	 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.	 * @param {string} text1 Old string to be diffed.	 * @param {string} text2 New string to be diffed.	 * @param {number} deadline Time at which to bail if not yet complete.	 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.	 * @private	 */	DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) {		var text1Length, text2Length, maxD, vOffset, vLength,			v1, v2, x, delta, front, k1start, k1end, k2start,			k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;		// Cache the text lengths to prevent multiple calls.		text1Length = text1.length;		text2Length = text2.length;		maxD = Math.ceil( ( text1Length + text2Length ) / 2 );		vOffset = maxD;		vLength = 2 * maxD;		v1 = new Array( vLength );		v2 = new Array( vLength );		// Setting all elements to -1 is faster in Chrome & Firefox than mixing		// integers and undefined.		for ( x = 0; x < vLength; x++ ) {			v1[ x ] = -1;			v2[ x ] = -1;		}		v1[ vOffset + 1 ] = 0;		v2[ vOffset + 1 ] = 0;		delta = text1Length - text2Length;		// If the total number of characters is odd, then the front path will collide		// with the reverse path.		front = ( delta % 2 !== 0 );		// Offsets for start and end of k loop.		// Prevents mapping of space beyond the grid.		k1start = 0;		k1end = 0;		k2start = 0;		k2end = 0;		for ( d = 0; d < maxD; d++ ) {			// Bail out if deadline is reached.			if ( ( new Date() ).getTime() > deadline ) {				break;			}			// Walk the front path one step.			for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {				k1Offset = vOffset + k1;				if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {					x1 = v1[ k1Offset + 1 ];				} else {					x1 = v1[ k1Offset - 1 ] + 1;				}				y1 = x1 - k1;				while ( x1 < text1Length && y1 < text2Length &&					text1.charAt( x1 ) === text2.charAt( y1 ) ) {					x1++;					y1++;				}				v1[ k1Offset ] = x1;				if ( x1 > text1Length ) {					// Ran off the right of the graph.					k1end += 2;				} else if ( y1 > text2Length ) {					// Ran off the bottom of the graph.					k1start += 2;				} else if ( front ) {					k2Offset = vOffset + delta - k1;					if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {						// Mirror x2 onto top-left coordinate system.						x2 = text1Length - v2[ k2Offset ];						if ( x1 >= x2 ) {							// Overlap detected.							return this.diffBisectSplit( text1, text2, x1, y1, deadline );						}					}				}			}			// Walk the reverse path one step.			for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) {				k2Offset = vOffset + k2;				if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {					x2 = v2[ k2Offset + 1 ];				} else {					x2 = v2[ k2Offset - 1 ] + 1;				}				y2 = x2 - k2;				while ( x2 < text1Length && y2 < text2Length &&					text1.charAt( text1Length - x2 - 1 ) ===					text2.charAt( text2Length - y2 - 1 ) ) {					x2++;					y2++;				}				v2[ k2Offset ] = x2;				if ( x2 > text1Length ) {					// Ran off the left of the graph.					k2end += 2;				} else if ( y2 > text2Length ) {					// Ran off the top of the graph.					k2start += 2;				} else if ( !front ) {					k1Offset = vOffset + delta - k2;					if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {						x1 = v1[ k1Offset ];						y1 = vOffset + x1 - k1Offset;						// Mirror x2 onto top-left coordinate system.						x2 = text1Length - x2;						if ( x1 >= x2 ) {							// Overlap detected.							return this.diffBisectSplit( text1, text2, x1, y1, deadline );						}					}				}			}		}		// Diff took too long and hit the deadline or		// number of diffs equals number of characters, no commonality at all.		return [			[ DIFF_DELETE, text1 ],			[ DIFF_INSERT, text2 ]		];	};	/**	 * Given the location of the 'middle snake', split the diff in two parts	 * and recurse.	 * @param {string} text1 Old string to be diffed.	 * @param {string} text2 New string to be diffed.	 * @param {number} x Index of split point in text1.	 * @param {number} y Index of split point in text2.	 * @param {number} deadline Time at which to bail if not yet complete.	 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.	 * @private	 */	DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {		var text1a, text1b, text2a, text2b, diffs, diffsb;		text1a = text1.substring( 0, x );		text2a = text2.substring( 0, y );		text1b = text1.substring( x );		text2b = text2.substring( y );		// Compute both diffs serially.		diffs = this.DiffMain( text1a, text2a, false, deadline );		diffsb = this.DiffMain( text1b, text2b, false, deadline );		return diffs.concat( diffsb );	};	/**	 * Reduce the number of edits by eliminating semantically trivial equalities.	 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.	 */	DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) {		var changes, equalities, equalitiesLength, lastequality,			pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,			lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;		changes = false;		equalities = []; // Stack of indices where equalities are found.		equalitiesLength = 0; // Keeping our own length var is faster in JS.		/** @type {?string} */		lastequality = null;		// Always equal to diffs[equalities[equalitiesLength - 1]][1]		pointer = 0; // Index of current position.		// Number of characters that changed prior to the equality.		lengthInsertions1 = 0;		lengthDeletions1 = 0;		// Number of characters that changed after the equality.		lengthInsertions2 = 0;		lengthDeletions2 = 0;		while ( pointer < diffs.length ) {			if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.				equalities[ equalitiesLength++ ] = pointer;				lengthInsertions1 = lengthInsertions2;				lengthDeletions1 = lengthDeletions2;				lengthInsertions2 = 0;				lengthDeletions2 = 0;				lastequality = diffs[ pointer ][ 1 ];			} else { // An insertion or deletion.				if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) {					lengthInsertions2 += diffs[ pointer ][ 1 ].length;				} else {					lengthDeletions2 += diffs[ pointer ][ 1 ].length;				}				// Eliminate an equality that is smaller or equal to the edits on both				// sides of it.				if ( lastequality && ( lastequality.length <=						Math.max( lengthInsertions1, lengthDeletions1 ) ) &&						( lastequality.length <= Math.max( lengthInsertions2,							lengthDeletions2 ) ) ) {					// Duplicate record.					diffs.splice(						equalities[ equalitiesLength - 1 ],						0,						[ DIFF_DELETE, lastequality ]					);					// Change second copy to insert.					diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;					// Throw away the equality we just deleted.					equalitiesLength--;					// Throw away the previous equality (it needs to be reevaluated).					equalitiesLength--;					pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;					// Reset the counters.					lengthInsertions1 = 0;					lengthDeletions1 = 0;					lengthInsertions2 = 0;					lengthDeletions2 = 0;					lastequality = null;					changes = true;				}			}			pointer++;		}		// Normalize the diff.		if ( changes ) {			this.diffCleanupMerge( diffs );		}		// Find any overlaps between deletions and insertions.		// e.g: <del>abcxxx</del><ins>xxxdef</ins>		//   -> <del>abc</del>xxx<ins>def</ins>		// e.g: <del>xxxabc</del><ins>defxxx</ins>		//   -> <ins>def</ins>xxx<del>abc</del>		// Only extract an overlap if it is as big as the edit ahead or behind it.		pointer = 1;		while ( pointer < diffs.length ) {			if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE &&					diffs[ pointer ][ 0 ] === DIFF_INSERT ) {				deletion = diffs[ pointer - 1 ][ 1 ];				insertion = diffs[ pointer ][ 1 ];				overlapLength1 = this.diffCommonOverlap( deletion, insertion );				overlapLength2 = this.diffCommonOverlap( insertion, deletion );				if ( overlapLength1 >= overlapLength2 ) {					if ( overlapLength1 >= deletion.length / 2 ||							overlapLength1 >= insertion.length / 2 ) {						// Overlap found.  Insert an equality and trim the surrounding edits.						diffs.splice(							pointer,							0,							[ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ]						);						diffs[ pointer - 1 ][ 1 ] =							deletion.substring( 0, deletion.length - overlapLength1 );						diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 );						pointer++;					}				} else {					if ( overlapLength2 >= deletion.length / 2 ||							overlapLength2 >= insertion.length / 2 ) {						// Reverse overlap found.						// Insert an equality and swap and trim the surrounding edits.						diffs.splice(							pointer,							0,							[ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]						);						diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT;						diffs[ pointer - 1 ][ 1 ] =							insertion.substring( 0, insertion.length - overlapLength2 );						diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE;						diffs[ pointer + 1 ][ 1 ] =							deletion.substring( overlapLength2 );						pointer++;					}				}				pointer++;			}			pointer++;		}	};	/**	 * Determine if the suffix of one string is the prefix of another.	 * @param {string} text1 First string.	 * @param {string} text2 Second string.	 * @return {number} The number of characters common to the end of the first	 *     string and the start of the second string.	 * @private	 */	DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {		var text1Length, text2Length, textLength,			best, length, pattern, found;		// Cache the text lengths to prevent multiple calls.		text1Length = text1.length;		text2Length = text2.length;		// Eliminate the null case.		if ( text1Length === 0 || text2Length === 0 ) {			return 0;		}		// Truncate the longer string.		if ( text1Length > text2Length ) {			text1 = text1.substring( text1Length - text2Length );		} else if ( text1Length < text2Length ) {			text2 = text2.substring( 0, text1Length );		}		textLength = Math.min( text1Length, text2Length );		// Quick check for the worst case.		if ( text1 === text2 ) {			return textLength;		}		// Start by looking for a single character match		// and increase length until no match is found.		// Performance analysis: https://neil.fraser.name/news/2010/11/04/		best = 0;		length = 1;		while ( true ) {			pattern = text1.substring( textLength - length );			found = text2.indexOf( pattern );			if ( found === -1 ) {				return best;			}			length += found;			if ( found === 0 || text1.substring( textLength - length ) ===					text2.substring( 0, length ) ) {				best = length;				length++;			}		}	};	/**	 * Split two texts into an array of strings.  Reduce the texts to a string of	 * hashes where each Unicode character represents one line.	 * @param {string} text1 First string.	 * @param {string} text2 Second string.	 * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}	 *     An object containing the encoded text1, the encoded text2 and	 *     the array of unique strings.	 *     The zeroth element of the array of unique strings is intentionally blank.	 * @private	 */	DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {		var lineArray, lineHash, chars1, chars2;		lineArray = []; // E.g. lineArray[4] === 'Hello\n'		lineHash = {};  // E.g. lineHash['Hello\n'] === 4		// '\x00' is a valid character, but various debuggers don't like it.		// So we'll insert a junk entry to avoid generating a null character.		lineArray[ 0 ] = "";		/**		 * Split a text into an array of strings.  Reduce the texts to a string of		 * hashes where each Unicode character represents one line.		 * Modifies linearray and linehash through being a closure.		 * @param {string} text String to encode.		 * @return {string} Encoded string.		 * @private		 */		function diffLinesToCharsMunge( text ) {			var chars, lineStart, lineEnd, lineArrayLength, line;			chars = "";			// Walk the text, pulling out a substring for each line.			// text.split('\n') would would temporarily double our memory footprint.			// Modifying text would create many large strings to garbage collect.			lineStart = 0;			lineEnd = -1;			// Keeping our own length variable is faster than looking it up.			lineArrayLength = lineArray.length;			while ( lineEnd < text.length - 1 ) {				lineEnd = text.indexOf( "\n", lineStart );				if ( lineEnd === -1 ) {					lineEnd = text.length - 1;				}				line = text.substring( lineStart, lineEnd + 1 );				lineStart = lineEnd + 1;				if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) :							( lineHash[ line ] !== undefined ) ) {					chars += String.fromCharCode( lineHash[ line ] );				} else {					chars += String.fromCharCode( lineArrayLength );					lineHash[ line ] = lineArrayLength;					lineArray[ lineArrayLength++ ] = line;				}			}			return chars;		}		chars1 = diffLinesToCharsMunge( text1 );		chars2 = diffLinesToCharsMunge( text2 );		return {			chars1: chars1,			chars2: chars2,			lineArray: lineArray		};	};	/**	 * Rehydrate the text in a diff from a string of line hashes to real lines of	 * text.	 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.	 * @param {!Array.<string>} lineArray Array of unique strings.	 * @private	 */	DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {		var x, chars, text, y;		for ( x = 0; x < diffs.length; x++ ) {			chars = diffs[ x ][ 1 ];			text = [];			for ( y = 0; y < chars.length; y++ ) {				text[ y ] = lineArray[ chars.charCodeAt( y ) ];			}			diffs[ x ][ 1 ] = text.join( "" );		}	};	/**	 * Reorder and merge like edit sections.  Merge equalities.	 * Any edit section can move as long as it doesn't cross an equality.	 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.	 */	DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {		var pointer, countDelete, countInsert, textInsert, textDelete,			commonlength, changes, diffPointer, position;		diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.		pointer = 0;		countDelete = 0;		countInsert = 0;		textDelete = "";		textInsert = "";		commonlength;		while ( pointer < diffs.length ) {			switch ( diffs[ pointer ][ 0 ] ) {			case DIFF_INSERT:				countInsert++;				textInsert += diffs[ pointer ][ 1 ];				pointer++;				break;			case DIFF_DELETE:				countDelete++;				textDelete += diffs[ pointer ][ 1 ];				pointer++;				break;			case DIFF_EQUAL:				// Upon reaching an equality, check for prior redundancies.				if ( countDelete + countInsert > 1 ) {					if ( countDelete !== 0 && countInsert !== 0 ) {						// Factor out any common prefixes.						commonlength = this.diffCommonPrefix( textInsert, textDelete );						if ( commonlength !== 0 ) {							if ( ( pointer - countDelete - countInsert ) > 0 &&									diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] ===									DIFF_EQUAL ) {								diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=									textInsert.substring( 0, commonlength );							} else {								diffs.splice( 0, 0, [ DIFF_EQUAL,									textInsert.substring( 0, commonlength )								] );								pointer++;							}							textInsert = textInsert.substring( commonlength );							textDelete = textDelete.substring( commonlength );						}						// Factor out any common suffixies.						commonlength = this.diffCommonSuffix( textInsert, textDelete );						if ( commonlength !== 0 ) {							diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length -									commonlength ) + diffs[ pointer ][ 1 ];							textInsert = textInsert.substring( 0, textInsert.length -								commonlength );							textDelete = textDelete.substring( 0, textDelete.length -								commonlength );						}					}					// Delete the offending records and add the merged ones.					if ( countDelete === 0 ) {						diffs.splice( pointer - countInsert,							countDelete + countInsert, [ DIFF_INSERT, textInsert ] );					} else if ( countInsert === 0 ) {						diffs.splice( pointer - countDelete,							countDelete + countInsert, [ DIFF_DELETE, textDelete ] );					} else {						diffs.splice(							pointer - countDelete - countInsert,							countDelete + countInsert,							[ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ]						);					}					pointer = pointer - countDelete - countInsert +						( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1;				} else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {					// Merge this equality with the previous one.					diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ];					diffs.splice( pointer, 1 );				} else {					pointer++;				}				countInsert = 0;				countDelete = 0;				textDelete = "";				textInsert = "";				break;			}		}		if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {			diffs.pop(); // Remove the dummy entry at the end.		}		// Second pass: look for single edits surrounded on both sides by equalities		// which can be shifted sideways to eliminate an equality.		// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC		changes = false;		pointer = 1;		// Intentionally ignore the first and last element (don't need checking).		while ( pointer < diffs.length - 1 ) {			if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&					diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {				diffPointer = diffs[ pointer ][ 1 ];				position = diffPointer.substring(					diffPointer.length - diffs[ pointer - 1 ][ 1 ].length				);				// This is a single edit surrounded by equalities.				if ( position === diffs[ pointer - 1 ][ 1 ] ) {					// Shift the edit over the previous equality.					diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +						diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -							diffs[ pointer - 1 ][ 1 ].length );					diffs[ pointer + 1 ][ 1 ] =						diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];					diffs.splice( pointer - 1, 1 );					changes = true;				} else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===						diffs[ pointer + 1 ][ 1 ] ) {					// Shift the edit over the next equality.					diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];					diffs[ pointer ][ 1 ] =						diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +						diffs[ pointer + 1 ][ 1 ];					diffs.splice( pointer + 1, 1 );					changes = true;				}			}			pointer++;		}		// If shifts were made, the diff needs reordering and another shift sweep.		if ( changes ) {			this.diffCleanupMerge( diffs );		}	};	return function( o, n ) {		var diff, output, text;		diff = new DiffMatchPatch();		output = diff.DiffMain( o, n );		diff.diffCleanupEfficiency( output );		text = diff.diffPrettyHtml( output );		return text;	};}() );}() );
 |