|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221 |
- /*!
- * Benchmark.js
- * Copyright 2010-2011 Mathias Bynens <http://mths.be/>
- * Based on JSLitmus.js, copyright Robert Kieffer <http://broofa.com/>
- * Modified by John-David Dalton <http://allyoucanleet.com/>
- * Available under MIT license <http://mths.be/mit>
- */
- (function(window) {
-
- /** Detect DOM0 timeout API (performed at the bottom) */
- var HAS_TIMEOUT_API,
-
- /** Detect Adobe AIR environment */
- IN_AIR = isClassOf(window.runtime, 'ScriptBridgingProxyObject'),
-
- /** Detect Java environment */
- IN_JAVA = isClassOf(window.java, 'JavaPackage'),
-
- /** Used to integrity check compiled tests */
- EMBEDDED_UID = +new Date,
-
- /** Used to skip initialization of the Benchmark constructor */
- HEADLESS = function() { },
-
- /** Used to avoid hz of Infinity */
- CYCLE_DIVISORS = {
- '1': 4096,
- '2': 512,
- '3': 64,
- '4': 8,
- '5': 0
- },
-
- /**
- * T-Distribution two-tailed critical values for 95% confidence
- * http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm
- */
- T_DISTRIBUTION = {
- '1': 12.706,'2': 4.303, '3': 3.182, '4': 2.776, '5': 2.571, '6': 2.447,
- '7': 2.365, '8': 2.306, '9': 2.262, '10': 2.228, '11': 2.201, '12': 2.179,
- '13': 2.160, '14': 2.145, '15': 2.131, '16': 2.120, '17': 2.110, '18': 2.101,
- '19': 2.093, '20': 2.086, '21': 2.080, '22': 2.074, '23': 2.069, '24': 2.064,
- '25': 2.060, '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,
- 'Infinity': 1.960
- },
-
- /** Internal cached used by various methods */
- cache = {
- 'counter': 0
- },
-
- /** Used in Benchmark.hasKey() */
- hasOwnProperty = cache.hasOwnProperty,
-
- /** Used to convert array-like objects to arrays */
- slice = [].slice,
-
- /** Used generically when invoking over queued arrays */
- shift = aloClean([].shift),
-
- /** Math shortcuts */
- abs = Math.abs,
- max = Math.max,
- min = Math.min,
- pow = Math.pow,
- sqrt = Math.sqrt;
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Benchmark constructor.
- * @constructor
- * @param {String} name A name to identify the benchmark.
- * @param {Function} fn The test to benchmark.
- * @param {Object} [options={}] Options object.
- * @example
- *
- * // basic usage
- * var bench = new Benchmark(fn);
- *
- * // or using a name first
- * var bench = new Benchmark('foo', fn);
- *
- * // or with options
- * var bench = new Benchmark('foo', fn, {
- *
- * // displayed by Benchmark#toString if `name` is not available
- * 'id': 'xyz',
- *
- * // called when the benchmark starts running
- * 'onStart': onStart,
- *
- * // called after each run cycle
- * 'onCycle': onCycle,
- *
- * // called when aborted
- * 'onAbort': onAbort,
- *
- * // called when a test errors
- * 'onError': onError,
- *
- * // called when reset
- * 'onReset': onReset,
- *
- * // called when the benchmark completes running
- * 'onComplete': onComplete,
- *
- * // compiled/called before the test loop
- * 'setup': setup,
- *
- * // compiled/called after the test loop
- * 'teardown': teardown
- * });
- *
- * // or options only
- * var bench = new Benchmark({
- *
- * // benchmark name
- * 'name': 'foo',
- *
- * // benchmark test function
- * 'fn': fn
- * });
- *
- * // a test's `this` binding is set to the benchmark instance
- * var bench = new Benchmark('foo', function() {
- * 'My name is '.concat(this.name); // My name is foo
- * });
- */
- function Benchmark(name, fn, options) {
- // juggle arguments
- var me = this;
- if (isClassOf(name, 'Object')) {
- // 1 argument
- options = name;
- }
- else if (isClassOf(name, 'Function')) {
- // 2 arguments
- options = fn;
- fn = name;
- }
- else {
- // 3 arguments
- me.name = name;
- }
- // initialize if not headless
- if (name != HEADLESS) {
- setOptions(me, options);
- fn = me.fn || (me.fn = fn);
- fn.uid || (fn.uid = ++cache.counter);
-
- me.created = +new Date;
- me.stats = extend({ }, me.stats);
- me.times = extend({ }, me.times);
- }
- }
-
- /**
- * Suite constructor.
- * @constructor
- * @member Benchmark
- * @param {String} name A name to identify the suite.
- * @param {Object} [options={}] Options object.
- * @example
- *
- * // basic usage
- * var suite = new Benchmark.Suite;
- *
- * // or using a name first
- * var suite = new Benchmark.Suite('foo');
- *
- * // or with options
- * var suite = new Benchmark.Suite('foo', {
- *
- * // called when the suite starts running
- * 'onStart': onStart,
- *
- * // called between running benchmarks
- * 'onCycle': onCycle,
- *
- * // called when aborted
- * 'onAbort': onAbort,
- *
- * // called when a test errors
- * 'onError': onError,
- *
- * // called when reset
- * 'onReset': onReset,
- *
- * // called when the suite completes running
- * 'onComplete': onComplete
- * });
- */
- function Suite(name, options) {
- // juggle arguments
- var me = this;
- if (isClassOf(name, 'Object')) {
- options = name;
- } else {
- me.name = name;
- }
- setOptions(me, options);
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Wraps an array function to ensure array-like-objects (ALO) are empty when their length is `0`.
- * @private
- * @param {Function} fn The array function to be wrapped.
- * @returns {Function} The new function.
- */
- function aloClean(fn) {
- return function() {
- var me = this,
- result = fn.apply(me, arguments);
- // fixes IE<9 and IE8 compatibility mode bugs
- if (!me.length) {
- delete me[0];
- }
- return result;
- };
- }
-
- /**
- * Executes a function asynchronously or synchronously.
- * @private
- * @param {Object} me The benchmark instance passed to `fn`.
- * @param {Function} fn Function to be executed.
- * @param {Boolean} [async=false] Flag to run asynchronously.
- */
- function call(me, fn, async) {
- // only attempt asynchronous calls if supported
- if (async && HAS_TIMEOUT_API) {
- me.timerId = setTimeout(function() {
- delete me.timerId;
- fn();
- }, me.CYCLE_DELAY * 1e3);
- }
- else {
- fn();
- }
- }
-
- /**
- * Clocks the time taken to execute a test per cycle (secs).
- * @private
- * @param {Object} me The benchmark instance.
- * @returns {Number} The time taken.
- */
- function clock() {
- var applet,
- args,
- fallback,
- proto = Benchmark.prototype,
- timers = [],
- timerNS = Date,
- msRes = getRes('ms'),
- timer = { 'ns': timerNS, 'res': max(0.0015, msRes), 'unit': 'ms' },
- code = 'var r$,s$,m$=this,i$=m$.count,f$=m$.fn;#{setup}#{start};while(i$--){|}#{end};#{teardown}return{time:r$,uid:"$"}|m$.teardown&&m$.teardown();|f$()|m$.setup&&m$.setup();|n$';
-
- clock = function(me) {
- var result,
- fn = me.fn,
- compiled = fn.compiled,
- count = me.count;
-
- if (applet) {
- // repair nanosecond timer
- try {
- timerNS.nanoTime();
- } catch(e) {
- // use non-element to avoid issues with libs that augment them
- timerNS = new applet.Packages.nano;
- }
- }
- if (compiled == null || compiled) {
- try {
- if (!compiled) {
- // insert test body into the while-loop
- compiled = createFunction(args,
- interpolate(code[0], { 'setup': getSource(me.setup) }) +
- getSource(fn) +
- interpolate(code[1], { 'teardown': getSource(me.teardown) })
- );
- // determine if compiled code is exited early, usually by a rogue
- // return statement, by checking for a return object with the uid
- me.count = 1;
- compiled = fn.compiled = compiled.call(me, timerNS).uid == EMBEDDED_UID && compiled;
- me.count = count;
- }
- if (compiled) {
- result = compiled.call(me, timerNS).time;
- }
- } catch(e) {
- me.count = count;
- compiled = fn.compiled = false;
- }
- }
- // fallback to simple while-loop when compiled is false
- if (!compiled) {
- result = fallback.call(me, timerNS).time;
- }
- return result;
- };
-
- function getRes(unit) {
- var measured,
- start,
- count = 30,
- divisor = 1e3,
- sample = [];
-
- // get average smallest measurable time
- while (count--) {
- if (unit == 'us') {
- divisor = 1e6;
- if (timerNS.stop) {
- timerNS.start();
- while(!(measured = timerNS.microseconds()));
- } else {
- start = timerNS();
- while(!(measured = timerNS() - start));
- }
- }
- else if (unit == 'ns') {
- divisor = 1e9;
- start = timerNS.nanoTime();
- while(!(measured = timerNS.nanoTime() - start));
- }
- else {
- start = new timerNS;
- while(!(measured = new timerNS - start));
- }
- // check for broken timers (nanoTime may have issues)
- // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/
- if (measured > 0) {
- sample.push(measured);
- } else {
- sample.push(Infinity);
- break;
- }
- }
- // convert to seconds
- return getMean(sample) / divisor;
- }
-
- // detect nanosecond support from a Java applet
- each(window.document && document.applets || [], function(element) {
- if (timerNS = applet = 'nanoTime' in element && element) {
- return false;
- }
- });
-
- // or the exposed Java API
- // http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime()
- try {
- timerNS = java.lang.System;
- } catch(e) { }
-
- // check type in case Safari returns an object instead of a number
- try {
- if (typeof timerNS.nanoTime() == 'number') {
- timers.push({ 'ns': timerNS, 'res': getRes('ns'), 'unit': 'ns' });
- }
- } catch(e) { }
-
- // detect Chrome's microsecond timer:
- // enable benchmarking via the --enable-benchmarking command
- // line switch in at least Chrome 7 to use chrome.Interval
- try {
- if (timerNS = new (window.chrome || window.chromium).Interval) {
- timers.push({ 'ns': timerNS, 'res': getRes('us'), 'unit': 'us' });
- }
- } catch(e) { }
-
- // detect Node's microtime module:
- // npm install microtime
- try {
- if (timerNS = typeof require == 'function' && !window.require && require('microtime').now) {
- timers.push({ 'ns': timerNS, 'res': getRes('us'), 'unit': 'us' });
- }
- } catch(e) { }
-
- // pick timer with highest resolution
- timerNS = (timer = reduce(timers, function(timer, other) {
- return other.res < timer.res ? other : timer;
- }, timer)).ns;
-
- // restore ms res
- if (timer.unit == 'ms') {
- timer.res = msRes;
- }
- // remove unused applet
- if (timer.unit != 'ns' && applet) {
- applet.parentNode.removeChild(applet);
- applet = null;
- }
- // error if there are no working timers
- if (timer.res == Infinity) {
- throw new Error('Benchmark.js was unable to find a working timer.');
- }
- // use API of chosen timer
- if (timer.unit == 'ns') {
- code = interpolate(code, {
- 'start': 's$=n$.nanoTime()',
- 'end': 'r$=(n$.nanoTime()-s$)/1e9'
- });
- }
- else if (timer.unit == 'us') {
- code = interpolate(code, timerNS.stop ? {
- 'start': 's$=n$.start()',
- 'end': 'r$=n$.microseconds()/1e6'
- } : {
- 'start': 's$=n$()',
- 'end': 'r$=(n$()-s$)/1e6'
- });
- }
- else {
- code = interpolate(code, {
- 'start': 's$=new n$',
- 'end': 'r$=(new n$-s$)/1e3'
- });
- }
-
- // inject uid into variable names to avoid collisions with
- // embedded tests and create non-embedding fallback
- code = code.replace(/\$/g, EMBEDDED_UID).split('|');
- args = code.pop();
- fallback = createFunction(args,
- interpolate(code[0], { 'setup': code.pop() }) +
- code.pop() +
- interpolate(code[1], { 'teardown': code.pop() })
- );
-
- // resolve time to achieve a percent uncertainty of 1%
- proto.MIN_TIME || (proto.MIN_TIME = max(timer.res / 2 / 0.01, 0.05));
- return clock.apply(null, arguments);
- }
-
- /**
- * Creates a function from the given arguments string and body.
- * @private
- * @param {String} args The comma separated function arguments.
- * @param {String} body The function body.
- * @returns {Function} The new function.
- */
- function createFunction() {
- var scripts,
- prop = 'c' + EMBEDDED_UID;
-
- createFunction = function(args, body) {
- var parent = scripts[0].parentNode,
- script = document.createElement('script');
-
- script.appendChild(document.createTextNode('Benchmark.' + prop + '=function(' + args + '){' + body + '}'));
- parent.removeChild(parent.insertBefore(script, scripts[0]));
- return [Benchmark[prop], delete Benchmark[prop]][0];
- };
-
- // fix JaegerMonkey bug
- // http://bugzil.la/639720
- try {
- scripts = document.getElementsByTagName('script');
- createFunction = createFunction('', 'return ' + EMBEDDED_UID)() == EMBEDDED_UID && createFunction;
- } catch (e) {
- createFunction = false;
- }
- createFunction || (createFunction = Function);
- return createFunction.apply(null, arguments);
- }
-
- /**
- * Wraps a function and passes `this` to the original function as the first argument.
- * @private
- * @param {Function} fn The function to be wrapped.
- * @returns {Function} The new function.
- */
- function methodize(fn) {
- return function() {
- return fn.apply(this, [this].concat(slice.call(arguments)));
- };
- }
-
- /**
- * Gets the critical value for the specified degrees of freedom.
- * @private
- * @param {Number} df The degrees of freedom.
- * @returns {Number} The critical value.
- */
- function getCriticalValue(df) {
- return T_DISTRIBUTION[Math.round(df) || 1] || T_DISTRIBUTION.Infinity;
- }
-
- /**
- * Computes the arithmetic mean of a sample.
- * @private
- * @param {Array} sample The sample.
- * @returns {Number} The mean.
- */
- function getMean(sample) {
- return reduce(sample, function(sum, x) {
- return sum + x;
- }, 0) / sample.length || 0;
- }
-
- /**
- * Gets the source code of a function.
- * @private
- * @param {Function} fn The function.
- * @returns {String} The function's source code.
- */
- function getSource(fn) {
- return trim((/^[^{]+{([\s\S]*)}\s*$/.exec(fn) || 0)[1] || '')
- .replace(/([^\n])$/, '$1\n');
- }
-
- /**
- * Sets the options of a benchmark.
- * @private
- * @param {Object} me The benchmark instance.
- * @param {Object} [options={}] Options object.
- */
- function setOptions(me, options) {
- options = extend(extend({ }, me.constructor.options), options);
- me.options = each(options, function(value, key) {
- // add event listeners
- if (/^on[A-Z]/.test(key)) {
- each(key.split(' '), function(key) {
- me.on(key.slice(2).toLowerCase(), value);
- });
- } else {
- me[key] = value;
- }
- });
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * A bare-bones `Array#forEach`/`for-in` own property solution.
- * Callbacks may terminate the loop by explicitly returning `false`.
- * @static
- * @member Benchmark
- * @param {Array|Object} object The object to iterate over.
- * @param {Function} callback The function called per iteration.
- * @returns {Array|Object} Returns the object iterated over.
- */
- function each(object, callback) {
- var i = -1,
- length = object.length;
-
- if (hasKey(object, 'length') && length > -1 && length < 4294967296) {
- while (++i < length) {
- if (i in object && callback(object[i], i, object) === false) {
- break;
- }
- }
- } else {
- for (i in object) {
- if (hasKey(object, i) && callback(object[i], i, object) === false) {
- break;
- }
- }
- }
- return object;
- }
-
- /**
- * Copies own/inherited properties of a source object to the destination object.
- * @static
- * @member Benchmark
- * @param {Object} destination The destination object.
- * @param {Object} [source={}] The source object.
- * @returns {Object} The destination object.
- */
- function extend(destination, source) {
- source || (source = { });
- for (var key in source) {
- destination[key] = source[key];
- }
- return destination;
- }
-
- /**
- * A generic bare-bones `Array#filter` solution.
- * @static
- * @member Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function|String} callback The function/alias called per iteration.
- * @returns {Array} A new array of values that passed callback filter.
- * @example
- *
- * // get odd numbers
- * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
- * return n % 2;
- * }); // -> [1, 3, 5];
- *
- * // get fastest benchmarks
- * Benchmark.filter(benches, 'fastest');
- *
- * // get slowest benchmarks
- * Benchmark.filter(benches, 'slowest');
- *
- * // get benchmarks that completed without erroring
- * Benchmark.filter(benches, 'successful');
- */
- function filter(array, callback) {
- var result;
- if (callback == 'successful') {
- // callback to exclude errored or unrun benchmarks
- callback = function(bench) { return bench.cycles; };
- }
- else if (/^(?:fast|slow)est$/.test(callback)) {
- // get successful, sort by period + margin of error, and filter fastest/slowest
- result = filter(array, 'successful').sort(function(a, b) {
- a = a.stats;
- b = b.stats;
- return (a.mean + a.ME > b.mean + b.ME ? 1 : -1) * (callback == 'fastest' ? 1 : -1);
- });
- result = filter(result, function(bench) {
- return !result[0].compare(bench);
- });
- }
- return result || reduce(array, function(result, value, index) {
- return callback(value, index, array) ? result.push(value) && result : result;
- }, []);
- }
-
- /**
- * Converts a number to a more readable comma-separated string representation.
- * @static
- * @member Benchmark
- * @param {Number} number The number to convert.
- * @returns {String} The more readable string representation.
- */
- function formatNumber(number) {
- number = String(number).split('.');
- return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') + (number[1] ? '.' + number[1] : '');
- }
-
- /**
- * Checks if an object has the specified key as a direct property.
- * @static
- * @member Benchmark
- * @param {Object} object The object to check.
- * @param {String} key The key to check for.
- * @returns {Boolean} Returns `true` if key is a direct property, else `false`.
- */
- function hasKey(object, key) {
- var result,
- parent = (object.constructor || Object).prototype;
-
- // for modern browsers
- object = Object(object);
- if (isClassOf(hasOwnProperty, 'Function')) {
- result = hasOwnProperty.call(object, key);
- }
- // for Safari 2
- else if (cache.__proto__ == Object.prototype) {
- object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
- }
- // for others (not as accurate)
- else {
- result = key in object && !(key in parent && object[key] === parent[key]);
- }
- return result;
- }
-
- /**
- * A generic bare-bones `Array#indexOf` solution.
- * @static
- * @member Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Mixed} value The value to search for.
- * @returns {Number} The index of the matched value or `-1`.
- */
- function indexOf(array, value) {
- var result = -1;
- each(array, function(v, i) {
- if (v === value) {
- result = i;
- return false;
- }
- });
- return result;
- }
-
- /**
- * Invokes a method on all items in an array.
- * @static
- * @member Benchmark
- * @param {Array} benches Array of benchmarks to iterate over.
- * @param {String|Object} name The name of the method to invoke OR options object.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} A new array of values returned from each method invoked.
- * @example
- *
- * // invoke `reset` on all benchmarks
- * Benchmark.invoke(benches, 'reset');
- *
- * // invoke `emit` with arguments
- * Benchmark.invoke(benches, 'emit', 'complete', listener);
- *
- * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
- * Benchmark.invoke(benches, {
- *
- * // invoke the `run` method
- * 'name': 'run',
- *
- * // pass a single argument
- * 'args': true,
- *
- * // treat as queue, removing benchmarks from front of `benches` until empty
- * 'queued': true,
- *
- * // called before any benchmarks have been invoked.
- * 'onStart': onStart,
- *
- * // called between invoking benchmarks
- * 'onCycle': onCycle,
- *
- * // called after all benchmarks have been invoked.
- * 'onComplete': onComplete
- * });
- */
- function invoke(benches, name) {
- var args,
- async,
- bench,
- queued,
- i = 0,
- options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
- result = slice.call(benches, 0);
-
- function execute() {
- var listeners;
- if (async) {
- // use `next` as a listener
- bench.on('complete', next);
- listeners = bench.events['complete'];
- listeners.splice(0, 0, listeners.pop());
- }
- // execute method
- result[i] = bench[name].apply(bench, args);
- // if synchronous return true until finished
- return async || next();
- }
-
- function next() {
- var last = bench;
- bench = false;
-
- if (async) {
- last.removeListener('complete', next);
- last.emit('complete');
- }
- // choose next benchmark if not exiting early
- if (options.onCycle.call(benches, last) !== false) {
- if (queued) {
- // use generic shift
- shift.call(benches);
- bench = benches[0];
- } else {
- bench = benches[++i];
- }
- }
- if (bench) {
- if (async) {
- call(bench, execute, async);
- } else {
- return true;
- }
- } else {
- options.onComplete.call(benches, last);
- }
- // when async the `return false` will cancel the rest of the "complete"
- // listeners because they were called above and when synchronous it will
- // end the while-loop
- return false;
- }
-
- // juggle arguments
- if (isClassOf(name, 'String')) {
- args = slice.call(arguments, 2);
- } else {
- options = extend(options, name);
- name = options.name;
- args = isArray(args = 'args' in options ? options.args : []) ? args : [args];
- queued = options.queued;
- }
- // async for use with Benchmark#run only
- if (name == 'run') {
- async = (args[0] == null ? Benchmark.prototype.DEFAULT_ASYNC :
- args[0]) && HAS_TIMEOUT_API;
- }
- // start iterating over the array
- if (bench = benches[0]) {
- options.onStart.call(benches, bench);
- if (async) {
- call(bench, execute, async);
- } else {
- result.length = 0;
- while (execute());
- }
- }
- return result;
- }
-
- /**
- * Modify a string by replacing named tokens with matching object property values.
- * @static
- * @member Benchmark
- * @param {String} string The string to modify.
- * @param {Object} object The template object.
- * @returns {String} The modified string.
- * @example
- *
- * Benchmark.interpolate('#{greet} #{who}!', {
- * 'greet': 'Hello',
- * 'who': 'world'
- * }); // -> 'Hello world!'
- */
- function interpolate(string, object) {
- string = string == null ? '' : string;
- each(object || { }, function(value, key) {
- string = string.replace(RegExp('#\\{' + key + '\\}', 'g'), String(value));
- });
- return string;
- }
-
- /**
- * Determines if the given value is an array.
- * @static
- * @member Benchmark
- * @param {Mixed} value The value to check.
- * @returns {Boolean} Returns `true` if value is an array, else `false`.
- */
- function isArray(value) {
- return isClassOf(value, 'Array');
- }
-
- /**
- * Checks if an object is of the specified class.
- * @static
- * @member Benchmark
- * @param {Object} object The object.
- * @param {String} name The name of the class.
- * @returns {Boolean} Returns `true` if of the class, else `false`.
- */
- function isClassOf(object, name) {
- return object != null && {}.toString.call(object).slice(8, -1) == name;
- }
-
- /**
- * Host objects can return type values that are different from their actual
- * data type. The objects we are concerned with usually return non-primitive
- * types of object, function, or unknown.
- * @static
- * @member Benchmark
- * @param {Mixed} object The owner of the property.
- * @param {String} property The property to check.
- * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
- */
- function isHostType(object, property) {
- return !/^(?:boolean|number|string|undefined)$/
- .test(typeof object[property]) && !!object[property];
- }
-
- /**
- * Creates a string of joined array values or object key-value pairs.
- * @static
- * @member Benchmark
- * @param {Array|Object} object The object to operate on.
- * @param {String} [separator1=','] The separator used between key-value pairs.
- * @param {String} [separator2=': '] The separator used between keys and values.
- * @returns {String} The joined result.
- */
- function join(object, separator1, separator2) {
- var pairs = [];
- if (isArray(object)) {
- pairs = object;
- }
- else {
- separator2 || (separator2 = ': ');
- each(object, function(value, key) {
- pairs.push(key + separator2 + value);
- });
- }
- return pairs.join(separator1 || ',');
- }
-
- /**
- * A generic bare-bones `Array#map` solution.
- * @static
- * @member Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function called per iteration.
- * @returns {Array} A new array of values returned by the callback.
- */
- function map(array, callback) {
- return reduce(array, function(result, value, index) {
- result.push(callback(value, index, array));
- return result;
- }, []);
- }
-
- /**
- * A no-operation function.
- * @static
- * @member Benchmark
- */
- function noop() {
- // no operation performed
- }
-
- /**
- * Retrieves the value of a specified property from all items in an array.
- * @static
- * @member Benchmark
- * @param {Array} array The array to iterate over.
- * @param {String} property The property to pluck.
- * @returns {Array} A new array of property values.
- */
- function pluck(array, property) {
- return map(array, function(object) {
- return object[property];
- });
- }
-
- /**
- * A generic bare-bones `Array#reduce` solution.
- * @static
- * @member Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} accumulator Initial value of the accumulator.
- * @returns {Mixed} The accumulator.
- */
- function reduce(array, callback, accumulator) {
- each(array, function(value, index) {
- accumulator = callback(accumulator, value, index, array);
- });
- return accumulator;
- }
-
- /**
- * A generic bare-bones `String#trim` solution.
- * @static
- * @member Benchmark
- * @param {String} string The string to trim.
- * @returns {String} The trimmed string.
- */
- function trim(string) {
- return String(string).replace(/^\s+/, '').replace(/\s+$/, '');
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Aborts all benchmarks in the suite.
- * @name abort
- * @member Benchmark.Suite
- * @returns {Object} The suite instance.
- */
- function abortSuite() {
- var me = this;
- me.aborted = true;
- me.running = false;
- invoke(me, 'abort');
- me.emit('abort');
- return me;
- }
-
- /**
- * Adds a test to the benchmark suite.
- * @member Benchmark.Suite
- * @param {String} name A name to identify the benchmark.
- * @param {Function} fn The test to benchmark.
- * @param {Object} [options={}] Options object.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * suite.add(fn);
- *
- * // or using a name first
- * suite.add('foo', fn);
- *
- * // or with options
- * suite.add('foo', fn, {
- * 'onCycle': onCycle,
- * 'onComplete': onComplete
- * });
- */
- function add(name, fn, options) {
- var me = this,
- bench = new Benchmark(HEADLESS);
-
- Benchmark.call(bench, name, fn, options);
- me.push(bench);
- me.emit('add', bench);
- return me;
- }
-
- /**
- * Creates a new suite with cloned benchmarks.
- * @name clone
- * @member Benchmark.Suite
- * @param {Object} options Options object to overwrite cloned options.
- * @returns {Object} The new suite instance.
- */
- function cloneSuite(options) {
- var me = this,
- result = new me.constructor(extend(extend({ }, me.options), options));
- // copy own properties
- each(me, function(value, key) {
- if (!hasKey(result, key)) {
- result[key] = /^\d+$/.test(key) ? value.clone() : value;
- }
- });
- return result.reset();
- }
-
- /**
- * A bare-bones `Array#filter` solution.
- * @name filter
- * @member Benchmark.Suite
- * @param {Function|String} callback The function/alias called per iteration.
- * @returns {Object} A new suite of benchmarks that passed callback filter.
- */
- function filterSuite(callback) {
- var me = this,
- result = new me.constructor;
-
- result.push.apply(result, filter(me, callback));
- return result;
- }
-
- /**
- * Resets all benchmarks in the suite.
- * @name reset
- * @member Benchmark.Suite
- * @returns {Object} The suite instance.
- */
- function resetSuite() {
- var me = this;
- me.aborted = me.running = false;
- invoke(me, 'reset');
- me.emit('reset');
- return me;
- }
-
- /**
- * Runs the suite.
- * @name run
- * @member Benchmark.Suite
- * @param {Boolean} [async=false] Flag to run asynchronously.
- * @param {Boolean} [queued=false] Flag to treat benchmarks as a queue.
- * @returns {Object} The suite instance.
- */
- function runSuite(async, queued) {
- var me = this;
- me.reset();
- me.running = true;
- invoke(me, {
- 'name': 'run',
- 'args': async,
- 'queued': queued,
- 'onStart': function(bench) {
- me.emit('start', bench);
- },
- 'onCycle': function(bench) {
- if (bench.error) {
- me.emit('error', bench);
- }
- return !me.aborted && me.emit('cycle', bench);
- },
- 'onComplete': function(bench) {
- me.running = false;
- me.emit('complete', bench);
- }
- });
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Registers a single listener of a specified event type.
- * @member Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- * @param {Function} listener The function called when the event occurs.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * bench.addListener('cycle', listener);
- *
- * // register a listener for multiple event types
- * bench.addListener('start cycle', listener);
- */
- function addListener(type, listener) {
- var me = this,
- events = me.events || (me.events = { });
-
- each(type.split(' '), function(type) {
- (events[type] || (events[type] = [])).push(listener);
- });
- return me;
- }
-
- /**
- * Executes all registered listeners of a specified event type.
- * @member Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- */
- function emit(type) {
- var me = this,
- args = slice.call(arguments, 1),
- events = me.events,
- listeners = events && events[type] || [],
- successful = true;
-
- each(listeners, function(listener) {
- if (listener.apply(me, args) === false) {
- successful = false;
- return successful;
- }
- });
- return successful;
- }
-
- /**
- * Unregisters a single listener of a specified event type.
- * @member Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- * @param {Function} listener The function to unregister.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * bench.removeListener('cycle', listener);
- *
- * // unregister a listener for multiple event types
- * bench.removeListener('start cycle', listener);
- */
- function removeListener(type, listener) {
- var me = this,
- events = me.events;
-
- each(type.split(' '), function(type) {
- var listeners = events && events[type] || [],
- index = indexOf(listeners, listener);
- if (index > -1) {
- listeners.splice(index, 1);
- }
- });
- return me;
- }
-
- /**
- * Unregisters all listeners of a specified event type.
- * @member Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * bench.removeAllListeners('cycle');
- *
- * // unregister all listeners for multiple event types
- * bench.removeListener('start cycle');
- */
- function removeAllListeners(type) {
- var me = this,
- events = me.events;
-
- each(type.split(' '), function(type) {
- (events && events[type] || []).length = 0;
- });
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Aborts the benchmark without recording times.
- * @member Benchmark
- * @returns {Object} The benchmark instance.
- */
- function abort() {
- var me = this;
- if (me.running) {
- if (me.timerId && HAS_TIMEOUT_API) {
- clearTimeout(me.timerId);
- delete me.timerId;
- }
- // set running as NaN so reset() will detect it as falsey and *not* call abort(),
- // but *will* detect it as a change and fire the onReset() callback
- me.running = NaN;
- me.reset();
- me.aborted = true;
- me.emit('abort');
- }
- return me;
- }
-
- /**
- * Creates a new benchmark using the same test and options.
- * @member Benchmark
- * @param {Object} options Options object to overwrite cloned options.
- * @returns {Object} The new benchmark instance.
- * @example
- *
- * var bizarro = bench.clone({
- * 'name': 'doppelganger'
- * });
- */
- function clone(options) {
- var me = this,
- result = new me.constructor(me.fn, extend(extend({ }, me.options), options));
- // copy own properties
- each(me, function(value, key) {
- if (!hasKey(result, key)) {
- result[key] = value;
- }
- });
- return result.reset();
- }
-
- /**
- * Determines if the benchmark's period is smaller than another.
- * @member Benchmark
- * @param {Object} other The benchmark to compare.
- * @returns {Number} Returns `1` if smaller, `-1` if larger, and `0` if indeterminate.
- */
- function compare(other) {
- // unpaired two-sample t-test assuming equal variance
- // http://en.wikipedia.org/wiki/Student's_t-test
- // http://www.chem.utoronto.ca/coursenotes/analsci/StatsTutorial/12tailed.html
- var a = this.stats,
- b = other.stats,
- df = a.size + b.size - 2,
- pooled = (((a.size - 1) * a.variance) + ((b.size - 1) * b.variance)) / df,
- tstat = (a.mean - b.mean) / sqrt(pooled * (1 / a.size + 1 / b.size)),
- near = abs(1 - a.mean / b.mean) < 0.055 && a.RME < 3 && b.RME < 3;
-
- // check if the means aren't close and the t-statistic is significant
- return !near && abs(tstat) > getCriticalValue(df) ? (tstat > 0 ? -1 : 1) : 0;
- }
-
- /**
- * Reset properties and abort if running.
- * @member Benchmark
- * @returns {Object} The benchmark instance.
- */
- function reset() {
- var changed,
- pair,
- me = this,
- source = extend(extend({ }, me.constructor.prototype), me.options),
- pairs = [[source, me]];
-
- function check(value, key) {
- var other = pair[1][key];
- if (value && isClassOf(value, 'Object')) {
- pairs.push([value, other]);
- }
- else if (!isClassOf(value, 'Function') &&
- key != 'created' && value != other) {
- pair[1][key] = value;
- changed = true;
- }
- }
-
- if (me.running) {
- // no worries, reset() is called within abort()
- me.abort();
- me.aborted = source.aborted;
- }
- else {
- // check if properties have changed and reset them
- while (pairs.length) {
- each((pair = pairs.pop(), pair[0]), check);
- }
- if (changed) {
- me.emit('reset');
- }
- }
- return me;
- }
-
- /**
- * Displays relevant benchmark information when coerced to a string.
- * @member Benchmark
- * @returns {String} A string representation of the benchmark instance.
- */
- function toString() {
- var me = this,
- error = me.error,
- hz = me.hz,
- stats = me.stats,
- size = stats.size,
- pm = IN_JAVA ? '+/-' : '\xb1',
- result = me.name || me.id || ('<Test #' + me.fn.uid + '>');
-
- if (error) {
- result += ': ' + join(error);
- } else {
- result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
- stats.RME.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
- }
- return result;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Computes stats on benchmark results.
- * @private
- * @param {Object} me The benchmark instance.
- * @param {Boolean} [async=false] Flag to run asynchronously.
- */
- function compute(me, async) {
- var queue = [],
- sample = [],
- runCount = me.INIT_RUN_COUNT;
-
- function enqueue(count) {
- while (count--) {
- queue.push(me.clone({
- 'computing': me,
- 'events': { 'start': [update], 'cycle': [update] }
- }));
- }
- }
-
- function update() {
- // port changes from clone to host
- var clone = this;
- if (me.running) {
- if (clone.cycles) {
- // the me.count and me.cycles props of the host are updated in cycle() below
- me.hz = clone.hz;
- me.times.period = clone.times.period;
- me.INIT_RUN_COUNT = clone.INIT_RUN_COUNT;
- me.emit('cycle');
- }
- else if (clone.error) {
- me.abort();
- me.error = clone.error;
- me.emit('error');
- }
- else {
- // on start
- clone.count = me.INIT_RUN_COUNT;
- }
- } else if (me.aborted) {
- clone.abort();
- }
- }
-
- function evaluate(clone) {
- var mean,
- moe,
- rme,
- sd,
- sem,
- variance,
- now = +new Date,
- times = me.times,
- aborted = me.aborted,
- elapsed = (now - times.start) / 1e3,
- maxedOut = elapsed > me.MAX_TIME_ELAPSED,
- size = sample.push(clone.times.period),
- varOf = function(sum, x) { return sum + pow(x - mean, 2); };
-
- // exit early for aborted or unclockable tests
- if (aborted || clone.hz == Infinity) {
- maxedOut = !(size = sample.length = queue.length = 0);
- }
- // simulate onComplete and enqueue additional runs if needed
- if (queue.length < 2) {
- // sample mean (estimate of the population mean)
- mean = getMean(sample);
- // sample variance (estimate of the population variance)
- variance = reduce(sample, varOf, 0) / (size - 1);
- // sample standard deviation (estimate of the population standard deviation)
- sd = sqrt(variance);
- // standard error of the mean (aka the standard deviation of the sampling distribution of the sample mean)
- sem = sd / sqrt(size);
- // margin of error
- moe = sem * getCriticalValue(size - 1);
- // relative margin of error
- rme = (moe / mean) * 100 || 0;
-
- // if time permits, increase sample size to reduce the margin of error
- if (!maxedOut) {
- enqueue(1);
- }
- else {
- // set host values
- if (!aborted) {
- me.running = false;
- times.stop = now;
- times.elapsed = elapsed;
- extend(me.stats, {
- 'ME': moe,
- 'RME': rme,
- 'SEM': sem,
- 'deviation': sd,
- 'mean': mean,
- 'size': size,
- 'variance': variance
- });
-
- if (me.hz != Infinity) {
- times.period = mean;
- times.cycle = mean * me.count;
- me.hz = 1 / mean;
- }
- }
- me.INIT_RUN_COUNT = runCount;
- me.emit('complete');
- }
- }
- return !aborted;
- }
-
- // init sample/queue and begin
- enqueue(me.MIN_SAMPLE_SIZE);
- invoke(queue, { 'name': 'run', 'args': async, 'queued': true, 'onCycle': evaluate });
- }
-
- /**
- * Runs the benchmark.
- * @member Benchmark
- * @param {Boolean} [async=false] Flag to run asynchronously.
- * @returns {Object} The benchmark instance.
- */
- function run(async) {
- var me = this;
-
- function cycle() {
- var clocked,
- divisor,
- minTime,
- period,
- count = me.count,
- host = me.computing,
- times = me.times;
-
- // continue, if not aborted between cycles
- if (me.running) {
- me.cycles++;
- host.cycles++;
- host.count = count;
-
- try {
- clocked = clock(host);
- minTime = me.MIN_TIME;
- } catch(e) {
- me.abort();
- me.error = e;
- me.emit('error');
- }
- }
- // continue, if not errored
- if (me.running) {
- // time taken to complete last test cycle
- times.cycle = clocked;
- // seconds per operation
- period = times.period = clocked / count;
- // ops per second
- me.hz = 1 / period;
- // do we need to do another cycle?
- me.running = clocked < minTime;
- // avoid working our way up to this next time
- me.INIT_RUN_COUNT = count;
-
- if (me.running) {
- // tests may clock at 0 when INIT_RUN_COUNT is a small number,
- // to avoid that we set its count to something a bit higher
- if (!clocked && (divisor = CYCLE_DIVISORS[me.cycles]) != null) {
- count = Math.floor(4e6 / divisor);
- }
- // calculate how many more iterations it will take to achive the MIN_TIME
- if (count <= me.count) {
- count += Math.ceil((minTime - clocked) / period);
- }
- me.running = count != Infinity;
- }
- }
- // should we exit early?
- if (me.emit('cycle') === false) {
- me.abort();
- }
- // figure out what to do next
- if (me.running) {
- me.count = count;
- call(me, cycle, async);
- } else {
- // fix TraceMonkey bug
- // http://bugzil.la/509069
- if (window.Benchmark == Benchmark) {
- window.Benchmark = 1;
- window.Benchmark = Benchmark;
- }
- me.emit('complete');
- }
- }
-
- // set running to false so reset() won't call abort()
- me.running = false;
- me.reset();
- me.running = true;
-
- me.count = me.INIT_RUN_COUNT;
- me.times.start = +new Date;
- me.emit('start');
-
- async = (async == null ? me.DEFAULT_ASYNC : async) && HAS_TIMEOUT_API;
- if (me.computing) {
- cycle();
- } else {
- compute(me, async);
- }
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Platform object containing browser name, version, and operating system.
- * @static
- * @member Benchmark
- * @type Object
- */
- Benchmark.platform = (function() {
- var me = this,
- alpha = IN_JAVA ? 'a' : '\u03b1',
- beta = IN_JAVA ? 'b' : '\u03b2',
- description = [],
- doc = window.document || {},
- nav = window.navigator || {},
- ua = nav.userAgent || 'unknown platform',
- layout = /Gecko|Trident|WebKit/.exec(ua),
- data = { '6.1': 'Server 2008 R2 / 7', '6.0': 'Server 2008 / Vista', '5.2': 'Server 2003 / XP x64', '5.1': 'XP', '5.0': '2000', '4.0': 'NT', '4.9': 'ME' },
- name = 'Avant Browser,Camino,Epiphany,Fennec,Flock,Galeon,GreenBrowser,iCab,Iron,K-Meleon,Konqueror,Lunascape,Maxthon,Minefield,Nook Browser,RockMelt,SeaMonkey,Sleipnir,SlimBrowser,Sunrise,Swiftfox,Opera Mini,Opera,Chrome,Firefox,IE,Safari',
- os = 'Android,Cygwin,SymbianOS,webOS[ /]\\d,Linux,Mac OS(?: X)?,Macintosh,Windows 98;,Windows ',
- product = 'BlackBerry\\s?\\d+,iP[ao]d,iPhone,Kindle,Nokia,Nook,PlayBook,Samsung,Xoom',
- version = isClassOf(window.opera, 'Opera') && opera.version();
-
- function format(string) {
- // trim and conditionally capitalize
- return /^(?:webOS|i(?:OS|P))/.test(string = trim(string)) ? string :
- string.charAt(0).toUpperCase() + string.slice(1);
- }
-
- name = reduce(name.split(','), function(name, guess) {
- return name || RegExp(guess + '\\b', 'i').exec(ua) && guess;
- });
-
- product = reduce(product.split(','), function(product, guess) {
- if (!product && (product = RegExp(guess + '[^ ();-]*', 'i').exec(ua))) {
- // correct character case and split by forward slash
- if ((product = String(product).replace(RegExp(guess = /\w+/.exec(guess), 'i'), guess).split('/'))[1]) {
- if (/[\d.]+/.test(product[0])) {
- version = version || product[1];
- } else {
- product[0] += ' ' + product[1];
- }
- }
- product = format(product[0].replace(/([a-z])(\d)/i, '$1 $2').split('-')[0]);
- }
- return product;
- });
-
- os = reduce(os.split(','), function(os, guess) {
- if (!os && (os = RegExp(guess + '[^();/-]*').exec(ua))) {
- // platform tokens defined at
- // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
- if (/^win/i.test(os) && (data = data[0/*opera fix*/,/[456]\.\d/.exec(os)])) {
- os = 'Windows ' + data;
- }
- // normalize iOS
- else if (/^iP/.test(product)) {
- name || (name = 'Safari');
- os = 'iOS' + ((data = /\bOS ([\d_]+)/.exec(ua)) ? ' ' + data[1] : '');
- }
- // cleanup
- os = String(os).replace(RegExp(guess = /\w+/.exec(guess), 'i'), guess)
- .replace(/\/(\d)/, ' $1').replace(/_/g, '.').replace(/x86\.64/g, 'x86_64')
- .replace('Macintosh', 'Mac OS').replace(/(OS X) Mach$/, '$1').split(' on ')[0];
- }
- return os;
- });
-
- // detect simulator
- if (/Simulator/i.exec(ua)) {
- product = (product ? product + ' ' : '') + 'Simulator';
- }
- // detect non Firefox Gecko/Safari WebKit based browsers
- if (ua && (data = /^(?:Firefox|Safari|null)/.exec(name))) {
- if (name && !product && /[/,]/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
- name = null;
- }
- if ((data = product || os) && !/^(?:iP|Lin|Mac|Win)/.test(data)) {
- name = /[a-z]+/i.exec(/^And/.test(os) && os || data) + ' Browser';
- }
- }
- // detect non Opera versions
- if (!version) {
- version = reduce(['version', name, 'AdobeAIR', 'Firefox', 'NetFront'], function(version, guess) {
- return version || (RegExp(guess + '(?:-[\\d.]+/|[ /-])([^ ();/-]*)', 'i').exec(ua) || 0)[1] || null;
- });
- }
- // detect server-side js
- if (me && isHostType(me, 'global')) {
- if (typeof exports == 'object' && exports) {
- if (me == window && typeof system == 'object' && (data = system)) {
- name = data.global == global ? 'Narwhal' : 'RingoJS';
- os = data.os || null;
- }
- else if (typeof process == 'object' && (data = process)) {
- name = 'Node.js';
- version = /[\d.]+/.exec(data.version)[0];
- os = data.platform;
- }
- } else if (isClassOf(me.environment, 'Environment')) {
- name = 'Rhino';
- }
- if (IN_JAVA && !os) {
- os = String(java.lang.System.getProperty('os.name'));
- }
- }
- // detect Adobe AIR
- else if (IN_AIR) {
- name = 'Adobe AIR';
- os = runtime.flash.system.Capabilities.os;
- }
- // detect PhantomJS
- else if (isClassOf(data = window.phantom, 'RuntimeObject')) {
- name = 'PhantomJS';
- version = (data = data.version) && (data.major + '.' + data.minor + '.' + data.patch);
- }
- // detect IE compatibility mode
- else if (typeof doc.documentMode == 'number' && (data = /Trident\/(\d+)/.exec(ua))) {
- version = [version, doc.documentMode];
- version[1] = (data = +data[1] + 4) != version[1] ? (layout = null, description.push('running in IE ' + version[1] + ' mode'), data) : version[1];
- version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
- }
- // detect release phases
- if (version && (data = /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) || /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + nav.appMinorVersion))) {
- version = version.replace(RegExp(data + '\\+?$'), '') + (/^b/i.test(data) ? beta : alpha) + (/\d+\+?/.exec(data) || '');
- }
- // detect Maxthon's unreliable version info
- if (/^Max/.test(name)) {
- version = version && version.replace(/\.[.\d]*/, '.x');
- }
- // detect Firefox nightly
- else if (/^Min/.test(name)) {
- name = 'Firefox';
- version = RegExp(alpha + '|' + beta + '|null').test(version) ? version : version + alpha;
- }
- // detect mobile
- else if (name && (!product || name == 'IE') && !/Bro/.test(name) && /Mobi/.test(ua)) {
- name += ' Mobile';
- }
- // detect unspecified Chrome/Safari versions
- if (data = (/AppleWebKit\/(\d+(?:\.\d+)?)/.exec(ua) || 0)[1]) {
- if (/^And/.exec(os)) {
- data = data < 530 ? 1 : data < 532 ? 2 : data < 532.5 ? 3 : data < 533 ? 4 : data < 534.3 ? 5 : data < 534.7 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : '10';
- layout = 'like Chrome';
- } else {
- data = data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : '4';
- layout = 'like Safari';
- }
- layout += ' ' + (data += typeof data == 'number' ? '.x' : '+');
- version = name == 'Safari' && (!version || parseInt(version) > 45) ? data : version;
- }
- // detect platform preview
- if (RegExp(alpha + '|' + beta).test(version) && typeof external == 'object' && !external) {
- layout = layout && !/like /.test(layout) ? 'rendered by ' + layout : layout;
- description.unshift('platform preview');
- }
- // add layout engine
- if (layout && /Ado|Bro|Lun|Max|Pha|Sle/.test(name)) {
- description.push(layout);
- }
- // combine contextual information
- if (description.length) {
- description = ['(' + description.join('; ') + ')'];
- }
- // append product
- if (product && String(name).indexOf(product) < 0) {
- description.push('on ' + product);
- }
- // add browser/os architecture
- if (/\b(?:WOW|x|IA)64\b/.test(ua)) {
- os = os && os + (/64/.test(os) ? '' : ' x64');
- if (name && (/WOW64/.test(ua) || /\w(?:86|32)$/.test(nav.cpuClass || nav.platform))) {
- description.unshift('x86');
- }
- }
- return {
- 'version': name && version && description.unshift(version) && version,
- 'name': name && description.unshift(name) && name,
- 'os': name && (os = os && format(os)) && description.push(product ? '(' + os + ')' : 'on ' + os) && os,
- 'product': product,
- 'description': description.length ? description.join(' ') : ua,
- 'toString': function() { return this.description; }
- };
- }());
-
- /*--------------------------------------------------------------------------*/
-
- extend(Benchmark, {
-
- /**
- * The version number.
- * @static
- * @member Benchmark
- * @type String
- */
- 'version': '0.2.1',
-
- /**
- * The default options object copied by instances.
- * @static
- * @member Benchmark
- * @type Object
- */
- 'options': { },
-
- // generic Array#forEach/for-in
- 'each': each,
-
- // copy properties to another object
- 'extend': extend,
-
- // generic Array#filter
- 'filter': filter,
-
- // converts a number to a comma-separated string
- 'formatNumber': formatNumber,
-
- // xbrowser Object#hasOwnProperty
- 'hasKey': hasKey,
-
- // generic Array#indexOf
- 'indexOf': indexOf,
-
- // invokes a method on each item in an array
- 'invoke': invoke,
-
- // modifies a string using a template object
- 'interpolate': interpolate,
-
- // xbrowser Array.isArray
- 'isArray': isArray,
-
- // checks internal [[Class]] of an object
- 'isClassOf': isClassOf,
-
- // checks if an object's property is a non-primitive value
- 'isHostType': isHostType,
-
- // generic Array#join for arrays and objects
- 'join': join,
-
- // generic Array#map
- 'map': map,
-
- // no operation
- 'noop': noop,
-
- // retrieves a property value from each item in an array
- 'pluck': pluck,
-
- // generic Array#reduce
- 'reduce': reduce,
-
- // generic String#trim
- 'trim': trim
- });
-
- /*--------------------------------------------------------------------------*/
-
- // IE may ignore `toString` in a for-in loop
- Benchmark.prototype.toString = toString;
-
- extend(Benchmark.prototype, {
-
- /**
- * The delay between test cycles (secs).
- * @member Benchmark
- * @type Number
- */
- 'CYCLE_DELAY': 0.005,
-
- /**
- * A flag to indicate methods will run asynchronously by default.
- * @member Benchmark
- * @type Boolean
- */
- 'DEFAULT_ASYNC': false,
-
- /**
- * The default number of times to execute a test on a benchmark's first cycle.
- * @member Benchmark
- * @type Number
- */
- 'INIT_RUN_COUNT': 5,
-
- /**
- * The maximum time a benchmark is allowed to run before finishing (secs).
- * @member Benchmark
- * @type Number
- */
- 'MAX_TIME_ELAPSED': 5,
-
- /**
- * The minimum sample size required to perform statistical analysis.
- * @member Benchmark
- * @type Number
- */
- 'MIN_SAMPLE_SIZE': 5,
-
- /**
- * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
- * @member Benchmark
- * @type Number
- */
- 'MIN_TIME': 0,
-
- /**
- * The number of times a test was executed.
- * @member Benchmark
- * @type Number
- */
- 'count': 0,
-
- /**
- * A timestamp of when the benchmark was created.
- * @member Benchmark
- * @type Number
- */
- 'created': 0,
-
- /**
- * The number of cycles performed while benchmarking.
- * @member Benchmark
- * @type Number
- */
- 'cycles': 0,
-
- /**
- * The error object if the test failed.
- * @member Benchmark
- * @type Object|Null
- */
- 'error': null,
-
- /**
- * The number of executions per second.
- * @member Benchmark
- * @type Number
- */
- 'hz': 0,
-
- /**
- * A flag to indicate if the benchmark is aborted.
- * @member Benchmark
- * @type Boolean
- */
- 'aborted': false,
-
- /**
- * A flag to indicate if the benchmark is running.
- * @member Benchmark
- * @type Boolean
- */
- 'running': false,
-
- /**
- * Alias of [`Benchmark#addListener`](#Benchmark:addListener).
- * @member Benchmark, Benchmark.Suite
- */
- 'on': addListener,
-
- /**
- * Compiled into the test and executed immediately **before** the test loop.
- * @member Benchmark
- * @type Function
- * @example
- *
- * var bench = new Benchmark({
- * 'fn': function() {
- * a += 1;
- * },
- * 'setup': function() {
- * // reset local var `a` at the beginning of each test cycle
- * a = 0;
- * }
- * });
- *
- * // compiles into something like:
- * var a = 0;
- * var start = new Date;
- * while (count--) {
- * a += 1;
- * }
- * var end = new Date - start;
- */
- 'setup': noop,
-
- /**
- * Compiled into the test and executed immediately **after** the test loop.
- * @member Benchmark
- * @type Function
- */
- 'teardown': noop,
-
- /**
- * An object of stats including mean, margin or error, and standard deviation.
- * @member Benchmark
- * @type Object
- */
- 'stats': {
-
- /**
- * The margin of error.
- * @member Benchmark#stats
- * @type Number
- */
- 'ME': 0,
-
- /**
- * The relative margin of error (expressed as a percentage of the mean).
- * @member Benchmark#stats
- * @type Number
- */
- 'RME': 0,
-
- /**
- * The standard error of the mean.
- * @member Benchmark#stats
- * @type Number
- */
- 'SEM': 0,
-
- /**
- * The sample standard deviation.
- * @member Benchmark#stats
- * @type Number
- */
- 'deviation': 0,
-
- /**
- * The sample arithmetic mean.
- * @member Benchmark#stats
- * @type Number
- */
- 'mean': 0,
-
- /**
- * The sample size.
- * @member Benchmark#stats
- * @type Number
- */
- 'size': 0,
-
- /**
- * The sample variance.
- * @member Benchmark#stats
- * @type Number
- */
- 'variance': 0
- },
-
- /**
- * An object of timing data including cycle, elapsed, period, start, and stop.
- * @member Benchmark
- * @type Object
- */
- 'times': {
-
- /**
- * The time taken to complete the last cycle (secs)
- * @member Benchmark#times
- * @type Number
- */
- 'cycle': 0,
-
- /**
- * The time taken to complete the benchmark (secs).
- * @member Benchmark#times
- * @type Number
- */
- 'elapsed': 0,
-
- /**
- * The time taken to execute the test once (secs).
- * @member Benchmark#times
- * @type Number
- */
- 'period': 0,
-
- /**
- * A timestamp of when the benchmark started (ms).
- * @member Benchmark#times
- * @type Number
- */
- 'start': 0,
-
- /**
- * A timestamp of when the benchmark finished (ms).
- * @member Benchmark#times
- * @type Number
- */
- 'stop': 0
- },
-
- // aborts benchmark (does not record times)
- 'abort': abort,
-
- // registers a single listener
- 'addListener': addListener,
-
- // creates a new benchmark using the same test and options
- 'clone': clone,
-
- // compares benchmark's hertz with another
- 'compare': compare,
-
- // executes listeners of a specified type
- 'emit': emit,
-
- // removes all listeners of a specified type
- 'removeAllListeners': removeAllListeners,
-
- // removes a single listener
- 'removeListener': removeListener,
-
- // reset benchmark properties
- 'reset': reset,
-
- // runs the benchmark
- 'run': run
- });
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * The default options object copied by instances.
- * @static
- * @member Benchmark.Suite
- * @type Object
- */
- Suite.options = { };
-
- /*--------------------------------------------------------------------------*/
-
- extend(Suite.prototype, {
-
- /**
- * The number of benchmarks in the suite.
- * @member Benchmark.Suite
- * @type Number
- */
- 'length': 0,
-
- /**
- * A flag to indicate if the suite is aborted.
- * @member Benchmark.Suite
- * @type Boolean
- */
- 'aborted': false,
-
- /**
- * A flag to indicate if the suite is running.
- * @member Benchmark.Suite
- * @type Boolean
- */
- 'running': false,
-
- /**
- * A bare-bones `Array#forEach` solution.
- * Callbacks may terminate the loop by explicitly returning `false`.
- * @member Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @returns {Object} The suite iterated over.
- */
- 'each': methodize(each),
-
- /**
- * A bare-bones `Array#indexOf` solution.
- * @member Benchmark.Suite
- * @param {Mixed} value The value to search for.
- * @returns {Number} The index of the matched value or `-1`.
- */
- 'indexOf': methodize(indexOf),
-
- /**
- * Invokes a method on all benchmarks in the suite.
- * @member Benchmark.Suite
- * @param {String|Object} name The name of the method to invoke OR options object.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} A new array of values returned from each method invoked.
- */
- 'invoke': methodize(invoke),
-
- /**
- * A bare-bones `Array#map` solution.
- * @member Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @returns {Array} A new array of values returned by the callback.
- */
- 'map': methodize(map),
-
- /**
- * Retrieves the value of a specified property from all benchmarks in the suite.
- * @member Benchmark.Suite
- * @param {String} property The property to pluck.
- * @returns {Array} A new array of property values.
- */
- 'pluck': methodize(pluck),
-
- /**
- * A bare-bones `Array#reduce` solution.
- * @member Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} accumulator Initial value of the accumulator.
- * @returns {Mixed} The accumulator.
- */
- 'reduce': methodize(reduce),
-
- // aborts all benchmarks in the suite
- 'abort': abortSuite,
-
- // adds a benchmark to the suite
- 'add': add,
-
- // registers a single listener
- 'addListener': addListener,
-
- // creates a new suite with cloned benchmarks
- 'clone': cloneSuite,
-
- // executes listeners of a specified type
- 'emit': emit,
-
- // creates a new suite of filtered benchmarks
- 'filter': filterSuite,
-
- // alias of addListener
- 'on': addListener,
-
- // removes all listeners of a specified type
- 'removeAllListeners': removeAllListeners,
-
- // removes a single listener
- 'removeListener': removeListener,
-
- // resets all benchmarks in the suite
- 'reset': resetSuite,
-
- // runs all benchmarks in the suite
- 'run': runSuite,
-
- // array methods
- 'concat': [].concat,
-
- 'join': [].join,
-
- 'pop': aloClean([].pop),
-
- 'push': [].push,
-
- 'reverse': [].reverse,
-
- 'shift': shift,
-
- 'slice': slice,
-
- 'sort': [].sort,
-
- 'splice': aloClean([].splice),
-
- 'unshift': [].unshift
- });
-
- /*--------------------------------------------------------------------------*/
-
- // expose Suite
- Benchmark.Suite = Suite;
-
- // expose Benchmark
- if (typeof exports == 'object' && exports && typeof global == 'object' && global) {
- window = global;
- if (typeof module == 'object' && module && module.exports == exports) {
- module.exports = Benchmark;
- } else {
- exports.Benchmark = Benchmark;
- }
- } else {
- window.Benchmark = Benchmark;
- }
-
- // trigger clock's lazy define early to avoid a security error
- if (IN_AIR) {
- clock({ 'fn': noop, 'count': 1 });
- }
-
- // feature detect
- HAS_TIMEOUT_API = isHostType(window, 'setTimeout') &&
- isHostType(window, 'clearTimeout');
-
- }(this));
|