qunit.js 121 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349
  1. /*!
  2. * QUnit 2.1.0
  3. * https://qunitjs.com/
  4. *
  5. * Copyright jQuery Foundation and other contributors
  6. * Released under the MIT license
  7. * https://jquery.org/license
  8. *
  9. * Date: 2016-12-06T04:45Z
  10. */
  11. (function (global$1) {
  12. 'use strict';
  13. global$1 = 'default' in global$1 ? global$1['default'] : global$1;
  14. var window = global$1.window;
  15. var console = global$1.console;
  16. var setTimeout = global$1.setTimeout;
  17. var clearTimeout = global$1.clearTimeout;
  18. var document = window && window.document;
  19. var navigator = window && window.navigator;
  20. var sessionStorage = window && window.sessionStorage;
  21. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  22. return typeof obj;
  23. } : function (obj) {
  24. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  25. };
  26. var get$1 = function get$1(object, property, receiver) {
  27. if (object === null) object = Function.prototype;
  28. var desc = Object.getOwnPropertyDescriptor(object, property);
  29. if (desc === undefined) {
  30. var parent = Object.getPrototypeOf(object);
  31. if (parent === null) {
  32. return undefined;
  33. } else {
  34. return get$1(parent, property, receiver);
  35. }
  36. } else if ("value" in desc) {
  37. return desc.value;
  38. } else {
  39. var getter = desc.get;
  40. if (getter === undefined) {
  41. return undefined;
  42. }
  43. return getter.call(receiver);
  44. }
  45. };
  46. var set$1 = function set$1(object, property, value, receiver) {
  47. var desc = Object.getOwnPropertyDescriptor(object, property);
  48. if (desc === undefined) {
  49. var parent = Object.getPrototypeOf(object);
  50. if (parent !== null) {
  51. set$1(parent, property, value, receiver);
  52. }
  53. } else if ("value" in desc && desc.writable) {
  54. desc.value = value;
  55. } else {
  56. var setter = desc.set;
  57. if (setter !== undefined) {
  58. setter.call(receiver, value);
  59. }
  60. }
  61. return value;
  62. };
  63. var toConsumableArray = function (arr) {
  64. if (Array.isArray(arr)) {
  65. for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
  66. return arr2;
  67. } else {
  68. return Array.from(arr);
  69. }
  70. };
  71. var toString = Object.prototype.toString;
  72. var hasOwn = Object.prototype.hasOwnProperty;
  73. var now = Date.now || function () {
  74. return new Date().getTime();
  75. };
  76. var defined = {
  77. document: window && window.document !== undefined,
  78. setTimeout: setTimeout !== undefined
  79. };
  80. // Returns a new Array with the elements that are in a but not in b
  81. function diff(a, b) {
  82. var i,
  83. j,
  84. result = a.slice();
  85. for (i = 0; i < result.length; i++) {
  86. for (j = 0; j < b.length; j++) {
  87. if (result[i] === b[j]) {
  88. result.splice(i, 1);
  89. i--;
  90. break;
  91. }
  92. }
  93. }
  94. return result;
  95. }
  96. // From jquery.js
  97. function inArray(elem, array) {
  98. if (array.indexOf) {
  99. return array.indexOf(elem);
  100. }
  101. for (var i = 0, length = array.length; i < length; i++) {
  102. if (array[i] === elem) {
  103. return i;
  104. }
  105. }
  106. return -1;
  107. }
  108. /**
  109. * Makes a clone of an object using only Array or Object as base,
  110. * and copies over the own enumerable properties.
  111. *
  112. * @param {Object} obj
  113. * @return {Object} New object with only the own properties (recursively).
  114. */
  115. function objectValues(obj) {
  116. var key,
  117. val,
  118. vals = is("array", obj) ? [] : {};
  119. for (key in obj) {
  120. if (hasOwn.call(obj, key)) {
  121. val = obj[key];
  122. vals[key] = val === Object(val) ? objectValues(val) : val;
  123. }
  124. }
  125. return vals;
  126. }
  127. function extend(a, b, undefOnly) {
  128. for (var prop in b) {
  129. if (hasOwn.call(b, prop)) {
  130. if (b[prop] === undefined) {
  131. delete a[prop];
  132. } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
  133. a[prop] = b[prop];
  134. }
  135. }
  136. }
  137. return a;
  138. }
  139. function objectType(obj) {
  140. if (typeof obj === "undefined") {
  141. return "undefined";
  142. }
  143. // Consider: typeof null === object
  144. if (obj === null) {
  145. return "null";
  146. }
  147. var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
  148. type = match && match[1];
  149. switch (type) {
  150. case "Number":
  151. if (isNaN(obj)) {
  152. return "nan";
  153. }
  154. return "number";
  155. case "String":
  156. case "Boolean":
  157. case "Array":
  158. case "Set":
  159. case "Map":
  160. case "Date":
  161. case "RegExp":
  162. case "Function":
  163. case "Symbol":
  164. return type.toLowerCase();
  165. }
  166. if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) === "object") {
  167. return "object";
  168. }
  169. }
  170. // Safe object type checking
  171. function is(type, obj) {
  172. return objectType(obj) === type;
  173. }
  174. // Test for equality any JavaScript type.
  175. // Author: Philippe Rathé <prathe@gmail.com>
  176. var equiv = (function () {
  177. // Stack to decide between skip/abort functions
  178. var callers = [];
  179. // Stack to avoiding loops from circular referencing
  180. var parents = [];
  181. var parentsB = [];
  182. var getProto = Object.getPrototypeOf || function (obj) {
  183. return obj.__proto__;
  184. };
  185. function useStrictEquality(b, a) {
  186. // To catch short annotation VS 'new' annotation of a declaration. e.g.:
  187. // `var i = 1;`
  188. // `var j = new Number(1);`
  189. if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
  190. a = a.valueOf();
  191. }
  192. if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
  193. b = b.valueOf();
  194. }
  195. return a === b;
  196. }
  197. function compareConstructors(a, b) {
  198. var protoA = getProto(a);
  199. var protoB = getProto(b);
  200. // Comparing constructors is more strict than using `instanceof`
  201. if (a.constructor === b.constructor) {
  202. return true;
  203. }
  204. // Ref #851
  205. // If the obj prototype descends from a null constructor, treat it
  206. // as a null prototype.
  207. if (protoA && protoA.constructor === null) {
  208. protoA = null;
  209. }
  210. if (protoB && protoB.constructor === null) {
  211. protoB = null;
  212. }
  213. // Allow objects with no prototype to be equivalent to
  214. // objects with Object as their constructor.
  215. if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
  216. return true;
  217. }
  218. return false;
  219. }
  220. function getRegExpFlags(regexp) {
  221. return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
  222. }
  223. var callbacks = {
  224. "string": useStrictEquality,
  225. "boolean": useStrictEquality,
  226. "number": useStrictEquality,
  227. "null": useStrictEquality,
  228. "undefined": useStrictEquality,
  229. "symbol": useStrictEquality,
  230. "date": useStrictEquality,
  231. "nan": function nan() {
  232. return true;
  233. },
  234. "regexp": function regexp(b, a) {
  235. return a.source === b.source &&
  236. // Include flags in the comparison
  237. getRegExpFlags(a) === getRegExpFlags(b);
  238. },
  239. // - skip when the property is a method of an instance (OOP)
  240. // - abort otherwise,
  241. // initial === would have catch identical references anyway
  242. "function": function _function(b, a) {
  243. var caller = callers[callers.length - 1];
  244. return caller !== Object && typeof caller !== "undefined" && a.toString() === b.toString();
  245. },
  246. "array": function array(b, a) {
  247. var i, j, len, loop, aCircular, bCircular;
  248. len = a.length;
  249. if (len !== b.length) {
  250. // Safe and faster
  251. return false;
  252. }
  253. // Track reference to avoid circular references
  254. parents.push(a);
  255. parentsB.push(b);
  256. for (i = 0; i < len; i++) {
  257. loop = false;
  258. for (j = 0; j < parents.length; j++) {
  259. aCircular = parents[j] === a[i];
  260. bCircular = parentsB[j] === b[i];
  261. if (aCircular || bCircular) {
  262. if (a[i] === b[i] || aCircular && bCircular) {
  263. loop = true;
  264. } else {
  265. parents.pop();
  266. parentsB.pop();
  267. return false;
  268. }
  269. }
  270. }
  271. if (!loop && !innerEquiv(a[i], b[i])) {
  272. parents.pop();
  273. parentsB.pop();
  274. return false;
  275. }
  276. }
  277. parents.pop();
  278. parentsB.pop();
  279. return true;
  280. },
  281. "set": function set(b, a) {
  282. var innerEq,
  283. outerEq = true;
  284. if (a.size !== b.size) {
  285. return false;
  286. }
  287. a.forEach(function (aVal) {
  288. innerEq = false;
  289. b.forEach(function (bVal) {
  290. if (innerEquiv(bVal, aVal)) {
  291. innerEq = true;
  292. }
  293. });
  294. if (!innerEq) {
  295. outerEq = false;
  296. }
  297. });
  298. return outerEq;
  299. },
  300. "map": function map(b, a) {
  301. var innerEq,
  302. outerEq = true;
  303. if (a.size !== b.size) {
  304. return false;
  305. }
  306. a.forEach(function (aVal, aKey) {
  307. innerEq = false;
  308. b.forEach(function (bVal, bKey) {
  309. if (innerEquiv([bVal, bKey], [aVal, aKey])) {
  310. innerEq = true;
  311. }
  312. });
  313. if (!innerEq) {
  314. outerEq = false;
  315. }
  316. });
  317. return outerEq;
  318. },
  319. "object": function object(b, a) {
  320. var i, j, loop, aCircular, bCircular;
  321. // Default to true
  322. var eq = true;
  323. var aProperties = [];
  324. var bProperties = [];
  325. if (compareConstructors(a, b) === false) {
  326. return false;
  327. }
  328. // Stack constructor before traversing properties
  329. callers.push(a.constructor);
  330. // Track reference to avoid circular references
  331. parents.push(a);
  332. parentsB.push(b);
  333. // Be strict: don't ensure hasOwnProperty and go deep
  334. for (i in a) {
  335. loop = false;
  336. for (j = 0; j < parents.length; j++) {
  337. aCircular = parents[j] === a[i];
  338. bCircular = parentsB[j] === b[i];
  339. if (aCircular || bCircular) {
  340. if (a[i] === b[i] || aCircular && bCircular) {
  341. loop = true;
  342. } else {
  343. eq = false;
  344. break;
  345. }
  346. }
  347. }
  348. aProperties.push(i);
  349. if (!loop && !innerEquiv(a[i], b[i])) {
  350. eq = false;
  351. break;
  352. }
  353. }
  354. parents.pop();
  355. parentsB.pop();
  356. // Unstack, we are done
  357. callers.pop();
  358. for (i in b) {
  359. // Collect b's properties
  360. bProperties.push(i);
  361. }
  362. // Ensures identical properties name
  363. return eq && innerEquiv(aProperties.sort(), bProperties.sort());
  364. }
  365. };
  366. function typeEquiv(a, b) {
  367. var type = objectType(a);
  368. return objectType(b) === type && callbacks[type](b, a);
  369. }
  370. // The real equiv function
  371. function innerEquiv(a, b) {
  372. // We're done when there's nothing more to compare
  373. if (arguments.length < 2) {
  374. return true;
  375. }
  376. // Require type-specific equality
  377. return (a === b || typeEquiv(a, b)) && (
  378. // ...across all consecutive argument pairs
  379. arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1)));
  380. }
  381. return innerEquiv;
  382. })();
  383. /**
  384. * Config object: Maintain internal state
  385. * Later exposed as QUnit.config
  386. * `config` initialized at top of scope
  387. */
  388. var config = {
  389. // The queue of tests to run
  390. queue: [],
  391. // Block until document ready
  392. blocking: true,
  393. // By default, run previously failed tests first
  394. // very useful in combination with "Hide passed tests" checked
  395. reorder: true,
  396. // By default, modify document.title when suite is done
  397. altertitle: true,
  398. // HTML Reporter: collapse every test except the first failing test
  399. // If false, all failing tests will be expanded
  400. collapse: true,
  401. // By default, scroll to top of the page when suite is done
  402. scrolltop: true,
  403. // Depth up-to which object will be dumped
  404. maxDepth: 5,
  405. // When enabled, all tests must call expect()
  406. requireExpects: false,
  407. // Placeholder for user-configurable form-exposed URL parameters
  408. urlConfig: [],
  409. // Set of all modules.
  410. modules: [],
  411. // Stack of nested modules
  412. moduleStack: [],
  413. // The first unnamed module
  414. currentModule: {
  415. name: "",
  416. tests: [],
  417. childModules: [],
  418. testsRun: 0
  419. },
  420. callbacks: {},
  421. // The storage module to use for reordering tests
  422. storage: sessionStorage
  423. };
  424. // take a predefined QUnit.config and extend the defaults
  425. var globalConfig = window && window.QUnit && window.QUnit.config;
  426. // only extend the global config if there is no QUnit overload
  427. if (window && window.QUnit && !window.QUnit.version) {
  428. extend(config, globalConfig);
  429. }
  430. // Push a loose unnamed module to the modules collection
  431. config.modules.push(config.currentModule);
  432. // Based on jsDump by Ariel Flesler
  433. // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
  434. var dump = (function () {
  435. function quote(str) {
  436. return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
  437. }
  438. function literal(o) {
  439. return o + "";
  440. }
  441. function join(pre, arr, post) {
  442. var s = dump.separator(),
  443. base = dump.indent(),
  444. inner = dump.indent(1);
  445. if (arr.join) {
  446. arr = arr.join("," + s + inner);
  447. }
  448. if (!arr) {
  449. return pre + post;
  450. }
  451. return [pre, inner + arr, base + post].join(s);
  452. }
  453. function array(arr, stack) {
  454. var i = arr.length,
  455. ret = new Array(i);
  456. if (dump.maxDepth && dump.depth > dump.maxDepth) {
  457. return "[object Array]";
  458. }
  459. this.up();
  460. while (i--) {
  461. ret[i] = this.parse(arr[i], undefined, stack);
  462. }
  463. this.down();
  464. return join("[", ret, "]");
  465. }
  466. function isArray(obj) {
  467. return (
  468. //Native Arrays
  469. toString.call(obj) === "[object Array]" ||
  470. // NodeList objects
  471. typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
  472. );
  473. }
  474. var reName = /^function (\w+)/,
  475. dump = {
  476. // The objType is used mostly internally, you can fix a (custom) type in advance
  477. parse: function parse(obj, objType, stack) {
  478. stack = stack || [];
  479. var res,
  480. parser,
  481. parserType,
  482. inStack = inArray(obj, stack);
  483. if (inStack !== -1) {
  484. return "recursion(" + (inStack - stack.length) + ")";
  485. }
  486. objType = objType || this.typeOf(obj);
  487. parser = this.parsers[objType];
  488. parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
  489. if (parserType === "function") {
  490. stack.push(obj);
  491. res = parser.call(this, obj, stack);
  492. stack.pop();
  493. return res;
  494. }
  495. return parserType === "string" ? parser : this.parsers.error;
  496. },
  497. typeOf: function typeOf(obj) {
  498. var type;
  499. if (obj === null) {
  500. type = "null";
  501. } else if (typeof obj === "undefined") {
  502. type = "undefined";
  503. } else if (is("regexp", obj)) {
  504. type = "regexp";
  505. } else if (is("date", obj)) {
  506. type = "date";
  507. } else if (is("function", obj)) {
  508. type = "function";
  509. } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
  510. type = "window";
  511. } else if (obj.nodeType === 9) {
  512. type = "document";
  513. } else if (obj.nodeType) {
  514. type = "node";
  515. } else if (isArray(obj)) {
  516. type = "array";
  517. } else if (obj.constructor === Error.prototype.constructor) {
  518. type = "error";
  519. } else {
  520. type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
  521. }
  522. return type;
  523. },
  524. separator: function separator() {
  525. if (this.multiline) {
  526. return this.HTML ? "<br />" : "\n";
  527. } else {
  528. return this.HTML ? "&#160;" : " ";
  529. }
  530. },
  531. // Extra can be a number, shortcut for increasing-calling-decreasing
  532. indent: function indent(extra) {
  533. if (!this.multiline) {
  534. return "";
  535. }
  536. var chr = this.indentChar;
  537. if (this.HTML) {
  538. chr = chr.replace(/\t/g, " ").replace(/ /g, "&#160;");
  539. }
  540. return new Array(this.depth + (extra || 0)).join(chr);
  541. },
  542. up: function up(a) {
  543. this.depth += a || 1;
  544. },
  545. down: function down(a) {
  546. this.depth -= a || 1;
  547. },
  548. setParser: function setParser(name, parser) {
  549. this.parsers[name] = parser;
  550. },
  551. // The next 3 are exposed so you can use them
  552. quote: quote,
  553. literal: literal,
  554. join: join,
  555. depth: 1,
  556. maxDepth: config.maxDepth,
  557. // This is the list of parsers, to modify them, use dump.setParser
  558. parsers: {
  559. window: "[Window]",
  560. document: "[Document]",
  561. error: function error(_error) {
  562. return "Error(\"" + _error.message + "\")";
  563. },
  564. unknown: "[Unknown]",
  565. "null": "null",
  566. "undefined": "undefined",
  567. "function": function _function(fn) {
  568. var ret = "function",
  569. // Functions never have name in IE
  570. name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
  571. if (name) {
  572. ret += " " + name;
  573. }
  574. ret += "(";
  575. ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
  576. return join(ret, dump.parse(fn, "functionCode"), "}");
  577. },
  578. array: array,
  579. nodelist: array,
  580. "arguments": array,
  581. object: function object(map, stack) {
  582. var keys,
  583. key,
  584. val,
  585. i,
  586. nonEnumerableProperties,
  587. ret = [];
  588. if (dump.maxDepth && dump.depth > dump.maxDepth) {
  589. return "[object Object]";
  590. }
  591. dump.up();
  592. keys = [];
  593. for (key in map) {
  594. keys.push(key);
  595. }
  596. // Some properties are not always enumerable on Error objects.
  597. nonEnumerableProperties = ["message", "name"];
  598. for (i in nonEnumerableProperties) {
  599. key = nonEnumerableProperties[i];
  600. if (key in map && inArray(key, keys) < 0) {
  601. keys.push(key);
  602. }
  603. }
  604. keys.sort();
  605. for (i = 0; i < keys.length; i++) {
  606. key = keys[i];
  607. val = map[key];
  608. ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
  609. }
  610. dump.down();
  611. return join("{", ret, "}");
  612. },
  613. node: function node(_node) {
  614. var len,
  615. i,
  616. val,
  617. open = dump.HTML ? "&lt;" : "<",
  618. close = dump.HTML ? "&gt;" : ">",
  619. tag = _node.nodeName.toLowerCase(),
  620. ret = open + tag,
  621. attrs = _node.attributes;
  622. if (attrs) {
  623. for (i = 0, len = attrs.length; i < len; i++) {
  624. val = attrs[i].nodeValue;
  625. // IE6 includes all attributes in .attributes, even ones not explicitly
  626. // set. Those have values like undefined, null, 0, false, "" or
  627. // "inherit".
  628. if (val && val !== "inherit") {
  629. ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
  630. }
  631. }
  632. }
  633. ret += close;
  634. // Show content of TextNode or CDATASection
  635. if (_node.nodeType === 3 || _node.nodeType === 4) {
  636. ret += _node.nodeValue;
  637. }
  638. return ret + open + "/" + tag + close;
  639. },
  640. // Function calls it internally, it's the arguments part of the function
  641. functionArgs: function functionArgs(fn) {
  642. var args,
  643. l = fn.length;
  644. if (!l) {
  645. return "";
  646. }
  647. args = new Array(l);
  648. while (l--) {
  649. // 97 is 'a'
  650. args[l] = String.fromCharCode(97 + l);
  651. }
  652. return " " + args.join(", ") + " ";
  653. },
  654. // Object calls it internally, the key part of an item in a map
  655. key: quote,
  656. // Function calls it internally, it's the content of the function
  657. functionCode: "[code]",
  658. // Node calls it internally, it's a html attribute value
  659. attribute: quote,
  660. string: quote,
  661. date: quote,
  662. regexp: literal,
  663. number: literal,
  664. "boolean": literal,
  665. symbol: function symbol(sym) {
  666. return sym.toString();
  667. }
  668. },
  669. // If true, entities are escaped ( <, >, \t, space and \n )
  670. HTML: false,
  671. // Indentation unit
  672. indentChar: " ",
  673. // If true, items in a collection, are separated by a \n, else just a space.
  674. multiline: true
  675. };
  676. return dump;
  677. })();
  678. // Register logging callbacks
  679. function registerLoggingCallbacks(obj) {
  680. var i,
  681. l,
  682. key,
  683. callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
  684. function registerLoggingCallback(key) {
  685. var loggingCallback = function loggingCallback(callback) {
  686. if (objectType(callback) !== "function") {
  687. throw new Error("QUnit logging methods require a callback function as their first parameters.");
  688. }
  689. config.callbacks[key].push(callback);
  690. };
  691. return loggingCallback;
  692. }
  693. for (i = 0, l = callbackNames.length; i < l; i++) {
  694. key = callbackNames[i];
  695. // Initialize key collection of logging callback
  696. if (objectType(config.callbacks[key]) === "undefined") {
  697. config.callbacks[key] = [];
  698. }
  699. obj[key] = registerLoggingCallback(key);
  700. }
  701. }
  702. function runLoggingCallbacks(key, args) {
  703. var i, l, callbacks;
  704. callbacks = config.callbacks[key];
  705. for (i = 0, l = callbacks.length; i < l; i++) {
  706. callbacks[i](args);
  707. }
  708. }
  709. // Doesn't support IE9, it will return undefined on these browsers
  710. // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
  711. var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
  712. function extractStacktrace(e, offset) {
  713. offset = offset === undefined ? 4 : offset;
  714. var stack, include, i;
  715. if (e && e.stack) {
  716. stack = e.stack.split("\n");
  717. if (/^error$/i.test(stack[0])) {
  718. stack.shift();
  719. }
  720. if (fileName) {
  721. include = [];
  722. for (i = offset; i < stack.length; i++) {
  723. if (stack[i].indexOf(fileName) !== -1) {
  724. break;
  725. }
  726. include.push(stack[i]);
  727. }
  728. if (include.length) {
  729. return include.join("\n");
  730. }
  731. }
  732. return stack[offset];
  733. }
  734. }
  735. function sourceFromStacktrace(offset) {
  736. var error = new Error();
  737. // Support: Safari <=7 only, IE <=10 - 11 only
  738. // Not all browsers generate the `stack` property for `new Error()`, see also #636
  739. if (!error.stack) {
  740. try {
  741. throw error;
  742. } catch (err) {
  743. error = err;
  744. }
  745. }
  746. return extractStacktrace(error, offset);
  747. }
  748. var unitSampler;
  749. var focused = false;
  750. var priorityCount = 0;
  751. function Test(settings) {
  752. var i, l;
  753. ++Test.count;
  754. this.expected = null;
  755. extend(this, settings);
  756. this.assertions = [];
  757. this.semaphore = 0;
  758. this.usedAsync = false;
  759. this.module = config.currentModule;
  760. this.stack = sourceFromStacktrace(3);
  761. // Register unique strings
  762. for (i = 0, l = this.module.tests; i < l.length; i++) {
  763. if (this.module.tests[i].name === this.testName) {
  764. this.testName += " ";
  765. }
  766. }
  767. this.testId = generateHash(this.module.name, this.testName);
  768. this.module.tests.push({
  769. name: this.testName,
  770. testId: this.testId
  771. });
  772. if (settings.skip) {
  773. // Skipped tests will fully ignore any sent callback
  774. this.callback = function () {};
  775. this.async = false;
  776. this.expected = 0;
  777. } else {
  778. this.assert = new Assert(this);
  779. }
  780. }
  781. Test.count = 0;
  782. function getNotStartedModules(startModule) {
  783. var module = startModule,
  784. modules = [];
  785. while (module && module.testsRun === 0) {
  786. modules.push(module);
  787. module = module.parentModule;
  788. }
  789. return modules;
  790. }
  791. Test.prototype = {
  792. before: function before() {
  793. var i,
  794. startModule,
  795. module = this.module,
  796. notStartedModules = getNotStartedModules(module);
  797. for (i = notStartedModules.length - 1; i >= 0; i--) {
  798. startModule = notStartedModules[i];
  799. startModule.stats = { all: 0, bad: 0, started: now() };
  800. runLoggingCallbacks("moduleStart", {
  801. name: startModule.name,
  802. tests: startModule.tests
  803. });
  804. }
  805. config.current = this;
  806. if (module.testEnvironment) {
  807. delete module.testEnvironment.before;
  808. delete module.testEnvironment.beforeEach;
  809. delete module.testEnvironment.afterEach;
  810. delete module.testEnvironment.after;
  811. }
  812. this.testEnvironment = extend({}, module.testEnvironment);
  813. this.started = now();
  814. runLoggingCallbacks("testStart", {
  815. name: this.testName,
  816. module: module.name,
  817. testId: this.testId,
  818. previousFailure: this.previousFailure
  819. });
  820. if (!config.pollution) {
  821. saveGlobal();
  822. }
  823. },
  824. run: function run() {
  825. var promise;
  826. config.current = this;
  827. this.callbackStarted = now();
  828. if (config.notrycatch) {
  829. runTest(this);
  830. return;
  831. }
  832. try {
  833. runTest(this);
  834. } catch (e) {
  835. this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
  836. // Else next test will carry the responsibility
  837. saveGlobal();
  838. // Restart the tests if they're blocking
  839. if (config.blocking) {
  840. internalRecover(this);
  841. }
  842. }
  843. function runTest(test) {
  844. promise = test.callback.call(test.testEnvironment, test.assert);
  845. test.resolvePromise(promise);
  846. }
  847. },
  848. after: function after() {
  849. checkPollution();
  850. },
  851. queueHook: function queueHook(hook, hookName, hookOwner) {
  852. var promise,
  853. test = this;
  854. return function runHook() {
  855. if (hookName === "before") {
  856. if (hookOwner.testsRun !== 0) {
  857. return;
  858. }
  859. test.preserveEnvironment = true;
  860. }
  861. if (hookName === "after" && hookOwner.testsRun !== numberOfTests(hookOwner) - 1) {
  862. return;
  863. }
  864. config.current = test;
  865. if (config.notrycatch) {
  866. callHook();
  867. return;
  868. }
  869. try {
  870. callHook();
  871. } catch (error) {
  872. test.pushFailure(hookName + " failed on " + test.testName + ": " + (error.message || error), extractStacktrace(error, 0));
  873. }
  874. function callHook() {
  875. promise = hook.call(test.testEnvironment, test.assert);
  876. test.resolvePromise(promise, hookName);
  877. }
  878. };
  879. },
  880. // Currently only used for module level hooks, can be used to add global level ones
  881. hooks: function hooks(handler) {
  882. var hooks = [];
  883. function processHooks(test, module) {
  884. if (module.parentModule) {
  885. processHooks(test, module.parentModule);
  886. }
  887. if (module.testEnvironment && objectType(module.testEnvironment[handler]) === "function") {
  888. hooks.push(test.queueHook(module.testEnvironment[handler], handler, module));
  889. }
  890. }
  891. // Hooks are ignored on skipped tests
  892. if (!this.skip) {
  893. processHooks(this, this.module);
  894. }
  895. return hooks;
  896. },
  897. finish: function finish() {
  898. config.current = this;
  899. if (config.requireExpects && this.expected === null) {
  900. this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
  901. } else if (this.expected !== null && this.expected !== this.assertions.length) {
  902. this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
  903. } else if (this.expected === null && !this.assertions.length) {
  904. this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
  905. }
  906. var i,
  907. module = this.module,
  908. moduleName = module.name,
  909. testName = this.testName,
  910. skipped = !!this.skip,
  911. bad = 0,
  912. storage = config.storage;
  913. this.runtime = now() - this.started;
  914. config.stats.all += this.assertions.length;
  915. module.stats.all += this.assertions.length;
  916. for (i = 0; i < this.assertions.length; i++) {
  917. if (!this.assertions[i].result) {
  918. bad++;
  919. config.stats.bad++;
  920. module.stats.bad++;
  921. }
  922. }
  923. notifyTestsRan(module);
  924. // Store result when possible
  925. if (storage) {
  926. if (bad) {
  927. storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
  928. } else {
  929. storage.removeItem("qunit-test-" + moduleName + "-" + testName);
  930. }
  931. }
  932. runLoggingCallbacks("testDone", {
  933. name: testName,
  934. module: moduleName,
  935. skipped: skipped,
  936. failed: bad,
  937. passed: this.assertions.length - bad,
  938. total: this.assertions.length,
  939. runtime: skipped ? 0 : this.runtime,
  940. // HTML Reporter use
  941. assertions: this.assertions,
  942. testId: this.testId,
  943. // Source of Test
  944. source: this.stack
  945. });
  946. if (module.testsRun === numberOfTests(module)) {
  947. runLoggingCallbacks("moduleDone", {
  948. name: module.name,
  949. tests: module.tests,
  950. failed: module.stats.bad,
  951. passed: module.stats.all - module.stats.bad,
  952. total: module.stats.all,
  953. runtime: now() - module.stats.started
  954. });
  955. }
  956. config.current = undefined;
  957. },
  958. preserveTestEnvironment: function preserveTestEnvironment() {
  959. if (this.preserveEnvironment) {
  960. this.module.testEnvironment = this.testEnvironment;
  961. this.testEnvironment = extend({}, this.module.testEnvironment);
  962. }
  963. },
  964. queue: function queue() {
  965. var priority,
  966. previousFailCount,
  967. test = this;
  968. if (!this.valid()) {
  969. return;
  970. }
  971. function run() {
  972. // Each of these can by async
  973. synchronize([function () {
  974. test.before();
  975. }, test.hooks("before"), function () {
  976. test.preserveTestEnvironment();
  977. }, test.hooks("beforeEach"), function () {
  978. test.run();
  979. }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
  980. test.after();
  981. }, function () {
  982. test.finish();
  983. }]);
  984. }
  985. previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
  986. // Prioritize previously failed tests, detected from storage
  987. priority = config.reorder && previousFailCount;
  988. this.previousFailure = !!previousFailCount;
  989. return synchronize(run, priority, config.seed);
  990. },
  991. pushResult: function pushResult(resultInfo) {
  992. // Destructure of resultInfo = { result, actual, expected, message, negative }
  993. var source,
  994. details = {
  995. module: this.module.name,
  996. name: this.testName,
  997. result: resultInfo.result,
  998. message: resultInfo.message,
  999. actual: resultInfo.actual,
  1000. expected: resultInfo.expected,
  1001. testId: this.testId,
  1002. negative: resultInfo.negative || false,
  1003. runtime: now() - this.started
  1004. };
  1005. if (!resultInfo.result) {
  1006. source = sourceFromStacktrace();
  1007. if (source) {
  1008. details.source = source;
  1009. }
  1010. }
  1011. runLoggingCallbacks("log", details);
  1012. this.assertions.push({
  1013. result: !!resultInfo.result,
  1014. message: resultInfo.message
  1015. });
  1016. },
  1017. pushFailure: function pushFailure(message, source, actual) {
  1018. if (!(this instanceof Test)) {
  1019. throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
  1020. }
  1021. var details = {
  1022. module: this.module.name,
  1023. name: this.testName,
  1024. result: false,
  1025. message: message || "error",
  1026. actual: actual || null,
  1027. testId: this.testId,
  1028. runtime: now() - this.started
  1029. };
  1030. if (source) {
  1031. details.source = source;
  1032. }
  1033. runLoggingCallbacks("log", details);
  1034. this.assertions.push({
  1035. result: false,
  1036. message: message
  1037. });
  1038. },
  1039. resolvePromise: function resolvePromise(promise, phase) {
  1040. var then,
  1041. resume,
  1042. message,
  1043. test = this;
  1044. if (promise != null) {
  1045. then = promise.then;
  1046. if (objectType(then) === "function") {
  1047. resume = internalStop(test);
  1048. then.call(promise, function () {
  1049. resume();
  1050. }, function (error) {
  1051. message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
  1052. test.pushFailure(message, extractStacktrace(error, 0));
  1053. // Else next test will carry the responsibility
  1054. saveGlobal();
  1055. // Unblock
  1056. resume();
  1057. });
  1058. }
  1059. }
  1060. },
  1061. valid: function valid() {
  1062. var filter = config.filter,
  1063. regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
  1064. module = config.module && config.module.toLowerCase(),
  1065. fullName = this.module.name + ": " + this.testName;
  1066. function moduleChainNameMatch(testModule) {
  1067. var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
  1068. if (testModuleName === module) {
  1069. return true;
  1070. } else if (testModule.parentModule) {
  1071. return moduleChainNameMatch(testModule.parentModule);
  1072. } else {
  1073. return false;
  1074. }
  1075. }
  1076. function moduleChainIdMatch(testModule) {
  1077. return inArray(testModule.moduleId, config.moduleId) > -1 || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
  1078. }
  1079. // Internally-generated tests are always valid
  1080. if (this.callback && this.callback.validTest) {
  1081. return true;
  1082. }
  1083. if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
  1084. return false;
  1085. }
  1086. if (config.testId && config.testId.length > 0 && inArray(this.testId, config.testId) < 0) {
  1087. return false;
  1088. }
  1089. if (module && !moduleChainNameMatch(this.module)) {
  1090. return false;
  1091. }
  1092. if (!filter) {
  1093. return true;
  1094. }
  1095. return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
  1096. },
  1097. regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
  1098. var regex = new RegExp(pattern, flags);
  1099. var match = regex.test(fullName);
  1100. return match !== exclude;
  1101. },
  1102. stringFilter: function stringFilter(filter, fullName) {
  1103. filter = filter.toLowerCase();
  1104. fullName = fullName.toLowerCase();
  1105. var include = filter.charAt(0) !== "!";
  1106. if (!include) {
  1107. filter = filter.slice(1);
  1108. }
  1109. // If the filter matches, we need to honour include
  1110. if (fullName.indexOf(filter) !== -1) {
  1111. return include;
  1112. }
  1113. // Otherwise, do the opposite
  1114. return !include;
  1115. }
  1116. };
  1117. function pushFailure() {
  1118. if (!config.current) {
  1119. throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
  1120. }
  1121. // Gets current test obj
  1122. var currentTest = config.current;
  1123. return currentTest.pushFailure.apply(currentTest, arguments);
  1124. }
  1125. // Based on Java's String.hashCode, a simple but not
  1126. // rigorously collision resistant hashing function
  1127. function generateHash(module, testName) {
  1128. var hex,
  1129. i = 0,
  1130. hash = 0,
  1131. str = module + "\x1C" + testName,
  1132. len = str.length;
  1133. for (; i < len; i++) {
  1134. hash = (hash << 5) - hash + str.charCodeAt(i);
  1135. hash |= 0;
  1136. }
  1137. // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
  1138. // strictly necessary but increases user understanding that the id is a SHA-like hash
  1139. hex = (0x100000000 + hash).toString(16);
  1140. if (hex.length < 8) {
  1141. hex = "0000000" + hex;
  1142. }
  1143. return hex.slice(-8);
  1144. }
  1145. function synchronize(callback, priority, seed) {
  1146. var last = !priority,
  1147. index;
  1148. if (objectType(callback) === "array") {
  1149. while (callback.length) {
  1150. synchronize(callback.shift());
  1151. }
  1152. return;
  1153. }
  1154. if (priority) {
  1155. config.queue.splice(priorityCount++, 0, callback);
  1156. } else if (seed) {
  1157. if (!unitSampler) {
  1158. unitSampler = unitSamplerGenerator(seed);
  1159. }
  1160. // Insert into a random position after all priority items
  1161. index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
  1162. config.queue.splice(priorityCount + index, 0, callback);
  1163. } else {
  1164. config.queue.push(callback);
  1165. }
  1166. if (internalState.autorun && !config.blocking) {
  1167. process(last);
  1168. }
  1169. }
  1170. function unitSamplerGenerator(seed) {
  1171. // 32-bit xorshift, requires only a nonzero seed
  1172. // http://excamera.com/sphinx/article-xorshift.html
  1173. var sample = parseInt(generateHash(seed), 16) || -1;
  1174. return function () {
  1175. sample ^= sample << 13;
  1176. sample ^= sample >>> 17;
  1177. sample ^= sample << 5;
  1178. // ECMAScript has no unsigned number type
  1179. if (sample < 0) {
  1180. sample += 0x100000000;
  1181. }
  1182. return sample / 0x100000000;
  1183. };
  1184. }
  1185. function saveGlobal() {
  1186. config.pollution = [];
  1187. if (config.noglobals) {
  1188. for (var key in global$1) {
  1189. if (hasOwn.call(global$1, key)) {
  1190. // In Opera sometimes DOM element ids show up here, ignore them
  1191. if (/^qunit-test-output/.test(key)) {
  1192. continue;
  1193. }
  1194. config.pollution.push(key);
  1195. }
  1196. }
  1197. }
  1198. }
  1199. function checkPollution() {
  1200. var newGlobals,
  1201. deletedGlobals,
  1202. old = config.pollution;
  1203. saveGlobal();
  1204. newGlobals = diff(config.pollution, old);
  1205. if (newGlobals.length > 0) {
  1206. pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
  1207. }
  1208. deletedGlobals = diff(old, config.pollution);
  1209. if (deletedGlobals.length > 0) {
  1210. pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
  1211. }
  1212. }
  1213. // Will be exposed as QUnit.test
  1214. function test(testName, callback) {
  1215. if (focused) {
  1216. return;
  1217. }
  1218. var newTest;
  1219. newTest = new Test({
  1220. testName: testName,
  1221. callback: callback
  1222. });
  1223. newTest.queue();
  1224. }
  1225. // Will be exposed as QUnit.skip
  1226. function skip(testName) {
  1227. if (focused) {
  1228. return;
  1229. }
  1230. var test = new Test({
  1231. testName: testName,
  1232. skip: true
  1233. });
  1234. test.queue();
  1235. }
  1236. // Will be exposed as QUnit.only
  1237. function only(testName, callback) {
  1238. var newTest;
  1239. if (focused) {
  1240. return;
  1241. }
  1242. config.queue.length = 0;
  1243. focused = true;
  1244. newTest = new Test({
  1245. testName: testName,
  1246. callback: callback
  1247. });
  1248. newTest.queue();
  1249. }
  1250. // Put a hold on processing and return a function that will release it.
  1251. function internalStop(test) {
  1252. var released = false;
  1253. test.semaphore += 1;
  1254. config.blocking = true;
  1255. // Set a recovery timeout, if so configured.
  1256. if (config.testTimeout && defined.setTimeout) {
  1257. clearTimeout(config.timeout);
  1258. config.timeout = setTimeout(function () {
  1259. pushFailure("Test timed out", sourceFromStacktrace(2));
  1260. internalRecover(test);
  1261. }, config.testTimeout);
  1262. }
  1263. return function resume() {
  1264. if (released) {
  1265. return;
  1266. }
  1267. released = true;
  1268. test.semaphore -= 1;
  1269. internalStart(test);
  1270. };
  1271. }
  1272. // Forcefully release all processing holds.
  1273. function internalRecover(test) {
  1274. test.semaphore = 0;
  1275. internalStart(test);
  1276. }
  1277. // Release a processing hold, scheduling a resumption attempt if no holds remain.
  1278. function internalStart(test) {
  1279. // If semaphore is non-numeric, throw error
  1280. if (isNaN(test.semaphore)) {
  1281. test.semaphore = 0;
  1282. pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
  1283. return;
  1284. }
  1285. // Don't start until equal number of stop-calls
  1286. if (test.semaphore > 0) {
  1287. return;
  1288. }
  1289. // Throw an Error if start is called more often than stop
  1290. if (test.semaphore < 0) {
  1291. test.semaphore = 0;
  1292. pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
  1293. return;
  1294. }
  1295. // Add a slight delay to allow more assertions etc.
  1296. if (defined.setTimeout) {
  1297. if (config.timeout) {
  1298. clearTimeout(config.timeout);
  1299. }
  1300. config.timeout = setTimeout(function () {
  1301. if (test.semaphore > 0) {
  1302. return;
  1303. }
  1304. if (config.timeout) {
  1305. clearTimeout(config.timeout);
  1306. }
  1307. begin();
  1308. }, 13);
  1309. } else {
  1310. begin();
  1311. }
  1312. }
  1313. function numberOfTests(module) {
  1314. var count = module.tests.length,
  1315. modules = [].concat(toConsumableArray(module.childModules));
  1316. // Do a breadth-first traversal of the child modules
  1317. while (modules.length) {
  1318. var nextModule = modules.shift();
  1319. count += nextModule.tests.length;
  1320. modules.push.apply(modules, toConsumableArray(nextModule.childModules));
  1321. }
  1322. return count;
  1323. }
  1324. function notifyTestsRan(module) {
  1325. module.testsRun++;
  1326. while (module = module.parentModule) {
  1327. module.testsRun++;
  1328. }
  1329. }
  1330. function Assert(testContext) {
  1331. this.test = testContext;
  1332. }
  1333. // Assert helpers
  1334. Assert.prototype = {
  1335. // Specify the number of expected assertions to guarantee that failed test
  1336. // (no assertions are run at all) don't slip through.
  1337. expect: function expect(asserts) {
  1338. if (arguments.length === 1) {
  1339. this.test.expected = asserts;
  1340. } else {
  1341. return this.test.expected;
  1342. }
  1343. },
  1344. // Put a hold on processing and return a function that will release it a maximum of once.
  1345. async: function async(count) {
  1346. var resume,
  1347. test$$1 = this.test,
  1348. popped = false,
  1349. acceptCallCount = count;
  1350. if (typeof acceptCallCount === "undefined") {
  1351. acceptCallCount = 1;
  1352. }
  1353. test$$1.usedAsync = true;
  1354. resume = internalStop(test$$1);
  1355. return function done() {
  1356. if (popped) {
  1357. test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
  1358. return;
  1359. }
  1360. acceptCallCount -= 1;
  1361. if (acceptCallCount > 0) {
  1362. return;
  1363. }
  1364. popped = true;
  1365. resume();
  1366. };
  1367. },
  1368. // Exports test.push() to the user API
  1369. // Alias of pushResult.
  1370. push: function push(result, actual, expected, message, negative) {
  1371. var currentAssert = this instanceof Assert ? this : config.current.assert;
  1372. return currentAssert.pushResult({
  1373. result: result,
  1374. actual: actual,
  1375. expected: expected,
  1376. message: message,
  1377. negative: negative
  1378. });
  1379. },
  1380. pushResult: function pushResult(resultInfo) {
  1381. // Destructure of resultInfo = { result, actual, expected, message, negative }
  1382. var assert = this,
  1383. currentTest = assert instanceof Assert && assert.test || config.current;
  1384. // Backwards compatibility fix.
  1385. // Allows the direct use of global exported assertions and QUnit.assert.*
  1386. // Although, it's use is not recommended as it can leak assertions
  1387. // to other tests from async tests, because we only get a reference to the current test,
  1388. // not exactly the test where assertion were intended to be called.
  1389. if (!currentTest) {
  1390. throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
  1391. }
  1392. if (currentTest.usedAsync === true && currentTest.semaphore === 0) {
  1393. currentTest.pushFailure("Assertion after the final `assert.async` was resolved", sourceFromStacktrace(2));
  1394. // Allow this assertion to continue running anyway...
  1395. }
  1396. if (!(assert instanceof Assert)) {
  1397. assert = currentTest.assert;
  1398. }
  1399. return assert.test.pushResult(resultInfo);
  1400. },
  1401. ok: function ok(result, message) {
  1402. message = message || (result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result));
  1403. this.pushResult({
  1404. result: !!result,
  1405. actual: result,
  1406. expected: true,
  1407. message: message
  1408. });
  1409. },
  1410. notOk: function notOk(result, message) {
  1411. message = message || (!result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result));
  1412. this.pushResult({
  1413. result: !result,
  1414. actual: result,
  1415. expected: false,
  1416. message: message
  1417. });
  1418. },
  1419. equal: function equal(actual, expected, message) {
  1420. // eslint-disable-next-line eqeqeq
  1421. var result = expected == actual;
  1422. this.pushResult({
  1423. result: result,
  1424. actual: actual,
  1425. expected: expected,
  1426. message: message
  1427. });
  1428. },
  1429. notEqual: function notEqual(actual, expected, message) {
  1430. // eslint-disable-next-line eqeqeq
  1431. var result = expected != actual;
  1432. this.pushResult({
  1433. result: result,
  1434. actual: actual,
  1435. expected: expected,
  1436. message: message,
  1437. negative: true
  1438. });
  1439. },
  1440. propEqual: function propEqual(actual, expected, message) {
  1441. actual = objectValues(actual);
  1442. expected = objectValues(expected);
  1443. this.pushResult({
  1444. result: equiv(actual, expected),
  1445. actual: actual,
  1446. expected: expected,
  1447. message: message
  1448. });
  1449. },
  1450. notPropEqual: function notPropEqual(actual, expected, message) {
  1451. actual = objectValues(actual);
  1452. expected = objectValues(expected);
  1453. this.pushResult({
  1454. result: !equiv(actual, expected),
  1455. actual: actual,
  1456. expected: expected,
  1457. message: message,
  1458. negative: true
  1459. });
  1460. },
  1461. deepEqual: function deepEqual(actual, expected, message) {
  1462. this.pushResult({
  1463. result: equiv(actual, expected),
  1464. actual: actual,
  1465. expected: expected,
  1466. message: message
  1467. });
  1468. },
  1469. notDeepEqual: function notDeepEqual(actual, expected, message) {
  1470. this.pushResult({
  1471. result: !equiv(actual, expected),
  1472. actual: actual,
  1473. expected: expected,
  1474. message: message,
  1475. negative: true
  1476. });
  1477. },
  1478. strictEqual: function strictEqual(actual, expected, message) {
  1479. this.pushResult({
  1480. result: expected === actual,
  1481. actual: actual,
  1482. expected: expected,
  1483. message: message
  1484. });
  1485. },
  1486. notStrictEqual: function notStrictEqual(actual, expected, message) {
  1487. this.pushResult({
  1488. result: expected !== actual,
  1489. actual: actual,
  1490. expected: expected,
  1491. message: message,
  1492. negative: true
  1493. });
  1494. },
  1495. "throws": function throws(block, expected, message) {
  1496. var actual,
  1497. expectedType,
  1498. expectedOutput = expected,
  1499. ok = false,
  1500. currentTest = this instanceof Assert && this.test || config.current;
  1501. // 'expected' is optional unless doing string comparison
  1502. if (objectType(expected) === "string") {
  1503. if (message == null) {
  1504. message = expected;
  1505. expected = null;
  1506. } else {
  1507. throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary." + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1508. }
  1509. }
  1510. currentTest.ignoreGlobalErrors = true;
  1511. try {
  1512. block.call(currentTest.testEnvironment);
  1513. } catch (e) {
  1514. actual = e;
  1515. }
  1516. currentTest.ignoreGlobalErrors = false;
  1517. if (actual) {
  1518. expectedType = objectType(expected);
  1519. // We don't want to validate thrown error
  1520. if (!expected) {
  1521. ok = true;
  1522. expectedOutput = null;
  1523. // Expected is a regexp
  1524. } else if (expectedType === "regexp") {
  1525. ok = expected.test(errorString(actual));
  1526. // Expected is a constructor, maybe an Error constructor
  1527. } else if (expectedType === "function" && actual instanceof expected) {
  1528. ok = true;
  1529. // Expected is an Error object
  1530. } else if (expectedType === "object") {
  1531. ok = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
  1532. // Expected is a validation function which returns true if validation passed
  1533. } else if (expectedType === "function" && expected.call({}, actual) === true) {
  1534. expectedOutput = null;
  1535. ok = true;
  1536. }
  1537. }
  1538. currentTest.assert.pushResult({
  1539. result: ok,
  1540. actual: actual,
  1541. expected: expectedOutput,
  1542. message: message
  1543. });
  1544. }
  1545. };
  1546. // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
  1547. // Known to us are: Closure Compiler, Narwhal
  1548. (function () {
  1549. // eslint-disable-next-line dot-notation
  1550. Assert.prototype.raises = Assert.prototype["throws"];
  1551. })();
  1552. function errorString(error) {
  1553. var name,
  1554. message,
  1555. resultErrorString = error.toString();
  1556. if (resultErrorString.substring(0, 7) === "[object") {
  1557. name = error.name ? error.name.toString() : "Error";
  1558. message = error.message ? error.message.toString() : "";
  1559. if (name && message) {
  1560. return name + ": " + message;
  1561. } else if (name) {
  1562. return name;
  1563. } else if (message) {
  1564. return message;
  1565. } else {
  1566. return "Error";
  1567. }
  1568. } else {
  1569. return resultErrorString;
  1570. }
  1571. }
  1572. /* global module, exports, define */
  1573. function applyDeprecated(name) {
  1574. return function () {
  1575. throw new Error(name + " is removed in QUnit 2.0.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1576. };
  1577. }
  1578. function exportQUnit(QUnit) {
  1579. Object.keys(Assert.prototype).forEach(function (key) {
  1580. QUnit[key] = applyDeprecated("`QUnit." + key + "`");
  1581. });
  1582. QUnit.asyncTest = function () {
  1583. throw new Error("asyncTest is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1584. };
  1585. QUnit.stop = function () {
  1586. throw new Error("QUnit.stop is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1587. };
  1588. function resetThrower() {
  1589. throw new Error("QUnit.reset is removed in QUnit 2.0 without replacement.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1590. }
  1591. Object.defineProperty(QUnit, "reset", {
  1592. get: function get() {
  1593. return resetThrower;
  1594. },
  1595. set: resetThrower
  1596. });
  1597. if (defined.document) {
  1598. // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
  1599. if (window.QUnit && window.QUnit.version) {
  1600. throw new Error("QUnit has already been defined.");
  1601. }
  1602. ["test", "module", "expect", "start", "ok", "notOk", "equal", "notEqual", "propEqual", "notPropEqual", "deepEqual", "notDeepEqual", "strictEqual", "notStrictEqual", "throws", "raises"].forEach(function (key) {
  1603. window[key] = applyDeprecated("The global `" + key + "`");
  1604. });
  1605. window.QUnit = QUnit;
  1606. }
  1607. // For nodejs
  1608. if (typeof module !== "undefined" && module && module.exports) {
  1609. module.exports = QUnit;
  1610. // For consistency with CommonJS environments' exports
  1611. module.exports.QUnit = QUnit;
  1612. }
  1613. // For CommonJS with exports, but without module.exports, like Rhino
  1614. if (typeof exports !== "undefined" && exports) {
  1615. exports.QUnit = QUnit;
  1616. }
  1617. if (typeof define === "function" && define.amd) {
  1618. define(function () {
  1619. return QUnit;
  1620. });
  1621. QUnit.config.autostart = false;
  1622. }
  1623. }
  1624. (function () {
  1625. if (!defined.document) {
  1626. return;
  1627. }
  1628. // `onErrorFnPrev` initialized at top of scope
  1629. // Preserve other handlers
  1630. var onErrorFnPrev = window.onerror;
  1631. // Cover uncaught exceptions
  1632. // Returning true will suppress the default browser handler,
  1633. // returning false will let it run.
  1634. window.onerror = function (error, filePath, linerNr) {
  1635. var ret = false;
  1636. if (onErrorFnPrev) {
  1637. ret = onErrorFnPrev(error, filePath, linerNr);
  1638. }
  1639. // Treat return value as window.onerror itself does,
  1640. // Only do our handling if not suppressed.
  1641. if (ret !== true) {
  1642. if (config.current) {
  1643. if (config.current.ignoreGlobalErrors) {
  1644. return true;
  1645. }
  1646. pushFailure(error, filePath + ":" + linerNr);
  1647. } else {
  1648. test("global failure", extend(function () {
  1649. pushFailure(error, filePath + ":" + linerNr);
  1650. }, { validTest: true }));
  1651. }
  1652. return false;
  1653. }
  1654. return ret;
  1655. };
  1656. })();
  1657. var QUnit = {};
  1658. var globalStartCalled = false;
  1659. var runStarted = false;
  1660. var internalState = {
  1661. autorun: false
  1662. };
  1663. // Figure out if we're running the tests from a server or not
  1664. QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
  1665. // Expose the current QUnit version
  1666. QUnit.version = "2.1.0";
  1667. extend(QUnit, {
  1668. // Call on start of module test to prepend name to all tests
  1669. module: function module(name, testEnvironment, executeNow) {
  1670. var module, moduleFns;
  1671. var currentModule = config.currentModule;
  1672. if (arguments.length === 2) {
  1673. if (objectType(testEnvironment) === "function") {
  1674. executeNow = testEnvironment;
  1675. testEnvironment = undefined;
  1676. }
  1677. }
  1678. module = createModule();
  1679. if (testEnvironment && (testEnvironment.setup || testEnvironment.teardown)) {
  1680. console.warn("Module's `setup` and `teardown` are not hooks anymore on QUnit 2.0, use " + "`beforeEach` and `afterEach` instead\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1681. }
  1682. moduleFns = {
  1683. before: setHook(module, "before"),
  1684. beforeEach: setHook(module, "beforeEach"),
  1685. afterEach: setHook(module, "afterEach"),
  1686. after: setHook(module, "after")
  1687. };
  1688. if (objectType(executeNow) === "function") {
  1689. config.moduleStack.push(module);
  1690. setCurrentModule(module);
  1691. executeNow.call(module.testEnvironment, moduleFns);
  1692. config.moduleStack.pop();
  1693. module = module.parentModule || currentModule;
  1694. }
  1695. setCurrentModule(module);
  1696. function createModule() {
  1697. var parentModule = config.moduleStack.length ? config.moduleStack.slice(-1)[0] : null;
  1698. var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
  1699. var module = {
  1700. name: moduleName,
  1701. parentModule: parentModule,
  1702. tests: [],
  1703. moduleId: generateHash(moduleName),
  1704. testsRun: 0,
  1705. childModules: []
  1706. };
  1707. var env = {};
  1708. if (parentModule) {
  1709. parentModule.childModules.push(module);
  1710. extend(env, parentModule.testEnvironment);
  1711. delete env.beforeEach;
  1712. delete env.afterEach;
  1713. }
  1714. extend(env, testEnvironment);
  1715. module.testEnvironment = env;
  1716. config.modules.push(module);
  1717. return module;
  1718. }
  1719. function setCurrentModule(module) {
  1720. config.currentModule = module;
  1721. }
  1722. },
  1723. test: test,
  1724. skip: skip,
  1725. only: only,
  1726. start: function start(count) {
  1727. var globalStartAlreadyCalled = globalStartCalled;
  1728. if (!config.current) {
  1729. globalStartCalled = true;
  1730. if (runStarted) {
  1731. throw new Error("Called start() while test already started running");
  1732. } else if (globalStartAlreadyCalled || count > 1) {
  1733. throw new Error("Called start() outside of a test context too many times");
  1734. } else if (config.autostart) {
  1735. throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
  1736. } else if (!config.pageLoaded) {
  1737. // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
  1738. config.autostart = true;
  1739. return;
  1740. }
  1741. } else {
  1742. throw new Error("QUnit.start cannot be called inside a test context. This feature is removed in " + "QUnit 2.0. For async tests, use QUnit.test() with assert.async() instead.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1743. }
  1744. scheduleBegin();
  1745. },
  1746. config: config,
  1747. is: is,
  1748. objectType: objectType,
  1749. extend: extend,
  1750. load: function load() {
  1751. config.pageLoaded = true;
  1752. // Initialize the configuration options
  1753. extend(config, {
  1754. stats: { all: 0, bad: 0 },
  1755. started: 0,
  1756. updateRate: 1000,
  1757. autostart: true,
  1758. filter: ""
  1759. }, true);
  1760. if (!runStarted) {
  1761. config.blocking = false;
  1762. if (config.autostart) {
  1763. scheduleBegin();
  1764. }
  1765. }
  1766. },
  1767. stack: function stack(offset) {
  1768. offset = (offset || 0) + 2;
  1769. return sourceFromStacktrace(offset);
  1770. }
  1771. });
  1772. QUnit.pushFailure = pushFailure;
  1773. QUnit.assert = Assert.prototype;
  1774. QUnit.equiv = equiv;
  1775. QUnit.dump = dump;
  1776. // 3.0 TODO: Remove
  1777. function jsDumpThrower() {
  1778. throw new Error("QUnit.jsDump is removed in QUnit 2.0, use QUnit.dump instead.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1779. }
  1780. Object.defineProperty(QUnit, "jsDump", {
  1781. get: jsDumpThrower,
  1782. set: jsDumpThrower
  1783. });
  1784. registerLoggingCallbacks(QUnit);
  1785. function scheduleBegin() {
  1786. runStarted = true;
  1787. // Add a slight delay to allow definition of more modules and tests.
  1788. if (defined.setTimeout) {
  1789. setTimeout(function () {
  1790. begin();
  1791. }, 13);
  1792. } else {
  1793. begin();
  1794. }
  1795. }
  1796. function begin() {
  1797. var i,
  1798. l,
  1799. modulesLog = [];
  1800. // If the test run hasn't officially begun yet
  1801. if (!config.started) {
  1802. // Record the time of the test run's beginning
  1803. config.started = now();
  1804. // Delete the loose unnamed module if unused.
  1805. if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
  1806. config.modules.shift();
  1807. }
  1808. // Avoid unnecessary information by not logging modules' test environments
  1809. for (i = 0, l = config.modules.length; i < l; i++) {
  1810. modulesLog.push({
  1811. name: config.modules[i].name,
  1812. tests: config.modules[i].tests
  1813. });
  1814. }
  1815. // The test run is officially beginning now
  1816. runLoggingCallbacks("begin", {
  1817. totalTests: Test.count,
  1818. modules: modulesLog
  1819. });
  1820. }
  1821. config.blocking = false;
  1822. process(true);
  1823. }
  1824. function process(last) {
  1825. function next() {
  1826. process(last);
  1827. }
  1828. var start = now();
  1829. config.depth = (config.depth || 0) + 1;
  1830. while (config.queue.length && !config.blocking) {
  1831. if (!defined.setTimeout || config.updateRate <= 0 || now() - start < config.updateRate) {
  1832. if (config.current) {
  1833. // Reset async tracking for each phase of the Test lifecycle
  1834. config.current.usedAsync = false;
  1835. }
  1836. config.queue.shift()();
  1837. } else {
  1838. setTimeout(next, 13);
  1839. break;
  1840. }
  1841. }
  1842. config.depth--;
  1843. if (last && !config.blocking && !config.queue.length && config.depth === 0) {
  1844. done$1();
  1845. }
  1846. }
  1847. function done$1() {
  1848. var runtime,
  1849. passed,
  1850. i,
  1851. key,
  1852. storage = config.storage;
  1853. internalState.autorun = true;
  1854. runtime = now() - config.started;
  1855. passed = config.stats.all - config.stats.bad;
  1856. runLoggingCallbacks("done", {
  1857. failed: config.stats.bad,
  1858. passed: passed,
  1859. total: config.stats.all,
  1860. runtime: runtime
  1861. });
  1862. // Clear own storage items if all tests passed
  1863. if (storage && config.stats.bad === 0) {
  1864. for (i = storage.length - 1; i >= 0; i--) {
  1865. key = storage.key(i);
  1866. if (key.indexOf("qunit-test-") === 0) {
  1867. storage.removeItem(key);
  1868. }
  1869. }
  1870. }
  1871. }
  1872. function setHook(module, hookName) {
  1873. if (module.testEnvironment === undefined) {
  1874. module.testEnvironment = {};
  1875. }
  1876. return function (callback) {
  1877. module.testEnvironment[hookName] = callback;
  1878. };
  1879. }
  1880. exportQUnit(QUnit);
  1881. (function () {
  1882. if (typeof window === "undefined" || typeof document === "undefined") {
  1883. return;
  1884. }
  1885. var config = QUnit.config,
  1886. hasOwn = Object.prototype.hasOwnProperty;
  1887. // Stores fixture HTML for resetting later
  1888. function storeFixture() {
  1889. // Avoid overwriting user-defined values
  1890. if (hasOwn.call(config, "fixture")) {
  1891. return;
  1892. }
  1893. var fixture = document.getElementById("qunit-fixture");
  1894. if (fixture) {
  1895. config.fixture = fixture.innerHTML;
  1896. }
  1897. }
  1898. QUnit.begin(storeFixture);
  1899. // Resets the fixture DOM element if available.
  1900. function resetFixture() {
  1901. if (config.fixture == null) {
  1902. return;
  1903. }
  1904. var fixture = document.getElementById("qunit-fixture");
  1905. if (fixture) {
  1906. fixture.innerHTML = config.fixture;
  1907. }
  1908. }
  1909. QUnit.testStart(resetFixture);
  1910. })();
  1911. // Escape text for attribute or text content.
  1912. function escapeText(s) {
  1913. if (!s) {
  1914. return "";
  1915. }
  1916. s = s + "";
  1917. // Both single quotes and double quotes (for attributes)
  1918. return s.replace(/['"<>&]/g, function (s) {
  1919. switch (s) {
  1920. case "'":
  1921. return "&#039;";
  1922. case "\"":
  1923. return "&quot;";
  1924. case "<":
  1925. return "&lt;";
  1926. case ">":
  1927. return "&gt;";
  1928. case "&":
  1929. return "&amp;";
  1930. }
  1931. });
  1932. }
  1933. (function () {
  1934. // Don't load the HTML Reporter on non-browser environments
  1935. if (typeof window === "undefined" || !window.document) {
  1936. return;
  1937. }
  1938. QUnit.init = function () {
  1939. throw new Error("QUnit.init is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" + "Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/");
  1940. };
  1941. var config = QUnit.config,
  1942. document$$1 = window.document,
  1943. collapseNext = false,
  1944. hasOwn = Object.prototype.hasOwnProperty,
  1945. unfilteredUrl = setUrl({ filter: undefined, module: undefined,
  1946. moduleId: undefined, testId: undefined }),
  1947. modulesList = [];
  1948. function addEvent(elem, type, fn) {
  1949. elem.addEventListener(type, fn, false);
  1950. }
  1951. function removeEvent(elem, type, fn) {
  1952. elem.removeEventListener(type, fn, false);
  1953. }
  1954. function addEvents(elems, type, fn) {
  1955. var i = elems.length;
  1956. while (i--) {
  1957. addEvent(elems[i], type, fn);
  1958. }
  1959. }
  1960. function hasClass(elem, name) {
  1961. return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
  1962. }
  1963. function addClass(elem, name) {
  1964. if (!hasClass(elem, name)) {
  1965. elem.className += (elem.className ? " " : "") + name;
  1966. }
  1967. }
  1968. function toggleClass(elem, name, force) {
  1969. if (force || typeof force === "undefined" && !hasClass(elem, name)) {
  1970. addClass(elem, name);
  1971. } else {
  1972. removeClass(elem, name);
  1973. }
  1974. }
  1975. function removeClass(elem, name) {
  1976. var set = " " + elem.className + " ";
  1977. // Class name may appear multiple times
  1978. while (set.indexOf(" " + name + " ") >= 0) {
  1979. set = set.replace(" " + name + " ", " ");
  1980. }
  1981. // Trim for prettiness
  1982. elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
  1983. }
  1984. function id(name) {
  1985. return document$$1.getElementById && document$$1.getElementById(name);
  1986. }
  1987. function interceptNavigation(ev) {
  1988. applyUrlParams();
  1989. if (ev && ev.preventDefault) {
  1990. ev.preventDefault();
  1991. }
  1992. return false;
  1993. }
  1994. function getUrlConfigHtml() {
  1995. var i,
  1996. j,
  1997. val,
  1998. escaped,
  1999. escapedTooltip,
  2000. selection = false,
  2001. urlConfig = config.urlConfig,
  2002. urlConfigHtml = "";
  2003. for (i = 0; i < urlConfig.length; i++) {
  2004. // Options can be either strings or objects with nonempty "id" properties
  2005. val = config.urlConfig[i];
  2006. if (typeof val === "string") {
  2007. val = {
  2008. id: val,
  2009. label: val
  2010. };
  2011. }
  2012. escaped = escapeText(val.id);
  2013. escapedTooltip = escapeText(val.tooltip);
  2014. if (!val.value || typeof val.value === "string") {
  2015. urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
  2016. } else {
  2017. urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
  2018. if (QUnit.is("array", val.value)) {
  2019. for (j = 0; j < val.value.length; j++) {
  2020. escaped = escapeText(val.value[j]);
  2021. urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
  2022. }
  2023. } else {
  2024. for (j in val.value) {
  2025. if (hasOwn.call(val.value, j)) {
  2026. urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
  2027. }
  2028. }
  2029. }
  2030. if (config[val.id] && !selection) {
  2031. escaped = escapeText(config[val.id]);
  2032. urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
  2033. }
  2034. urlConfigHtml += "</select>";
  2035. }
  2036. }
  2037. return urlConfigHtml;
  2038. }
  2039. // Handle "click" events on toolbar checkboxes and "change" for select menus.
  2040. // Updates the URL with the new state of `config.urlConfig` values.
  2041. function toolbarChanged() {
  2042. var updatedUrl,
  2043. value,
  2044. tests,
  2045. field = this,
  2046. params = {};
  2047. // Detect if field is a select menu or a checkbox
  2048. if ("selectedIndex" in field) {
  2049. value = field.options[field.selectedIndex].value || undefined;
  2050. } else {
  2051. value = field.checked ? field.defaultValue || true : undefined;
  2052. }
  2053. params[field.name] = value;
  2054. updatedUrl = setUrl(params);
  2055. // Check if we can apply the change without a page refresh
  2056. if ("hidepassed" === field.name && "replaceState" in window.history) {
  2057. QUnit.urlParams[field.name] = value;
  2058. config[field.name] = value || false;
  2059. tests = id("qunit-tests");
  2060. if (tests) {
  2061. toggleClass(tests, "hidepass", value || false);
  2062. }
  2063. window.history.replaceState(null, "", updatedUrl);
  2064. } else {
  2065. window.location = updatedUrl;
  2066. }
  2067. }
  2068. function setUrl(params) {
  2069. var key,
  2070. arrValue,
  2071. i,
  2072. querystring = "?",
  2073. location = window.location;
  2074. params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
  2075. for (key in params) {
  2076. // Skip inherited or undefined properties
  2077. if (hasOwn.call(params, key) && params[key] !== undefined) {
  2078. // Output a parameter for each value of this key (but usually just one)
  2079. arrValue = [].concat(params[key]);
  2080. for (i = 0; i < arrValue.length; i++) {
  2081. querystring += encodeURIComponent(key);
  2082. if (arrValue[i] !== true) {
  2083. querystring += "=" + encodeURIComponent(arrValue[i]);
  2084. }
  2085. querystring += "&";
  2086. }
  2087. }
  2088. }
  2089. return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
  2090. }
  2091. function applyUrlParams() {
  2092. var i,
  2093. selectedModules = [],
  2094. modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
  2095. filter = id("qunit-filter-input").value;
  2096. for (i = 0; i < modulesList.length; i++) {
  2097. if (modulesList[i].checked) {
  2098. selectedModules.push(modulesList[i].value);
  2099. }
  2100. }
  2101. window.location = setUrl({
  2102. filter: filter === "" ? undefined : filter,
  2103. moduleId: selectedModules.length === 0 ? undefined : selectedModules,
  2104. // Remove module and testId filter
  2105. module: undefined,
  2106. testId: undefined
  2107. });
  2108. }
  2109. function toolbarUrlConfigContainer() {
  2110. var urlConfigContainer = document$$1.createElement("span");
  2111. urlConfigContainer.innerHTML = getUrlConfigHtml();
  2112. addClass(urlConfigContainer, "qunit-url-config");
  2113. addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
  2114. addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
  2115. return urlConfigContainer;
  2116. }
  2117. function toolbarLooseFilter() {
  2118. var filter = document$$1.createElement("form"),
  2119. label = document$$1.createElement("label"),
  2120. input = document$$1.createElement("input"),
  2121. button = document$$1.createElement("button");
  2122. addClass(filter, "qunit-filter");
  2123. label.innerHTML = "Filter: ";
  2124. input.type = "text";
  2125. input.value = config.filter || "";
  2126. input.name = "filter";
  2127. input.id = "qunit-filter-input";
  2128. button.innerHTML = "Go";
  2129. label.appendChild(input);
  2130. filter.appendChild(label);
  2131. filter.appendChild(document$$1.createTextNode(" "));
  2132. filter.appendChild(button);
  2133. addEvent(filter, "submit", interceptNavigation);
  2134. return filter;
  2135. }
  2136. function moduleListHtml() {
  2137. var i,
  2138. checked,
  2139. html = "";
  2140. for (i = 0; i < config.modules.length; i++) {
  2141. if (config.modules[i].name !== "") {
  2142. checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
  2143. html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
  2144. }
  2145. }
  2146. return html;
  2147. }
  2148. function toolbarModuleFilter() {
  2149. var allCheckbox,
  2150. commit,
  2151. reset,
  2152. moduleFilter = document$$1.createElement("form"),
  2153. label = document$$1.createElement("label"),
  2154. moduleSearch = document$$1.createElement("input"),
  2155. dropDown = document$$1.createElement("div"),
  2156. actions = document$$1.createElement("span"),
  2157. dropDownList = document$$1.createElement("ul"),
  2158. dirty = false;
  2159. moduleSearch.id = "qunit-modulefilter-search";
  2160. addEvent(moduleSearch, "input", searchInput);
  2161. addEvent(moduleSearch, "input", searchFocus);
  2162. addEvent(moduleSearch, "focus", searchFocus);
  2163. addEvent(moduleSearch, "click", searchFocus);
  2164. label.id = "qunit-modulefilter-search-container";
  2165. label.innerHTML = "Module: ";
  2166. label.appendChild(moduleSearch);
  2167. actions.id = "qunit-modulefilter-actions";
  2168. actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
  2169. allCheckbox = actions.lastChild.firstChild;
  2170. commit = actions.firstChild;
  2171. reset = commit.nextSibling;
  2172. addEvent(commit, "click", applyUrlParams);
  2173. dropDownList.id = "qunit-modulefilter-dropdown-list";
  2174. dropDownList.innerHTML = moduleListHtml();
  2175. dropDown.id = "qunit-modulefilter-dropdown";
  2176. dropDown.style.display = "none";
  2177. dropDown.appendChild(actions);
  2178. dropDown.appendChild(dropDownList);
  2179. addEvent(dropDown, "change", selectionChange);
  2180. selectionChange();
  2181. moduleFilter.id = "qunit-modulefilter";
  2182. moduleFilter.appendChild(label);
  2183. moduleFilter.appendChild(dropDown);
  2184. addEvent(moduleFilter, "submit", interceptNavigation);
  2185. addEvent(moduleFilter, "reset", function () {
  2186. // Let the reset happen, then update styles
  2187. window.setTimeout(selectionChange);
  2188. });
  2189. // Enables show/hide for the dropdown
  2190. function searchFocus() {
  2191. if (dropDown.style.display !== "none") {
  2192. return;
  2193. }
  2194. dropDown.style.display = "block";
  2195. addEvent(document$$1, "click", hideHandler);
  2196. addEvent(document$$1, "keydown", hideHandler);
  2197. // Hide on Escape keydown or outside-container click
  2198. function hideHandler(e) {
  2199. var inContainer = moduleFilter.contains(e.target);
  2200. if (e.keyCode === 27 || !inContainer) {
  2201. if (e.keyCode === 27 && inContainer) {
  2202. moduleSearch.focus();
  2203. }
  2204. dropDown.style.display = "none";
  2205. removeEvent(document$$1, "click", hideHandler);
  2206. removeEvent(document$$1, "keydown", hideHandler);
  2207. moduleSearch.value = "";
  2208. searchInput();
  2209. }
  2210. }
  2211. }
  2212. // Processes module search box input
  2213. function searchInput() {
  2214. var i,
  2215. item,
  2216. searchText = moduleSearch.value.toLowerCase(),
  2217. listItems = dropDownList.children;
  2218. for (i = 0; i < listItems.length; i++) {
  2219. item = listItems[i];
  2220. if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
  2221. item.style.display = "";
  2222. } else {
  2223. item.style.display = "none";
  2224. }
  2225. }
  2226. }
  2227. // Processes selection changes
  2228. function selectionChange(evt) {
  2229. var i,
  2230. item,
  2231. checkbox = evt && evt.target || allCheckbox,
  2232. modulesList = dropDownList.getElementsByTagName("input"),
  2233. selectedNames = [];
  2234. toggleClass(checkbox.parentNode, "checked", checkbox.checked);
  2235. dirty = false;
  2236. if (checkbox.checked && checkbox !== allCheckbox) {
  2237. allCheckbox.checked = false;
  2238. removeClass(allCheckbox.parentNode, "checked");
  2239. }
  2240. for (i = 0; i < modulesList.length; i++) {
  2241. item = modulesList[i];
  2242. if (!evt) {
  2243. toggleClass(item.parentNode, "checked", item.checked);
  2244. } else if (checkbox === allCheckbox && checkbox.checked) {
  2245. item.checked = false;
  2246. removeClass(item.parentNode, "checked");
  2247. }
  2248. dirty = dirty || item.checked !== item.defaultChecked;
  2249. if (item.checked) {
  2250. selectedNames.push(item.parentNode.textContent);
  2251. }
  2252. }
  2253. commit.style.display = reset.style.display = dirty ? "" : "none";
  2254. moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
  2255. moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
  2256. }
  2257. return moduleFilter;
  2258. }
  2259. function appendToolbar() {
  2260. var toolbar = id("qunit-testrunner-toolbar");
  2261. if (toolbar) {
  2262. toolbar.appendChild(toolbarUrlConfigContainer());
  2263. toolbar.appendChild(toolbarModuleFilter());
  2264. toolbar.appendChild(toolbarLooseFilter());
  2265. toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
  2266. }
  2267. }
  2268. function appendHeader() {
  2269. var header = id("qunit-header");
  2270. if (header) {
  2271. header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
  2272. }
  2273. }
  2274. function appendBanner() {
  2275. var banner = id("qunit-banner");
  2276. if (banner) {
  2277. banner.className = "";
  2278. }
  2279. }
  2280. function appendTestResults() {
  2281. var tests = id("qunit-tests"),
  2282. result = id("qunit-testresult");
  2283. if (result) {
  2284. result.parentNode.removeChild(result);
  2285. }
  2286. if (tests) {
  2287. tests.innerHTML = "";
  2288. result = document$$1.createElement("p");
  2289. result.id = "qunit-testresult";
  2290. result.className = "result";
  2291. tests.parentNode.insertBefore(result, tests);
  2292. result.innerHTML = "Running...<br />&#160;";
  2293. }
  2294. }
  2295. function appendFilteredTest() {
  2296. var testId = QUnit.config.testId;
  2297. if (!testId || testId.length <= 0) {
  2298. return "";
  2299. }
  2300. return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
  2301. }
  2302. function appendUserAgent() {
  2303. var userAgent = id("qunit-userAgent");
  2304. if (userAgent) {
  2305. userAgent.innerHTML = "";
  2306. userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
  2307. }
  2308. }
  2309. function appendInterface() {
  2310. var qunit = id("qunit");
  2311. if (qunit) {
  2312. qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
  2313. }
  2314. appendHeader();
  2315. appendBanner();
  2316. appendTestResults();
  2317. appendUserAgent();
  2318. appendToolbar();
  2319. }
  2320. function appendTestsList(modules) {
  2321. var i, l, x, z, test, moduleObj;
  2322. for (i = 0, l = modules.length; i < l; i++) {
  2323. moduleObj = modules[i];
  2324. for (x = 0, z = moduleObj.tests.length; x < z; x++) {
  2325. test = moduleObj.tests[x];
  2326. appendTest(test.name, test.testId, moduleObj.name);
  2327. }
  2328. }
  2329. }
  2330. function appendTest(name, testId, moduleName) {
  2331. var title,
  2332. rerunTrigger,
  2333. testBlock,
  2334. assertList,
  2335. tests = id("qunit-tests");
  2336. if (!tests) {
  2337. return;
  2338. }
  2339. title = document$$1.createElement("strong");
  2340. title.innerHTML = getNameHtml(name, moduleName);
  2341. rerunTrigger = document$$1.createElement("a");
  2342. rerunTrigger.innerHTML = "Rerun";
  2343. rerunTrigger.href = setUrl({ testId: testId });
  2344. testBlock = document$$1.createElement("li");
  2345. testBlock.appendChild(title);
  2346. testBlock.appendChild(rerunTrigger);
  2347. testBlock.id = "qunit-test-output-" + testId;
  2348. assertList = document$$1.createElement("ol");
  2349. assertList.className = "qunit-assert-list";
  2350. testBlock.appendChild(assertList);
  2351. tests.appendChild(testBlock);
  2352. }
  2353. // HTML Reporter initialization and load
  2354. QUnit.begin(function (details) {
  2355. var i, moduleObj, tests;
  2356. // Sort modules by name for the picker
  2357. for (i = 0; i < details.modules.length; i++) {
  2358. moduleObj = details.modules[i];
  2359. if (moduleObj.name) {
  2360. modulesList.push(moduleObj.name);
  2361. }
  2362. }
  2363. modulesList.sort(function (a, b) {
  2364. return a.localeCompare(b);
  2365. });
  2366. // Initialize QUnit elements
  2367. appendInterface();
  2368. appendTestsList(details.modules);
  2369. tests = id("qunit-tests");
  2370. if (tests && config.hidepassed) {
  2371. addClass(tests, "hidepass");
  2372. }
  2373. });
  2374. QUnit.done(function (details) {
  2375. var banner = id("qunit-banner"),
  2376. tests = id("qunit-tests"),
  2377. 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("");
  2378. if (banner) {
  2379. banner.className = details.failed ? "qunit-fail" : "qunit-pass";
  2380. }
  2381. if (tests) {
  2382. id("qunit-testresult").innerHTML = html;
  2383. }
  2384. if (config.altertitle && document$$1.title) {
  2385. // Show ✖ for good, ✔ for bad suite result in title
  2386. // use escape sequences in case file gets loaded with non-utf-8-charset
  2387. document$$1.title = [details.failed ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
  2388. }
  2389. // Scroll back to top to show results
  2390. if (config.scrolltop && window.scrollTo) {
  2391. window.scrollTo(0, 0);
  2392. }
  2393. });
  2394. function getNameHtml(name, module) {
  2395. var nameHtml = "";
  2396. if (module) {
  2397. nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
  2398. }
  2399. nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
  2400. return nameHtml;
  2401. }
  2402. QUnit.testStart(function (details) {
  2403. var running, testBlock, bad;
  2404. testBlock = id("qunit-test-output-" + details.testId);
  2405. if (testBlock) {
  2406. testBlock.className = "running";
  2407. } else {
  2408. // Report later registered tests
  2409. appendTest(details.name, details.testId, details.module);
  2410. }
  2411. running = id("qunit-testresult");
  2412. if (running) {
  2413. bad = QUnit.config.reorder && details.previousFailure;
  2414. running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
  2415. }
  2416. });
  2417. function stripHtml(string) {
  2418. // Strip tags, html entity and whitespaces
  2419. return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
  2420. }
  2421. QUnit.log(function (details) {
  2422. var assertList,
  2423. assertLi,
  2424. message,
  2425. expected,
  2426. actual,
  2427. diff,
  2428. showDiff = false,
  2429. testItem = id("qunit-test-output-" + details.testId);
  2430. if (!testItem) {
  2431. return;
  2432. }
  2433. message = escapeText(details.message) || (details.result ? "okay" : "failed");
  2434. message = "<span class='test-message'>" + message + "</span>";
  2435. message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
  2436. // The pushFailure doesn't provide details.expected
  2437. // when it calls, it's implicit to also not show expected and diff stuff
  2438. // Also, we need to check details.expected existence, as it can exist and be undefined
  2439. if (!details.result && hasOwn.call(details, "expected")) {
  2440. if (details.negative) {
  2441. expected = "NOT " + QUnit.dump.parse(details.expected);
  2442. } else {
  2443. expected = QUnit.dump.parse(details.expected);
  2444. }
  2445. actual = QUnit.dump.parse(details.actual);
  2446. message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
  2447. if (actual !== expected) {
  2448. message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
  2449. // Don't show diff if actual or expected are booleans
  2450. if (!/^(true|false)$/.test(actual) && !/^(true|false)$/.test(expected)) {
  2451. diff = QUnit.diff(expected, actual);
  2452. showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
  2453. }
  2454. // Don't show diff if expected and actual are totally different
  2455. if (showDiff) {
  2456. message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
  2457. }
  2458. } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
  2459. 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>";
  2460. } else {
  2461. message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
  2462. }
  2463. if (details.source) {
  2464. message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
  2465. }
  2466. message += "</table>";
  2467. // This occurs when pushFailure is set and we have an extracted stack trace
  2468. } else if (!details.result && details.source) {
  2469. message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
  2470. }
  2471. assertList = testItem.getElementsByTagName("ol")[0];
  2472. assertLi = document$$1.createElement("li");
  2473. assertLi.className = details.result ? "pass" : "fail";
  2474. assertLi.innerHTML = message;
  2475. assertList.appendChild(assertLi);
  2476. });
  2477. QUnit.testDone(function (details) {
  2478. var testTitle,
  2479. time,
  2480. testItem,
  2481. assertList,
  2482. good,
  2483. bad,
  2484. testCounts,
  2485. skipped,
  2486. sourceName,
  2487. tests = id("qunit-tests");
  2488. if (!tests) {
  2489. return;
  2490. }
  2491. testItem = id("qunit-test-output-" + details.testId);
  2492. assertList = testItem.getElementsByTagName("ol")[0];
  2493. good = details.passed;
  2494. bad = details.failed;
  2495. if (bad === 0) {
  2496. // Collapse the passing tests
  2497. addClass(assertList, "qunit-collapsed");
  2498. } else if (config.collapse) {
  2499. if (!collapseNext) {
  2500. // Skip collapsing the first failing test
  2501. collapseNext = true;
  2502. } else {
  2503. // Collapse remaining tests
  2504. addClass(assertList, "qunit-collapsed");
  2505. }
  2506. }
  2507. // The testItem.firstChild is the test name
  2508. testTitle = testItem.firstChild;
  2509. testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
  2510. testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
  2511. if (details.skipped) {
  2512. testItem.className = "skipped";
  2513. skipped = document$$1.createElement("em");
  2514. skipped.className = "qunit-skipped-label";
  2515. skipped.innerHTML = "skipped";
  2516. testItem.insertBefore(skipped, testTitle);
  2517. } else {
  2518. addEvent(testTitle, "click", function () {
  2519. toggleClass(assertList, "qunit-collapsed");
  2520. });
  2521. testItem.className = bad ? "fail" : "pass";
  2522. time = document$$1.createElement("span");
  2523. time.className = "runtime";
  2524. time.innerHTML = details.runtime + " ms";
  2525. testItem.insertBefore(time, assertList);
  2526. }
  2527. // Show the source of the test when showing assertions
  2528. if (details.source) {
  2529. sourceName = document$$1.createElement("p");
  2530. sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
  2531. addClass(sourceName, "qunit-source");
  2532. if (bad === 0) {
  2533. addClass(sourceName, "qunit-collapsed");
  2534. }
  2535. addEvent(testTitle, "click", function () {
  2536. toggleClass(sourceName, "qunit-collapsed");
  2537. });
  2538. testItem.appendChild(sourceName);
  2539. }
  2540. });
  2541. // Avoid readyState issue with phantomjs
  2542. // Ref: #818
  2543. var notPhantom = function (p) {
  2544. return !(p && p.version && p.version.major > 0);
  2545. }(window.phantom);
  2546. if (notPhantom && document$$1.readyState === "complete") {
  2547. QUnit.load();
  2548. } else {
  2549. addEvent(window, "load", QUnit.load);
  2550. }
  2551. })();
  2552. /*
  2553. * This file is a modified version of google-diff-match-patch's JavaScript implementation
  2554. * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
  2555. * modifications are licensed as more fully set forth in LICENSE.txt.
  2556. *
  2557. * The original source of google-diff-match-patch is attributable and licensed as follows:
  2558. *
  2559. * Copyright 2006 Google Inc.
  2560. * https://code.google.com/p/google-diff-match-patch/
  2561. *
  2562. * Licensed under the Apache License, Version 2.0 (the "License");
  2563. * you may not use this file except in compliance with the License.
  2564. * You may obtain a copy of the License at
  2565. *
  2566. * https://www.apache.org/licenses/LICENSE-2.0
  2567. *
  2568. * Unless required by applicable law or agreed to in writing, software
  2569. * distributed under the License is distributed on an "AS IS" BASIS,
  2570. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2571. * See the License for the specific language governing permissions and
  2572. * limitations under the License.
  2573. *
  2574. * More Info:
  2575. * https://code.google.com/p/google-diff-match-patch/
  2576. *
  2577. * Usage: QUnit.diff(expected, actual)
  2578. *
  2579. */
  2580. QUnit.diff = function () {
  2581. function DiffMatchPatch() {}
  2582. // DIFF FUNCTIONS
  2583. /**
  2584. * The data structure representing a diff is an array of tuples:
  2585. * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
  2586. * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
  2587. */
  2588. var DIFF_DELETE = -1,
  2589. DIFF_INSERT = 1,
  2590. DIFF_EQUAL = 0;
  2591. /**
  2592. * Find the differences between two texts. Simplifies the problem by stripping
  2593. * any common prefix or suffix off the texts before diffing.
  2594. * @param {string} text1 Old string to be diffed.
  2595. * @param {string} text2 New string to be diffed.
  2596. * @param {boolean=} optChecklines Optional speedup flag. If present and false,
  2597. * then don't run a line-level diff first to identify the changed areas.
  2598. * Defaults to true, which does a faster, slightly less optimal diff.
  2599. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  2600. */
  2601. DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
  2602. var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
  2603. // The diff must be complete in up to 1 second.
  2604. deadline = new Date().getTime() + 1000;
  2605. // Check for null inputs.
  2606. if (text1 === null || text2 === null) {
  2607. throw new Error("Null input. (DiffMain)");
  2608. }
  2609. // Check for equality (speedup).
  2610. if (text1 === text2) {
  2611. if (text1) {
  2612. return [[DIFF_EQUAL, text1]];
  2613. }
  2614. return [];
  2615. }
  2616. if (typeof optChecklines === "undefined") {
  2617. optChecklines = true;
  2618. }
  2619. checklines = optChecklines;
  2620. // Trim off common prefix (speedup).
  2621. commonlength = this.diffCommonPrefix(text1, text2);
  2622. commonprefix = text1.substring(0, commonlength);
  2623. text1 = text1.substring(commonlength);
  2624. text2 = text2.substring(commonlength);
  2625. // Trim off common suffix (speedup).
  2626. commonlength = this.diffCommonSuffix(text1, text2);
  2627. commonsuffix = text1.substring(text1.length - commonlength);
  2628. text1 = text1.substring(0, text1.length - commonlength);
  2629. text2 = text2.substring(0, text2.length - commonlength);
  2630. // Compute the diff on the middle block.
  2631. diffs = this.diffCompute(text1, text2, checklines, deadline);
  2632. // Restore the prefix and suffix.
  2633. if (commonprefix) {
  2634. diffs.unshift([DIFF_EQUAL, commonprefix]);
  2635. }
  2636. if (commonsuffix) {
  2637. diffs.push([DIFF_EQUAL, commonsuffix]);
  2638. }
  2639. this.diffCleanupMerge(diffs);
  2640. return diffs;
  2641. };
  2642. /**
  2643. * Reduce the number of edits by eliminating operationally trivial equalities.
  2644. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  2645. */
  2646. DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
  2647. var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
  2648. changes = false;
  2649. equalities = []; // Stack of indices where equalities are found.
  2650. equalitiesLength = 0; // Keeping our own length var is faster in JS.
  2651. /** @type {?string} */
  2652. lastequality = null;
  2653. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
  2654. pointer = 0; // Index of current position.
  2655. // Is there an insertion operation before the last equality.
  2656. preIns = false;
  2657. // Is there a deletion operation before the last equality.
  2658. preDel = false;
  2659. // Is there an insertion operation after the last equality.
  2660. postIns = false;
  2661. // Is there a deletion operation after the last equality.
  2662. postDel = false;
  2663. while (pointer < diffs.length) {
  2664. // Equality found.
  2665. if (diffs[pointer][0] === DIFF_EQUAL) {
  2666. if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
  2667. // Candidate found.
  2668. equalities[equalitiesLength++] = pointer;
  2669. preIns = postIns;
  2670. preDel = postDel;
  2671. lastequality = diffs[pointer][1];
  2672. } else {
  2673. // Not a candidate, and can never become one.
  2674. equalitiesLength = 0;
  2675. lastequality = null;
  2676. }
  2677. postIns = postDel = false;
  2678. // An insertion or deletion.
  2679. } else {
  2680. if (diffs[pointer][0] === DIFF_DELETE) {
  2681. postDel = true;
  2682. } else {
  2683. postIns = true;
  2684. }
  2685. /*
  2686. * Five types to be split:
  2687. * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
  2688. * <ins>A</ins>X<ins>C</ins><del>D</del>
  2689. * <ins>A</ins><del>B</del>X<ins>C</ins>
  2690. * <ins>A</del>X<ins>C</ins><del>D</del>
  2691. * <ins>A</ins><del>B</del>X<del>C</del>
  2692. */
  2693. if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
  2694. // Duplicate record.
  2695. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  2696. // Change second copy to insert.
  2697. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  2698. equalitiesLength--; // Throw away the equality we just deleted;
  2699. lastequality = null;
  2700. if (preIns && preDel) {
  2701. // No changes made which could affect previous entry, keep going.
  2702. postIns = postDel = true;
  2703. equalitiesLength = 0;
  2704. } else {
  2705. equalitiesLength--; // Throw away the previous equality.
  2706. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  2707. postIns = postDel = false;
  2708. }
  2709. changes = true;
  2710. }
  2711. }
  2712. pointer++;
  2713. }
  2714. if (changes) {
  2715. this.diffCleanupMerge(diffs);
  2716. }
  2717. };
  2718. /**
  2719. * Convert a diff array into a pretty HTML report.
  2720. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  2721. * @param {integer} string to be beautified.
  2722. * @return {string} HTML representation.
  2723. */
  2724. DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
  2725. var op,
  2726. data,
  2727. x,
  2728. html = [];
  2729. for (x = 0; x < diffs.length; x++) {
  2730. op = diffs[x][0]; // Operation (insert, delete, equal)
  2731. data = diffs[x][1]; // Text of change.
  2732. switch (op) {
  2733. case DIFF_INSERT:
  2734. html[x] = "<ins>" + escapeText(data) + "</ins>";
  2735. break;
  2736. case DIFF_DELETE:
  2737. html[x] = "<del>" + escapeText(data) + "</del>";
  2738. break;
  2739. case DIFF_EQUAL:
  2740. html[x] = "<span>" + escapeText(data) + "</span>";
  2741. break;
  2742. }
  2743. }
  2744. return html.join("");
  2745. };
  2746. /**
  2747. * Determine the common prefix of two strings.
  2748. * @param {string} text1 First string.
  2749. * @param {string} text2 Second string.
  2750. * @return {number} The number of characters common to the start of each
  2751. * string.
  2752. */
  2753. DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
  2754. var pointermid, pointermax, pointermin, pointerstart;
  2755. // Quick check for common null cases.
  2756. if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
  2757. return 0;
  2758. }
  2759. // Binary search.
  2760. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  2761. pointermin = 0;
  2762. pointermax = Math.min(text1.length, text2.length);
  2763. pointermid = pointermax;
  2764. pointerstart = 0;
  2765. while (pointermin < pointermid) {
  2766. if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
  2767. pointermin = pointermid;
  2768. pointerstart = pointermin;
  2769. } else {
  2770. pointermax = pointermid;
  2771. }
  2772. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  2773. }
  2774. return pointermid;
  2775. };
  2776. /**
  2777. * Determine the common suffix of two strings.
  2778. * @param {string} text1 First string.
  2779. * @param {string} text2 Second string.
  2780. * @return {number} The number of characters common to the end of each string.
  2781. */
  2782. DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
  2783. var pointermid, pointermax, pointermin, pointerend;
  2784. // Quick check for common null cases.
  2785. if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
  2786. return 0;
  2787. }
  2788. // Binary search.
  2789. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  2790. pointermin = 0;
  2791. pointermax = Math.min(text1.length, text2.length);
  2792. pointermid = pointermax;
  2793. pointerend = 0;
  2794. while (pointermin < pointermid) {
  2795. if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
  2796. pointermin = pointermid;
  2797. pointerend = pointermin;
  2798. } else {
  2799. pointermax = pointermid;
  2800. }
  2801. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  2802. }
  2803. return pointermid;
  2804. };
  2805. /**
  2806. * Find the differences between two texts. Assumes that the texts do not
  2807. * have any common prefix or suffix.
  2808. * @param {string} text1 Old string to be diffed.
  2809. * @param {string} text2 New string to be diffed.
  2810. * @param {boolean} checklines Speedup flag. If false, then don't run a
  2811. * line-level diff first to identify the changed areas.
  2812. * If true, then run a faster, slightly less optimal diff.
  2813. * @param {number} deadline Time when the diff should be complete by.
  2814. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  2815. * @private
  2816. */
  2817. DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
  2818. var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
  2819. if (!text1) {
  2820. // Just add some text (speedup).
  2821. return [[DIFF_INSERT, text2]];
  2822. }
  2823. if (!text2) {
  2824. // Just delete some text (speedup).
  2825. return [[DIFF_DELETE, text1]];
  2826. }
  2827. longtext = text1.length > text2.length ? text1 : text2;
  2828. shorttext = text1.length > text2.length ? text2 : text1;
  2829. i = longtext.indexOf(shorttext);
  2830. if (i !== -1) {
  2831. // Shorter text is inside the longer text (speedup).
  2832. diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
  2833. // Swap insertions for deletions if diff is reversed.
  2834. if (text1.length > text2.length) {
  2835. diffs[0][0] = diffs[2][0] = DIFF_DELETE;
  2836. }
  2837. return diffs;
  2838. }
  2839. if (shorttext.length === 1) {
  2840. // Single character string.
  2841. // After the previous speedup, the character can't be an equality.
  2842. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  2843. }
  2844. // Check to see if the problem can be split in two.
  2845. hm = this.diffHalfMatch(text1, text2);
  2846. if (hm) {
  2847. // A half-match was found, sort out the return data.
  2848. text1A = hm[0];
  2849. text1B = hm[1];
  2850. text2A = hm[2];
  2851. text2B = hm[3];
  2852. midCommon = hm[4];
  2853. // Send both pairs off for separate processing.
  2854. diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
  2855. diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
  2856. // Merge the results.
  2857. return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
  2858. }
  2859. if (checklines && text1.length > 100 && text2.length > 100) {
  2860. return this.diffLineMode(text1, text2, deadline);
  2861. }
  2862. return this.diffBisect(text1, text2, deadline);
  2863. };
  2864. /**
  2865. * Do the two texts share a substring which is at least half the length of the
  2866. * longer text?
  2867. * This speedup can produce non-minimal diffs.
  2868. * @param {string} text1 First string.
  2869. * @param {string} text2 Second string.
  2870. * @return {Array.<string>} Five element Array, containing the prefix of
  2871. * text1, the suffix of text1, the prefix of text2, the suffix of
  2872. * text2 and the common middle. Or null if there was no match.
  2873. * @private
  2874. */
  2875. DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
  2876. var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
  2877. longtext = text1.length > text2.length ? text1 : text2;
  2878. shorttext = text1.length > text2.length ? text2 : text1;
  2879. if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
  2880. return null; // Pointless.
  2881. }
  2882. dmp = this; // 'this' becomes 'window' in a closure.
  2883. /**
  2884. * Does a substring of shorttext exist within longtext such that the substring
  2885. * is at least half the length of longtext?
  2886. * Closure, but does not reference any external variables.
  2887. * @param {string} longtext Longer string.
  2888. * @param {string} shorttext Shorter string.
  2889. * @param {number} i Start index of quarter length substring within longtext.
  2890. * @return {Array.<string>} Five element Array, containing the prefix of
  2891. * longtext, the suffix of longtext, the prefix of shorttext, the suffix
  2892. * of shorttext and the common middle. Or null if there was no match.
  2893. * @private
  2894. */
  2895. function diffHalfMatchI(longtext, shorttext, i) {
  2896. var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
  2897. // Start with a 1/4 length substring at position i as a seed.
  2898. seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
  2899. j = -1;
  2900. bestCommon = "";
  2901. while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
  2902. prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
  2903. suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
  2904. if (bestCommon.length < suffixLength + prefixLength) {
  2905. bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
  2906. bestLongtextA = longtext.substring(0, i - suffixLength);
  2907. bestLongtextB = longtext.substring(i + prefixLength);
  2908. bestShorttextA = shorttext.substring(0, j - suffixLength);
  2909. bestShorttextB = shorttext.substring(j + prefixLength);
  2910. }
  2911. }
  2912. if (bestCommon.length * 2 >= longtext.length) {
  2913. return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
  2914. } else {
  2915. return null;
  2916. }
  2917. }
  2918. // First check if the second quarter is the seed for a half-match.
  2919. hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
  2920. // Check again based on the third quarter.
  2921. hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
  2922. if (!hm1 && !hm2) {
  2923. return null;
  2924. } else if (!hm2) {
  2925. hm = hm1;
  2926. } else if (!hm1) {
  2927. hm = hm2;
  2928. } else {
  2929. // Both matched. Select the longest.
  2930. hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
  2931. }
  2932. // A half-match was found, sort out the return data.
  2933. if (text1.length > text2.length) {
  2934. text1A = hm[0];
  2935. text1B = hm[1];
  2936. text2A = hm[2];
  2937. text2B = hm[3];
  2938. } else {
  2939. text2A = hm[0];
  2940. text2B = hm[1];
  2941. text1A = hm[2];
  2942. text1B = hm[3];
  2943. }
  2944. midCommon = hm[4];
  2945. return [text1A, text1B, text2A, text2B, midCommon];
  2946. };
  2947. /**
  2948. * Do a quick line-level diff on both strings, then rediff the parts for
  2949. * greater accuracy.
  2950. * This speedup can produce non-minimal diffs.
  2951. * @param {string} text1 Old string to be diffed.
  2952. * @param {string} text2 New string to be diffed.
  2953. * @param {number} deadline Time when the diff should be complete by.
  2954. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  2955. * @private
  2956. */
  2957. DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
  2958. var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
  2959. // Scan the text on a line-by-line basis first.
  2960. a = this.diffLinesToChars(text1, text2);
  2961. text1 = a.chars1;
  2962. text2 = a.chars2;
  2963. linearray = a.lineArray;
  2964. diffs = this.DiffMain(text1, text2, false, deadline);
  2965. // Convert the diff back to original text.
  2966. this.diffCharsToLines(diffs, linearray);
  2967. // Eliminate freak matches (e.g. blank lines)
  2968. this.diffCleanupSemantic(diffs);
  2969. // Rediff any replacement blocks, this time character-by-character.
  2970. // Add a dummy entry at the end.
  2971. diffs.push([DIFF_EQUAL, ""]);
  2972. pointer = 0;
  2973. countDelete = 0;
  2974. countInsert = 0;
  2975. textDelete = "";
  2976. textInsert = "";
  2977. while (pointer < diffs.length) {
  2978. switch (diffs[pointer][0]) {
  2979. case DIFF_INSERT:
  2980. countInsert++;
  2981. textInsert += diffs[pointer][1];
  2982. break;
  2983. case DIFF_DELETE:
  2984. countDelete++;
  2985. textDelete += diffs[pointer][1];
  2986. break;
  2987. case DIFF_EQUAL:
  2988. // Upon reaching an equality, check for prior redundancies.
  2989. if (countDelete >= 1 && countInsert >= 1) {
  2990. // Delete the offending records and add the merged ones.
  2991. diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
  2992. pointer = pointer - countDelete - countInsert;
  2993. a = this.DiffMain(textDelete, textInsert, false, deadline);
  2994. for (j = a.length - 1; j >= 0; j--) {
  2995. diffs.splice(pointer, 0, a[j]);
  2996. }
  2997. pointer = pointer + a.length;
  2998. }
  2999. countInsert = 0;
  3000. countDelete = 0;
  3001. textDelete = "";
  3002. textInsert = "";
  3003. break;
  3004. }
  3005. pointer++;
  3006. }
  3007. diffs.pop(); // Remove the dummy entry at the end.
  3008. return diffs;
  3009. };
  3010. /**
  3011. * Find the 'middle snake' of a diff, split the problem in two
  3012. * and return the recursively constructed diff.
  3013. * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
  3014. * @param {string} text1 Old string to be diffed.
  3015. * @param {string} text2 New string to be diffed.
  3016. * @param {number} deadline Time at which to bail if not yet complete.
  3017. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3018. * @private
  3019. */
  3020. DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
  3021. var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
  3022. // Cache the text lengths to prevent multiple calls.
  3023. text1Length = text1.length;
  3024. text2Length = text2.length;
  3025. maxD = Math.ceil((text1Length + text2Length) / 2);
  3026. vOffset = maxD;
  3027. vLength = 2 * maxD;
  3028. v1 = new Array(vLength);
  3029. v2 = new Array(vLength);
  3030. // Setting all elements to -1 is faster in Chrome & Firefox than mixing
  3031. // integers and undefined.
  3032. for (x = 0; x < vLength; x++) {
  3033. v1[x] = -1;
  3034. v2[x] = -1;
  3035. }
  3036. v1[vOffset + 1] = 0;
  3037. v2[vOffset + 1] = 0;
  3038. delta = text1Length - text2Length;
  3039. // If the total number of characters is odd, then the front path will collide
  3040. // with the reverse path.
  3041. front = delta % 2 !== 0;
  3042. // Offsets for start and end of k loop.
  3043. // Prevents mapping of space beyond the grid.
  3044. k1start = 0;
  3045. k1end = 0;
  3046. k2start = 0;
  3047. k2end = 0;
  3048. for (d = 0; d < maxD; d++) {
  3049. // Bail out if deadline is reached.
  3050. if (new Date().getTime() > deadline) {
  3051. break;
  3052. }
  3053. // Walk the front path one step.
  3054. for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
  3055. k1Offset = vOffset + k1;
  3056. if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
  3057. x1 = v1[k1Offset + 1];
  3058. } else {
  3059. x1 = v1[k1Offset - 1] + 1;
  3060. }
  3061. y1 = x1 - k1;
  3062. while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
  3063. x1++;
  3064. y1++;
  3065. }
  3066. v1[k1Offset] = x1;
  3067. if (x1 > text1Length) {
  3068. // Ran off the right of the graph.
  3069. k1end += 2;
  3070. } else if (y1 > text2Length) {
  3071. // Ran off the bottom of the graph.
  3072. k1start += 2;
  3073. } else if (front) {
  3074. k2Offset = vOffset + delta - k1;
  3075. if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
  3076. // Mirror x2 onto top-left coordinate system.
  3077. x2 = text1Length - v2[k2Offset];
  3078. if (x1 >= x2) {
  3079. // Overlap detected.
  3080. return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  3081. }
  3082. }
  3083. }
  3084. }
  3085. // Walk the reverse path one step.
  3086. for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
  3087. k2Offset = vOffset + k2;
  3088. if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
  3089. x2 = v2[k2Offset + 1];
  3090. } else {
  3091. x2 = v2[k2Offset - 1] + 1;
  3092. }
  3093. y2 = x2 - k2;
  3094. while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
  3095. x2++;
  3096. y2++;
  3097. }
  3098. v2[k2Offset] = x2;
  3099. if (x2 > text1Length) {
  3100. // Ran off the left of the graph.
  3101. k2end += 2;
  3102. } else if (y2 > text2Length) {
  3103. // Ran off the top of the graph.
  3104. k2start += 2;
  3105. } else if (!front) {
  3106. k1Offset = vOffset + delta - k2;
  3107. if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
  3108. x1 = v1[k1Offset];
  3109. y1 = vOffset + x1 - k1Offset;
  3110. // Mirror x2 onto top-left coordinate system.
  3111. x2 = text1Length - x2;
  3112. if (x1 >= x2) {
  3113. // Overlap detected.
  3114. return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  3115. }
  3116. }
  3117. }
  3118. }
  3119. }
  3120. // Diff took too long and hit the deadline or
  3121. // number of diffs equals number of characters, no commonality at all.
  3122. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  3123. };
  3124. /**
  3125. * Given the location of the 'middle snake', split the diff in two parts
  3126. * and recurse.
  3127. * @param {string} text1 Old string to be diffed.
  3128. * @param {string} text2 New string to be diffed.
  3129. * @param {number} x Index of split point in text1.
  3130. * @param {number} y Index of split point in text2.
  3131. * @param {number} deadline Time at which to bail if not yet complete.
  3132. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3133. * @private
  3134. */
  3135. DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
  3136. var text1a, text1b, text2a, text2b, diffs, diffsb;
  3137. text1a = text1.substring(0, x);
  3138. text2a = text2.substring(0, y);
  3139. text1b = text1.substring(x);
  3140. text2b = text2.substring(y);
  3141. // Compute both diffs serially.
  3142. diffs = this.DiffMain(text1a, text2a, false, deadline);
  3143. diffsb = this.DiffMain(text1b, text2b, false, deadline);
  3144. return diffs.concat(diffsb);
  3145. };
  3146. /**
  3147. * Reduce the number of edits by eliminating semantically trivial equalities.
  3148. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3149. */
  3150. DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
  3151. var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
  3152. changes = false;
  3153. equalities = []; // Stack of indices where equalities are found.
  3154. equalitiesLength = 0; // Keeping our own length var is faster in JS.
  3155. /** @type {?string} */
  3156. lastequality = null;
  3157. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
  3158. pointer = 0; // Index of current position.
  3159. // Number of characters that changed prior to the equality.
  3160. lengthInsertions1 = 0;
  3161. lengthDeletions1 = 0;
  3162. // Number of characters that changed after the equality.
  3163. lengthInsertions2 = 0;
  3164. lengthDeletions2 = 0;
  3165. while (pointer < diffs.length) {
  3166. if (diffs[pointer][0] === DIFF_EQUAL) {
  3167. // Equality found.
  3168. equalities[equalitiesLength++] = pointer;
  3169. lengthInsertions1 = lengthInsertions2;
  3170. lengthDeletions1 = lengthDeletions2;
  3171. lengthInsertions2 = 0;
  3172. lengthDeletions2 = 0;
  3173. lastequality = diffs[pointer][1];
  3174. } else {
  3175. // An insertion or deletion.
  3176. if (diffs[pointer][0] === DIFF_INSERT) {
  3177. lengthInsertions2 += diffs[pointer][1].length;
  3178. } else {
  3179. lengthDeletions2 += diffs[pointer][1].length;
  3180. }
  3181. // Eliminate an equality that is smaller or equal to the edits on both
  3182. // sides of it.
  3183. if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
  3184. // Duplicate record.
  3185. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  3186. // Change second copy to insert.
  3187. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  3188. // Throw away the equality we just deleted.
  3189. equalitiesLength--;
  3190. // Throw away the previous equality (it needs to be reevaluated).
  3191. equalitiesLength--;
  3192. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  3193. // Reset the counters.
  3194. lengthInsertions1 = 0;
  3195. lengthDeletions1 = 0;
  3196. lengthInsertions2 = 0;
  3197. lengthDeletions2 = 0;
  3198. lastequality = null;
  3199. changes = true;
  3200. }
  3201. }
  3202. pointer++;
  3203. }
  3204. // Normalize the diff.
  3205. if (changes) {
  3206. this.diffCleanupMerge(diffs);
  3207. }
  3208. // Find any overlaps between deletions and insertions.
  3209. // e.g: <del>abcxxx</del><ins>xxxdef</ins>
  3210. // -> <del>abc</del>xxx<ins>def</ins>
  3211. // e.g: <del>xxxabc</del><ins>defxxx</ins>
  3212. // -> <ins>def</ins>xxx<del>abc</del>
  3213. // Only extract an overlap if it is as big as the edit ahead or behind it.
  3214. pointer = 1;
  3215. while (pointer < diffs.length) {
  3216. if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
  3217. deletion = diffs[pointer - 1][1];
  3218. insertion = diffs[pointer][1];
  3219. overlapLength1 = this.diffCommonOverlap(deletion, insertion);
  3220. overlapLength2 = this.diffCommonOverlap(insertion, deletion);
  3221. if (overlapLength1 >= overlapLength2) {
  3222. if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
  3223. // Overlap found. Insert an equality and trim the surrounding edits.
  3224. diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
  3225. diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
  3226. diffs[pointer + 1][1] = insertion.substring(overlapLength1);
  3227. pointer++;
  3228. }
  3229. } else {
  3230. if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
  3231. // Reverse overlap found.
  3232. // Insert an equality and swap and trim the surrounding edits.
  3233. diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
  3234. diffs[pointer - 1][0] = DIFF_INSERT;
  3235. diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
  3236. diffs[pointer + 1][0] = DIFF_DELETE;
  3237. diffs[pointer + 1][1] = deletion.substring(overlapLength2);
  3238. pointer++;
  3239. }
  3240. }
  3241. pointer++;
  3242. }
  3243. pointer++;
  3244. }
  3245. };
  3246. /**
  3247. * Determine if the suffix of one string is the prefix of another.
  3248. * @param {string} text1 First string.
  3249. * @param {string} text2 Second string.
  3250. * @return {number} The number of characters common to the end of the first
  3251. * string and the start of the second string.
  3252. * @private
  3253. */
  3254. DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
  3255. var text1Length, text2Length, textLength, best, length, pattern, found;
  3256. // Cache the text lengths to prevent multiple calls.
  3257. text1Length = text1.length;
  3258. text2Length = text2.length;
  3259. // Eliminate the null case.
  3260. if (text1Length === 0 || text2Length === 0) {
  3261. return 0;
  3262. }
  3263. // Truncate the longer string.
  3264. if (text1Length > text2Length) {
  3265. text1 = text1.substring(text1Length - text2Length);
  3266. } else if (text1Length < text2Length) {
  3267. text2 = text2.substring(0, text1Length);
  3268. }
  3269. textLength = Math.min(text1Length, text2Length);
  3270. // Quick check for the worst case.
  3271. if (text1 === text2) {
  3272. return textLength;
  3273. }
  3274. // Start by looking for a single character match
  3275. // and increase length until no match is found.
  3276. // Performance analysis: https://neil.fraser.name/news/2010/11/04/
  3277. best = 0;
  3278. length = 1;
  3279. while (true) {
  3280. pattern = text1.substring(textLength - length);
  3281. found = text2.indexOf(pattern);
  3282. if (found === -1) {
  3283. return best;
  3284. }
  3285. length += found;
  3286. if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
  3287. best = length;
  3288. length++;
  3289. }
  3290. }
  3291. };
  3292. /**
  3293. * Split two texts into an array of strings. Reduce the texts to a string of
  3294. * hashes where each Unicode character represents one line.
  3295. * @param {string} text1 First string.
  3296. * @param {string} text2 Second string.
  3297. * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
  3298. * An object containing the encoded text1, the encoded text2 and
  3299. * the array of unique strings.
  3300. * The zeroth element of the array of unique strings is intentionally blank.
  3301. * @private
  3302. */
  3303. DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
  3304. var lineArray, lineHash, chars1, chars2;
  3305. lineArray = []; // E.g. lineArray[4] === 'Hello\n'
  3306. lineHash = {}; // E.g. lineHash['Hello\n'] === 4
  3307. // '\x00' is a valid character, but various debuggers don't like it.
  3308. // So we'll insert a junk entry to avoid generating a null character.
  3309. lineArray[0] = "";
  3310. /**
  3311. * Split a text into an array of strings. Reduce the texts to a string of
  3312. * hashes where each Unicode character represents one line.
  3313. * Modifies linearray and linehash through being a closure.
  3314. * @param {string} text String to encode.
  3315. * @return {string} Encoded string.
  3316. * @private
  3317. */
  3318. function diffLinesToCharsMunge(text) {
  3319. var chars, lineStart, lineEnd, lineArrayLength, line;
  3320. chars = "";
  3321. // Walk the text, pulling out a substring for each line.
  3322. // text.split('\n') would would temporarily double our memory footprint.
  3323. // Modifying text would create many large strings to garbage collect.
  3324. lineStart = 0;
  3325. lineEnd = -1;
  3326. // Keeping our own length variable is faster than looking it up.
  3327. lineArrayLength = lineArray.length;
  3328. while (lineEnd < text.length - 1) {
  3329. lineEnd = text.indexOf("\n", lineStart);
  3330. if (lineEnd === -1) {
  3331. lineEnd = text.length - 1;
  3332. }
  3333. line = text.substring(lineStart, lineEnd + 1);
  3334. lineStart = lineEnd + 1;
  3335. if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
  3336. chars += String.fromCharCode(lineHash[line]);
  3337. } else {
  3338. chars += String.fromCharCode(lineArrayLength);
  3339. lineHash[line] = lineArrayLength;
  3340. lineArray[lineArrayLength++] = line;
  3341. }
  3342. }
  3343. return chars;
  3344. }
  3345. chars1 = diffLinesToCharsMunge(text1);
  3346. chars2 = diffLinesToCharsMunge(text2);
  3347. return {
  3348. chars1: chars1,
  3349. chars2: chars2,
  3350. lineArray: lineArray
  3351. };
  3352. };
  3353. /**
  3354. * Rehydrate the text in a diff from a string of line hashes to real lines of
  3355. * text.
  3356. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3357. * @param {!Array.<string>} lineArray Array of unique strings.
  3358. * @private
  3359. */
  3360. DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
  3361. var x, chars, text, y;
  3362. for (x = 0; x < diffs.length; x++) {
  3363. chars = diffs[x][1];
  3364. text = [];
  3365. for (y = 0; y < chars.length; y++) {
  3366. text[y] = lineArray[chars.charCodeAt(y)];
  3367. }
  3368. diffs[x][1] = text.join("");
  3369. }
  3370. };
  3371. /**
  3372. * Reorder and merge like edit sections. Merge equalities.
  3373. * Any edit section can move as long as it doesn't cross an equality.
  3374. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3375. */
  3376. DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
  3377. var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
  3378. diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
  3379. pointer = 0;
  3380. countDelete = 0;
  3381. countInsert = 0;
  3382. textDelete = "";
  3383. textInsert = "";
  3384. while (pointer < diffs.length) {
  3385. switch (diffs[pointer][0]) {
  3386. case DIFF_INSERT:
  3387. countInsert++;
  3388. textInsert += diffs[pointer][1];
  3389. pointer++;
  3390. break;
  3391. case DIFF_DELETE:
  3392. countDelete++;
  3393. textDelete += diffs[pointer][1];
  3394. pointer++;
  3395. break;
  3396. case DIFF_EQUAL:
  3397. // Upon reaching an equality, check for prior redundancies.
  3398. if (countDelete + countInsert > 1) {
  3399. if (countDelete !== 0 && countInsert !== 0) {
  3400. // Factor out any common prefixes.
  3401. commonlength = this.diffCommonPrefix(textInsert, textDelete);
  3402. if (commonlength !== 0) {
  3403. if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
  3404. diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
  3405. } else {
  3406. diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
  3407. pointer++;
  3408. }
  3409. textInsert = textInsert.substring(commonlength);
  3410. textDelete = textDelete.substring(commonlength);
  3411. }
  3412. // Factor out any common suffixies.
  3413. commonlength = this.diffCommonSuffix(textInsert, textDelete);
  3414. if (commonlength !== 0) {
  3415. diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
  3416. textInsert = textInsert.substring(0, textInsert.length - commonlength);
  3417. textDelete = textDelete.substring(0, textDelete.length - commonlength);
  3418. }
  3419. }
  3420. // Delete the offending records and add the merged ones.
  3421. if (countDelete === 0) {
  3422. diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
  3423. } else if (countInsert === 0) {
  3424. diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
  3425. } else {
  3426. diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
  3427. }
  3428. pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
  3429. } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
  3430. // Merge this equality with the previous one.
  3431. diffs[pointer - 1][1] += diffs[pointer][1];
  3432. diffs.splice(pointer, 1);
  3433. } else {
  3434. pointer++;
  3435. }
  3436. countInsert = 0;
  3437. countDelete = 0;
  3438. textDelete = "";
  3439. textInsert = "";
  3440. break;
  3441. }
  3442. }
  3443. if (diffs[diffs.length - 1][1] === "") {
  3444. diffs.pop(); // Remove the dummy entry at the end.
  3445. }
  3446. // Second pass: look for single edits surrounded on both sides by equalities
  3447. // which can be shifted sideways to eliminate an equality.
  3448. // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
  3449. changes = false;
  3450. pointer = 1;
  3451. // Intentionally ignore the first and last element (don't need checking).
  3452. while (pointer < diffs.length - 1) {
  3453. if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
  3454. diffPointer = diffs[pointer][1];
  3455. position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
  3456. // This is a single edit surrounded by equalities.
  3457. if (position === diffs[pointer - 1][1]) {
  3458. // Shift the edit over the previous equality.
  3459. diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
  3460. diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
  3461. diffs.splice(pointer - 1, 1);
  3462. changes = true;
  3463. } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
  3464. // Shift the edit over the next equality.
  3465. diffs[pointer - 1][1] += diffs[pointer + 1][1];
  3466. diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
  3467. diffs.splice(pointer + 1, 1);
  3468. changes = true;
  3469. }
  3470. }
  3471. pointer++;
  3472. }
  3473. // If shifts were made, the diff needs reordering and another shift sweep.
  3474. if (changes) {
  3475. this.diffCleanupMerge(diffs);
  3476. }
  3477. };
  3478. return function (o, n) {
  3479. var diff, output, text;
  3480. diff = new DiffMatchPatch();
  3481. output = diff.DiffMain(o, n);
  3482. diff.diffCleanupEfficiency(output);
  3483. text = diff.diffPrettyHtml(output);
  3484. return text;
  3485. };
  3486. }();
  3487. (function () {
  3488. // Only interact with URLs via window.location
  3489. var location = typeof window !== "undefined" && window.location;
  3490. if (!location) {
  3491. return;
  3492. }
  3493. var urlParams = getUrlParams();
  3494. QUnit.urlParams = urlParams;
  3495. // Match module/test by inclusion in an array
  3496. QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
  3497. QUnit.config.testId = [].concat(urlParams.testId || []);
  3498. // Exact case-insensitive match of the module name
  3499. QUnit.config.module = urlParams.module;
  3500. // Regular expression or case-insenstive substring match against "moduleName: testName"
  3501. QUnit.config.filter = urlParams.filter;
  3502. // Test order randomization
  3503. if (urlParams.seed === true) {
  3504. // Generate a random seed if the option is specified without a value
  3505. QUnit.config.seed = Math.random().toString(36).slice(2);
  3506. } else if (urlParams.seed) {
  3507. QUnit.config.seed = urlParams.seed;
  3508. }
  3509. // Add URL-parameter-mapped config values with UI form rendering data
  3510. QUnit.config.urlConfig.push({
  3511. id: "hidepassed",
  3512. label: "Hide passed tests",
  3513. tooltip: "Only show tests and assertions that fail. Stored as query-strings."
  3514. }, {
  3515. id: "noglobals",
  3516. label: "Check for Globals",
  3517. tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
  3518. }, {
  3519. id: "notrycatch",
  3520. label: "No try-catch",
  3521. tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
  3522. });
  3523. QUnit.begin(function () {
  3524. var i,
  3525. option,
  3526. urlConfig = QUnit.config.urlConfig;
  3527. for (i = 0; i < urlConfig.length; i++) {
  3528. // Options can be either strings or objects with nonempty "id" properties
  3529. option = QUnit.config.urlConfig[i];
  3530. if (typeof option !== "string") {
  3531. option = option.id;
  3532. }
  3533. if (QUnit.config[option] === undefined) {
  3534. QUnit.config[option] = urlParams[option];
  3535. }
  3536. }
  3537. });
  3538. function getUrlParams() {
  3539. var i, param, name, value;
  3540. var urlParams = Object.create(null);
  3541. var params = location.search.slice(1).split("&");
  3542. var length = params.length;
  3543. for (i = 0; i < length; i++) {
  3544. if (params[i]) {
  3545. param = params[i].split("=");
  3546. name = decodeQueryParam(param[0]);
  3547. // Allow just a key to turn on a flag, e.g., test.html?noglobals
  3548. value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
  3549. if (name in urlParams) {
  3550. urlParams[name] = [].concat(urlParams[name], value);
  3551. } else {
  3552. urlParams[name] = value;
  3553. }
  3554. }
  3555. }
  3556. return urlParams;
  3557. }
  3558. function decodeQueryParam(param) {
  3559. return decodeURIComponent(param.replace(/\+/g, "%20"));
  3560. }
  3561. })();
  3562. }((function() { return this; }())));