Merge "Update/refresh RS version tests + add 17 support."
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
index d675edc..881b8a4 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
@@ -21,7 +21,8 @@
import android.util.Log;
import android.view.WindowManager;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -39,6 +40,13 @@
private static final int KB = 1024;
private static final int MB = 1024 * 1024;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // warm-up
+ MemoryNative.runMemcpy(2 * MB, 100);
+ }
+
public void testMemcpyK004() {
doRunMemcpy(4 * KB);
}
@@ -93,13 +101,20 @@
private void doRunMemcpy(int bufferSize) {
double[] result = new double[REPETITION];
- for (int i = 0; i < REPETITION; i++) {
- result[i] = MemoryNative.runMemcpy(bufferSize, REPEAT_IN_EACH_CALL);
+ int repeatInEachCall = REPEAT_IN_EACH_CALL;
+ if (bufferSize < (1 * MB)) {
+ // too small buffer size finishes too early to give accurate result.
+ repeatInEachCall *= (1 * MB / bufferSize);
}
- getReportLog().printArray("ms", result, false);
+ for (int i = 0; i < REPETITION; i++) {
+ result[i] = MemoryNative.runMemcpy(bufferSize, repeatInEachCall);
+ }
+ getReportLog().printArray("memcpy time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
double[] mbps = ReportLog.calcRatePerSecArray(
- (double)bufferSize * REPEAT_IN_EACH_CALL / 1024.0 / 1024.0, result);
- getReportLog().printArray("MB/s", mbps, true);
+ (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
+ getReportLog().printArray("memcpy throughput", mbps, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
Stat.StatResult stat = Stat.getStat(mbps);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Point size = new Point();
@@ -108,8 +123,7 @@
double pixels = size.x * size.y;
// now this represents how many times the whole screen can be copied in a sec.
double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
- double screensPerSecStddev = stat.mStddev / pixels * 1024.0 * 1024.0 / 4.0;
- getReportLog().printSummary("screen copies per sec", screensPerSecAverage,
- PerfResultType.HIGHER_BETTER, screensPerSecStddev);
+ getReportLog().printSummary("memcpy in fps", screensPerSecAverage,
+ ResultType.HIGHER_BETTER, ResultUnit.FPS);
}
}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
index 1e39441..087cf0d 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
@@ -19,7 +19,6 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -28,7 +27,8 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.SystemUtil;
@@ -299,12 +299,14 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
times);
- report.printArray("MB/s",
- mbps, true);
- report.printArray("Rd amount", rdAmount, true);
+ report.printArray("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ // This is just the amount of IO returned from kernel. So this is performance neutral.
+ report.printArray("read amount", rdAmount, ResultType.NEUTRAL, ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
+ report.printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
/**
@@ -349,12 +351,14 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
times);
- report.printArray("MB/s",
- mbps, true);
- report.printArray("Wr amount", wrAmount, true);
+ report.printArray("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ report.printArray("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
+ report.printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
/**
@@ -387,11 +391,12 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)bufferSize / 1024 / 1024,
times);
- report.printArray(i + "-th round MB/s",
- mbps, true);
+ report.printArray(i + "-th round throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
}
Stat.StatResult stat = Stat.getStat(mbpsAll);
- report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
+ report.printSummary("update throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
index 52dcb16..4a9c3dd 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
@@ -19,7 +19,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -48,7 +49,8 @@
public void testSingleSequentialWrite() throws Exception {
final int numberOfFiles =(int)(FileUtil.getFileSizeExceedingMemory(
getContext(), BUFFER_SIZE) / BUFFER_SIZE);
- getReportLog().printValue("files", numberOfFiles);
+ getReportLog().printValue("files", numberOfFiles, ResultType.NEUTRAL,
+ ResultUnit.COUNT);
final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
final File[] files = FileUtil.createNewFiles(getContext(), DIR_SEQ_WR,
numberOfFiles);
@@ -62,12 +64,13 @@
}
});
double[] mbps = ReportLog.calcRatePerSecArray((double)BUFFER_SIZE / 1024 / 1024, times);
- getReportLog().printArray("try " + numberOfFiles + " files, result MB/s",
- mbps, true);
- getReportLog().printArray("Wr amount", wrAmount, true);
+ getReportLog().printArray("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ getReportLog().printArray("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
@TimeoutReq(minutes = 60)
@@ -85,8 +88,9 @@
final File file = FileUtil.createNewFilledFile(getContext(),
DIR_SEQ_RD, fileSize);
long finish = System.currentTimeMillis();
- getReportLog().printValue("write size " + fileSize + " result MB/s",
- ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start));
+ getReportLog().printValue("write throughput for test file of length " + fileSize,
+ ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start),
+ ResultType.HIGHER_BETTER, ResultUnit.MBPS);
final int NUMBER_READ = 10;
@@ -105,10 +109,10 @@
}
});
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / 1024 / 1024, times);
- getReportLog().printArray("read MB/s",
- mbps, true);
+ getReportLog().printArray("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
}
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
index 4231250..8b17171 100644
--- a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -18,7 +18,8 @@
import android.cts.util.TimeoutReq;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -96,10 +97,11 @@
for (int i = 0; i < numberRepeat; i++) {
result[i] = CpuNative.runSort(arrayLength, numberRepeatInEachCall);
}
- getReportLog().printArray("ms", result, false);
+ getReportLog().printArray("sorting time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(result);
- getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("sorting time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
/**
@@ -114,10 +116,11 @@
for (int i = 0; i < numberRepeat; i++) {
result[i] = CpuNative.runMatrixMultiplication(n, numberRepeatInEachCall);
}
- getReportLog().printArray("ms", result, false);
+ getReportLog().printArray("matrix mutiplication time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(result);
- getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("matrix mutiplication time", stat.mAverage,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
}
}
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
index d6d5f06..a2fd8d9 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
@@ -22,7 +22,8 @@
import android.view.Display;
import android.view.WindowManager;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsActivityInstrumentationTestCase2;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -118,10 +119,13 @@
}
}
Log.i(TAG, " fps nominal " + fpsNominal + " fps measured " + fpsMeasured);
- getReportLog().printArray("intervals ms", intervals, false);
- getReportLog().printArray("jankiness ms", jankiness, false);
- getReportLog().printSummaryFull("Frame interval",
- "max delay ms", maxDelay, PerfResultType.LOWER_BETTER,
- "number of jank", jankNumber, PerfResultType.LOWER_BETTER);
+ getReportLog().printArray("intervals", intervals, ResultType.NEUTRAL,
+ ResultUnit.MS);
+ getReportLog().printArray("jankiness", jankiness, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ getReportLog().printValue("number of jank", jankNumber, ResultType.LOWER_BETTER,
+ ResultUnit.COUNT);
+ getReportLog().printSummary("max jank delay", maxDelay, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
}
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
index 5324669..9fd43de 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
@@ -18,7 +18,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsActivityInstrumentationTestCase2;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -66,9 +67,10 @@
assertTrue(activity.scrollToTop());
}
});
- getReportLog().printArray("ms", results, false);
+ getReportLog().printArray("scrolling time", results, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("scrolling time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
}
diff --git a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
index 2e99bb8..dd7b834 100644
--- a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
+++ b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
@@ -19,7 +19,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.Stat.StatResult;
@@ -66,9 +67,11 @@
rebootDevice();
}
});
- mReport.printArray("time in ms", result, false);
+ mReport.printArray("bootup time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER, stat.mStddev);
+ mReport.printSummary("bootup time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
private void rebootDevice() throws DeviceNotAvailableException {
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/benchmarks.json b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/benchmarks.json
new file mode 100644
index 0000000..74628cd
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/benchmarks.json
@@ -0,0 +1,359 @@
+var ROBOHORNET_DATA = {
+ /**
+ * Benchmark version.
+ *
+ * @type {string}
+ */
+ version: 'RH-A1',
+
+ /**
+ * List of product, application and library tags.
+ *
+ * name: Short human readable name of tag.
+ */
+ productTags: {
+ G_SPREADSHEETS: {
+ name: 'Google Spreadsheets'
+ },
+ G_PRESENTATIONS: {
+ name: 'Google Presentations'
+ },
+ G_MAPS: {
+ name: 'Google Maps'
+ },
+ YUI: {
+ name: 'YUI'
+ },
+ JQUERY: {
+ name: 'jQuery'
+ },
+ EMBER: {
+ name: 'Ember'
+ },
+ HANDLEBARS: {
+ name: 'Handlebars'
+ },
+ METAMORPH: {
+ name: 'Metamorph.js'
+ },
+ CAPPUCCINO: {
+ name: 'Cappuccino'
+ }
+ },
+
+
+ /**
+ * List of technology tags.
+ *
+ * name: Short human readable name of tag.
+ */
+ technologyTags: {
+ TABLE: {
+ name: 'Table'
+ },
+ DOM: {
+ name: 'DOM'
+ },
+ CSS_SELECTORS: {
+ name: 'CSS Selectors'
+ },
+ CANVAS: {
+ name: 'Canvas'
+ },
+ SCROLLING: {
+ name: 'Scrolling'
+ },
+ SVG: {
+ name: 'SVG'
+ },
+ JS: {
+ name: 'Javascript'
+ },
+ MATH: {
+ name: 'Math'
+ }
+ },
+
+ /**
+ * List of benchmarks. Array of objects.
+ *
+ * name: Short human readable name of benchmark.
+ * description: Description of benchmark.
+ * filename: Filename of benchmark, each benchmark file must implement a test
+ * function and may implement a setUp and tearDown function.
+ * runs: List of parameters to feed to test, setUp and tearDown
+ * functions. For each entry a test run is constructed and the
+ * parameter in the second field is fed to the test function.
+ * The first field is a short human readable description of the
+ * parameter.
+ * weight: Weight of test as relative to the other tests in the file.
+ * A percentage weight will be computed based on the relative
+ * weight of each benchmark.
+ * baselineTime: Baseline time, in milliseconds.
+ */
+ benchmarks: [
+ {
+ name: 'Add Rows to Table',
+ issueNumber: 10,
+ description: 'Tests adding rows to an existing table',
+ filename: 'tests/addrow.html',
+ runs: [
+ ['250 rows', 250],
+ ['1000 rows', 1000]
+ ],
+ weight: 2,
+ baselineTime: 49.68,
+ tags: ['G_SPREADSHEETS', 'TABLE', 'DOM', 'YUI', 'JQUERY', 'EMBER'],
+ extended: false
+ },
+
+ {
+ name: 'Add Columns to Table',
+ issueNumber: 11,
+ description: 'Tests adding columns to an existing table',
+ filename: 'tests/addcol.html',
+ runs: [
+ ['250 columns', 250],
+ ['1000 columns', 1000]
+ ],
+ weight: 1.5,
+ baselineTime: 45.29,
+ tags: ['G_SPREADSHEETS', 'TABLE', 'DOM', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: 'Descendant Selector',
+ issueNumber: 12,
+ description: 'Tests descendant selectors at different DOM depths',
+ filename: 'tests/descendantselector.html',
+ runs: [
+ ['1000 nodes deep', 1000]
+ ],
+ weight: 2,
+ baselineTime: 87.19,
+ tags: ['DOM', 'CSS_SELECTORS', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: '2D Canvas toDataURL',
+ issueNumber: 14,
+ description: 'Test converting a 2D canvas to a data URI',
+ filename: 'tests/canvastodataurl.html',
+ runs: [
+ ['256x256, 1000 lines', [256, 1000]],
+ ['512x512, 1000 lines', [512, 1000]]
+ ],
+ weight: 2,
+ baselineTime: 304.56,
+ tags: ['CANVAS', 'G_PRESENTATIONS'],
+ extended: false
+ },
+
+ {
+ name: '2D Canvas clearRect',
+ issueNumber: 43,
+ description: 'Test clearing a 2D canvas',
+ filename: 'tests/canvasclear.html',
+ runs: [
+ ['256x256', 256],
+ ['512x512', 512]
+ ],
+ weight: 2,
+ baselineTime: 16.73,
+ tags: ['CANVAS', 'G_PRESENTATIONS', 'CAPPUCCINO'],
+ extended: false
+ },
+
+ {
+ name: 'innerHTML Table',
+ issueNumber: 15,
+ description: 'Test table render speed after innerHTML.',
+ filename: 'tests/createtable.html',
+ runs: [
+ ['200x10', [200, 10]],
+ ['200x50', [200, 50]],
+ ['200x100', [200, 100]]
+ ],
+ weight: 2,
+ baselineTime: 283.53,
+ tags: ['DOM', 'G_SPREADSHEETS', 'TABLE', 'YUI', 'JQUERY', 'EMBER'],
+ extended: false
+ },
+
+ {
+ name: 'Table scrolling',
+ issueNumber: 16,
+ description: 'Test scrolling speed using scrollTop',
+ filename: 'tests/table_scrolltop.html',
+ runs: [
+ ['500x10', [500, 10]],
+ ['500x50', [500, 50]],
+ ['1000,10', [1000, 10]],
+ ['1000,50', [1000, 50]]
+ ],
+ weight: 2,
+ baselineTime: 964.37,
+ tags: ['DOM', 'G_SPREADSHEETS', 'TABLE', 'SCROLLING', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: 'Resize columns',
+ issueNumber: 17,
+ description: 'Test resizing columns in a table',
+ filename: 'tests/resizecol.html',
+ runs: [
+ ['500x10', [500, 10]],
+ ['500x50', [500, 50]]
+ ],
+ weight: 2,
+ baselineTime: 2739.90,
+ tags: ['DOM', 'TABLE', 'G_SPREADSHEETS', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: 'SVG resize',
+ issueNumber: 18,
+ description: 'Test resizing SVGs',
+ filename: 'tests/svgresize.html',
+ runs: [
+ ['50 SVGs', 50],
+ ['100 SVGs', 100]
+ ],
+ weight: 2,
+ baselineTime: 502.73,
+ tags: ['SVG', 'G_PRESENTATIONS', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: 'Object Scope Access',
+ issueNumber: 31,
+ description: 'Test different methods to access variables from an object',
+ filename: 'tests/object_scope.html',
+ runs: [
+ ['100,000 runs', 100000],
+ ['1,000,000 runs', 1000000],
+ ['10,000,000 runs', 10000000]
+ ],
+ weight: 1,
+ baselineTime: 100.0,
+ tags: ['JS'],
+ extended: false
+ },
+
+ {
+ name: 'ES5 Property Accessors',
+ issueNumber: 19,
+ description: 'Test ES5 getter/setters',
+ filename: 'tests/property_accessors.html',
+ runs: [
+ ['Getter', 'GET'],
+ ['Setter', 'SET'],
+ ['Combined', '']
+ ],
+ weight: 1,
+ baselineTime: 89.57,
+ tags: ['JS', 'EMBER'],
+ extended: false
+ },
+
+ {
+ name: 'Argument instantiation',
+ issueNumber: 21,
+ description: 'Test referencing the arguments array',
+ filename: 'tests/varargs.html',
+ runs: [
+ ['100 Instantiations', 100],
+ ['1000 Instantiations', 1000],
+ ['1000000 Instantiations', 1000000]
+ ],
+ weight: 2,
+ baselineTime: 315.13,
+ tags: ['JS', 'YUI', 'JQUERY', 'EMBER', 'CAPPUCCINO'],
+ extended: false
+ },
+
+ {
+ name: 'Animated GIFS',
+ issueNumber: 22,
+ description: 'Test scrolling lots of animated GIFs',
+ filename: 'tests/animated_gifs.html',
+ runs: [
+ ['20x10 GIFs', [20, 10]],
+ ['100x10 GIFs', [100, 10]],
+ ['100x100 GIFs', [100, 100]]
+ ],
+ weight: 0.25,
+ baselineTime: 127.69,
+ tags: ['DOM', 'SCROLLING'],
+ extended: false
+ },
+
+ {
+ name: 'offsetHeight triggers reflow',
+ issueNumber: 30,
+ description: 'Test the affect of forcing a reflow by calling offsetHeight',
+ filename: 'tests/offsetreflow.html',
+ runs: [
+ ['100 Reflows', 100],
+ ['1000 Reflows', 1000],
+ ['10000 Reflows', 10000]
+ ],
+ weight: 3,
+ baselineTime: 723.69,
+ tags: ['DOM', 'G_SPREADSHEETS', 'YUI', 'JQUERY', 'EMBER'],
+ extended: false
+ },
+
+ {
+ name: 'DOM Range API',
+ issueNumber: 9,
+ description: 'Test replacing a number of DOM nodes using the Range API',
+ filename: 'tests/range.html',
+ runs: [
+ ['50 Nodes', 50],
+ ['100 Nodes', 100]
+ ],
+ weight: 1,
+ baselineTime: 103.79,
+ tags: ['DOM', 'YUI', 'METAMORPH'],
+ extended: false
+ },
+
+ {
+ name: 'Write to localStorage',
+ issueNumber: 23,
+ description: 'Test the localStorage write performance',
+ filename: 'tests/localstorage_write.html',
+ runs: [
+ ['50 Writes', 50],
+ ['100 Writes', 100],
+ ['1000 Writes', 1000]
+ ],
+ weight: 2,
+ baselineTime: 49.84,
+ tags: ['JS', 'YUI'],
+ extended: false
+ },
+
+ {
+ name: 'Read from localStorage',
+ issueNumber: 24,
+ description: 'Test the localStorage read performance',
+ filename: 'tests/localstorage_read.html',
+ runs: [
+ ['50 Reads', 50],
+ ['100 Reads', 100],
+ ['1000 Reads', 1000]
+ ],
+ weight: 2,
+ baselineTime: 33.93,
+ tags: ['JS', 'YUI'],
+ extended: false
+ }
+ ]
+};
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.ttf b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.ttf
new file mode 100755
index 0000000..6848db0
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.ttf
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.woff
new file mode 100644
index 0000000..5282c2e
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Black.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.ttf b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.ttf
new file mode 100755
index 0000000..7434369
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.ttf
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.woff
new file mode 100644
index 0000000..68729b7
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Bold.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-BoldItalic.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-BoldItalic.woff
new file mode 100644
index 0000000..6f396fc
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-BoldItalic.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Hairline.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Hairline.woff
new file mode 100644
index 0000000..2ed3609
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Hairline.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Italic.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Italic.woff
new file mode 100644
index 0000000..03c8be2
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Italic.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.ttf b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.ttf
new file mode 100755
index 0000000..04ea8ef
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.ttf
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.woff b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.woff
new file mode 100644
index 0000000..26a056b
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/fonts/Lato-Regular.woff
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/icon.png b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/icon.png
new file mode 100644
index 0000000..961dc75
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/icon.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/main.css b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/main.css
new file mode 100644
index 0000000..d44c922
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/main.css
@@ -0,0 +1,557 @@
+/*
+ * Orange: #F05A2A
+ * Light-Orange: #F6943D
+ * Yellow: #FCCF4D
+ * Light-Yellow: #FDE7A7
+ * Very-Light-Yellow: #FEF1CA
+ * Dark-Gray: #454545
+ * Light-Gray: #AAAAAA;
+ */
+
+@font-face {
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 400;
+ src: local('Lato Italic'), local('Lato-Italic'), url('fonts/Lato-Italic.woff') format('woff');
+}
+@font-face {
+ font-family: 'Lato';
+ font-style: italic;
+ font-weight: 700;
+ src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url('fonts/Lato-BoldItalic.woff') format('woff');
+}
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Lato Bold'), local('Lato-Bold'), url('fonts/Lato-Bold.woff') format('woff');
+}
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Lato Regular'), local('Lato-Regular'), url('fonts/Lato-Regular.woff') format('woff');
+}
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Lato Hairline'), local('Lato-Hairline'), url('fonts/Lato-Hairline.woff') format('woff');
+}
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Lato Black'), local('Lato-Black'), url('fonts/Lato-Black.woff') format('woff');
+}
+
+html {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ margin: 0;
+ height: 100%;
+ font-family: Lato, Tahoma, sans-serif;
+ color: #454545;
+ background-color: #FEF1CA;
+ background-image: -webkit-linear-gradient(top, #FDE7A7, #FEF1CA 300px);
+ background-image: -moz-linear-gradient(top, #FDE7A7, #FEF1CA 300px);
+ background-image: -o-linear-gradient(top, #FDE7A7, #FEF1CA 300px);
+ background-repeat: no-repeat;
+ font-size: 14px;
+ overflow-x: hidden;
+}
+
+table {
+ border-spacing: 0;
+}
+
+th, td {
+ padding: 0 1ex;
+}
+
+thead small {
+ padding-left: 1em;
+ font-size: x-small;
+}
+
+a {
+ color: inherit;
+}
+
+#sidebar {
+ float: left;
+ width: 300px;
+ margin-left: 15px;
+ margin-right: 10px;
+}
+
+#test-container {
+ margin: 0 auto;
+ width: 1050px;
+}
+
+#iframe-wrap {
+ height: 300px;
+ margin: 9px 23px 0 0;
+ overflow: hidden;
+ width: 300px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+iframe {
+ height: 300px;
+ width: 300px;
+}
+
+#tests-wrap {
+ display: inline-block;
+}
+
+#tests > thead > tr > td {
+ border-bottom: 1px solid #454545;
+}
+
+#tests tr.summary-row td {
+ border-top: 1px solid #454545;
+}
+
+#tests tr.summary-row td:first-child {
+ text-align: right;
+}
+
+#tests tr.summary-row td.final {
+ font-weight: bold;
+}
+
+#tests > tbody > tr > td {
+ font-weight: normal;
+}
+
+#tests > tbody > tr > td.name {
+ padding-left: 2ex;
+}
+
+#tests > thead > tr > td.number, #tests > tbody > tr > td.number {
+ width: 10ex;
+}
+
+#tests > tbody > tr > td.details {
+ border-top: none;
+ padding: 0.5ex 5ex 2ex 5ex;
+}
+
+#tests > tbody > tr.disabled {
+ color: rgba(69, 69, 69, 0.5);
+}
+
+#tests > tbody > tr td > span {
+ font-size: 16px;
+ margin-left: 16px;
+ font-weight: bold;
+ cursor: pointer;
+ position: relative;
+ padding-left: 19px;
+}
+
+#tests > tbody > tr td > span:hover {
+ color: black;
+}
+
+#tests > tbody > tr td > span::before {
+ content: "\25BA";
+ font-size: 10px;
+ margin-right: 10px;
+ position: absolute;
+ top: 4px;
+ left: 0;
+ cursor: pointer;
+ display: inline-block;
+}
+
+#tests > tbody > tr.expanded td > span::before {
+ -webkit-transform: rotate(90deg);
+ -moz-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ -o-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+
+div[id^=tags] {
+ text-align: center;
+}
+
+.details > div {
+ font-size: 14px;
+ border: 1px dotted #FCCF4D;
+ background: #FDE7A7;
+}
+
+tr.details td {
+ display: none;
+}
+
+tr.details.expanded td {
+ display: table-cell;
+}
+
+.details > div.disabled {
+ opacity: 0.5;
+}
+
+.runs {
+ margin-top: 0.5ex;
+ table-layout: fixed;
+}
+
+.runs > thead > tr > td.number, .runs > tbody > tr > td.number {
+ width: 10ex;
+}
+
+.runs > thead > tr > td.name, .runs > tbody > tr > td.name {
+ width: 30ex;
+}
+
+.runs > tbody > tr > td {
+ font-weight: normal;
+ border-top:1px dotted #FCCF4D;
+}
+
+td.number {
+ text-align: right;
+}
+
+.tag {
+ display: inline-block;
+ background-color: #FCCF4D;
+ color: #FEF1CA;
+ border-radius: 3px;
+ padding: 2px;
+ margin: 2px;
+ cursor: pointer;
+}
+
+.tag.technology {
+ background-color: #F6943D;
+}
+
+.tag.special {
+ background-color: #AAAAAA;
+}
+
+.tag.inactive {
+ opacity: 0.2;
+}
+
+.tag.partially-inactive {
+ opacity: 0.6;
+}
+
+.tag:hover {
+ opacity: 1.0;
+ color: #454545;
+}
+
+#console {
+ color: silver;
+ font-size: 10px;
+ overflow-y: auto;
+ min-height: 200px;
+}
+
+#testSection {
+ clear: both;
+}
+
+#testFrame {
+ width: 300px;
+ height: 300px;
+ border: 1px solid silver;
+}
+
+h2 {
+ font-size: 14px;
+ font-weight: normal;
+ margin: 0;
+}
+
+h1 {
+ font-size: 36px;
+ margin: 0 10px 0 0;
+ display:inline;
+}
+
+header {
+ border-bottom: 1px solid #454545;
+ margin-bottom: 15px;
+ padding:15px;
+ position: relative;
+}
+
+header .logo {
+ display: inline-block;
+}
+
+header .logo h1 {
+ /* Necessary for positioning of "ALPHA" tag */
+ position: relative;
+}
+
+header .logo h1::after {
+ content: "ALPHA";
+ font-size: 14px;
+ font-weight:normal;
+ position:absolute;
+ bottom: -10px;
+ right: 0px;
+ color: #F05A2A;
+}
+
+header img {
+ height: 40px;
+ width: 40px;
+}
+
+#progress {
+ position: absolute;
+ background-color: #F05A2A;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ z-index: -1;
+ margin-left: -100%;
+ -webkit-transition: opacity 3s ease;
+ -moz-transition: opacity 3s ease;
+ -o-transition: opacity 3s ease;
+ transition: opacity 3s ease;
+}
+
+div.results {
+ margin: 120px 0;
+ text-align: center;
+ width: 100%;
+ -webkit-transition: .3s ease;
+ -moz-transition: .3s ease;
+ -o-transition: .3s ease;
+ transition: .3s ease;
+}
+
+.running div.results {
+ margin: 0;
+}
+
+header > div > div {
+
+}
+
+.results div {
+ color: #454545;
+ font-weight: bold;
+ font-size: 108px;
+ font-style: normal;
+}
+
+#index-prefix {
+ color: #FCCF4D;
+ font-weight: 100;
+}
+
+#index {
+ opacity: 0.1;
+ -webkit-transition: opacity 2s;
+ -moz-transition: opacity 2s;
+ -o-transition: opacity 2s;
+ transition: opacity 2s;
+}
+
+#index.final {
+ opacity: 1.0;
+}
+
+#runButton {
+ float: right;
+ margin: 0 130px 0 10px;
+ padding: 5px;
+ border-radius: 5px;
+ background-color: #F05A2A;
+ background-image: -webkit-linear-gradient(top, #F05A2A, #F6943D);
+ background-image: -moz-linear-gradient(top, #F05A2A, #F6943D);
+ background-image: -o-linear-gradient(top, #F05A2A, #F6943D);
+ background-image: linear-gradient(top, #F05A2A, #F6943D);
+ color: #FDE7A7;
+ font-size: 26px;
+ /* Large enough so the size doesn't change for any of the messages */
+ width:175px;
+ border: 1px solid #FCCF4D;
+ cursor: pointer;
+ box-shadow: 0px 2px 5px 0px #454545;
+ -webkit-animation-name: button-flourish;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-iteration-count: 1;
+ -webkit-animation-timing-function: ease;
+ /* we ideally want to flourish just as the logo crosses on load. */
+ -webkit-animation-delay: 1s;
+
+ -moz-animation-name: button-flourish;
+ -moz-animation-duration: 2s;
+ -moz-animation-iteration-count: 1;
+ -moz-animation-timing-function: ease;
+ /* we ideally want to flourish just as the logo crosses on load. */
+ -moz-animation-delay: 1s;
+
+ -o-animation-name: button-flourish;
+ -o-animation-duration: 2s;
+ -o-animation-iteration-count: 1;
+ -o-animation-timing-function: ease;
+ /* we ideally want to flourish just as the logo crosses on load. */
+ -o-animation-delay: 1s;
+
+ animation-name: button-flourish;
+ animation-duration: 2s;
+ animation-iteration-count: 1;
+ animation-timing-function: ease;
+ /* we ideally want to flourish just as the logo crosses on load. */
+ animation-delay: 1s;
+}
+
+#runButton:active {
+ background-image: -webkit-linear-gradient(top, #F6943D, #F05A2A);
+ background-image: -moz-linear-gradient(top, #F6943D, #F05A2A);
+ background-image: -o-linear-gradient(top, #F6943D, #F05A2A);
+ background-image: linear-gradient(top, #F6943D, #F05A2A);
+}
+
+#runButton:hover {
+ color:#FCCF4D;
+}
+
+#runButton:disabled {
+ opacity: 0.6;
+ color: #FCCF4D;
+}
+
+#status {
+ font-size: 14px;
+ text-align: center;
+ color: #666666;
+ font-style: italic;
+ font-weight: normal;
+}
+
+#results > thead > tr > td {
+ border-bottom: 1px solid black;
+}
+
+header img {
+ -webkit-transform-origin: 30% 65%;
+ -moz-transform-origin: 30% 65%;
+ -o-transform-origin: 30% 65%;
+ transform-origin: 30% 65%;
+ /* Trigger compositing now so we don't get a flash when it does */
+ -webkit-transform: translate(0,0,0);
+ /* Show over top of the Run button */
+ z-index:1;
+}
+
+body.ready header img {
+ -webkit-animation-name: logo-ready;
+ -webkit-animation-duration: 4s;
+ -webkit-animation-iteration-count: 1;
+ -webkit-animation-timing-function: ease;
+
+ -moz-animation-name: logo-ready;
+ -moz-animation-duration: 4s;
+ -moz-animation-iteration-count: 1;
+ -moz-animation-timing-function: ease;
+}
+
+@-webkit-keyframes logo-ready {
+ from {
+ -webkit-transform: translate(0, 0);
+ }
+ 10% {
+ -webkit-transform: translate(0, 0) rotate(34deg);
+ }
+ 17% {
+ -webkit-transform: translate(-50%, 0) rotate(45deg);
+ }
+ 50% {
+ -webkit-transform: translate(10000%, 0) rotate(45deg);
+ opacity: 1.0;
+ }
+ 51% {
+ opacity: 0.0;
+ }
+ 52% {
+ -webkit-transform: translate(0, 0);
+ }
+ to {
+ opacity: 1.0;
+ }
+}
+
+@-moz-keyframes logo-ready {
+ from {
+ -moz-transform: translate(0, 0);
+ }
+ 10% {
+ -moz-transform: translate(0, 0) rotate(34deg);
+ }
+ 17% {
+ -moz-transform: translate(-50%, 0) rotate(45deg);
+ }
+ 50% {
+ -moz-transform: translate(10000%, 0) rotate(45deg);
+ opacity: 1.0;
+ }
+ 51% {
+ opacity: 0.0;
+ }
+ 52% {
+ -moz-transform: translate(0, 0);
+ }
+ to {
+ opacity: 1.0;
+ }
+}
+
+@-webkit-keyframes button-flourish {
+ from {
+ -webkit-transform: scale(1.0);
+ }
+ 10% {
+ -webkit-transform: scale(1.15);
+ }
+ to {
+ -webkit-transform: scale(1.0);
+ }
+}
+
+@-moz-keyframes button-flourish {
+ from {
+ -moz-transform: scale(1.0);
+ }
+ 10% {
+ -moz-transform: scale(1.15);
+ }
+ to {
+ -moz-transform: scale(1.0);
+ }
+}
+
+footer #fork {
+ -webkit-animation: button-flourish 2s ease 1.02s 1;
+ -moz-animation: button-flourish 2s ease 1.02s 1;
+ -o-animation: button-flourish 2s ease 1.02s 1;
+ animation: button-flourish 2s ease 1.02s 1;
+
+ position: absolute;
+ top: 0;
+ right: 0;
+ border: 0;
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.html
new file mode 100644
index 0000000..801714e
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>RoboHornet</title>
+ <meta name="viewport" content="width=1024">
+ <script>
+ if (document.location.host.toLowerCase().indexOf('robohornet.com') != -1)
+ document.location.replace('http://www.robohornet.org');
+ </script>
+ <script src="third_party/benchmark.js/benchmark.js"></script>
+ <script src="third_party/classList.js/classList.min.js"></script>
+ <script src="robohornet.js"></script>
+ <script src="benchmarks.json"></script>
+ <link href="main.css" rel="stylesheet" type="text/css">
+ <link rel="icon" href="icon.png" type="image/png">
+ </head>
+ <body>
+ <header>
+ <div>
+ <div class='logo'>
+ <h1>RoboHornet</h1><img src='robohornet.png'>
+ </div>
+ <button id="runButton" onclick="__robohornet__.run()" disabled="true">Loading...</button>
+ </div>
+ <div class='results'>
+ <div>
+ <span id="index-prefix">RH:</span><span id='index'>000.00</span>
+ </div>
+ <div id="status">
+ RoboHornet is a benchmark designed around performance pain points real web developers care about.
+ <br>
+ <a href="javascript:__robohornet__.run()">Run the test</a> to see the index or <a href='https://github.com/robohornet/robohornet/blob/master/CONTRIBUTING.md' target="_blank">learn how to participate</a>.
+ </div>
+ </div>
+ <div id='progress'></div>
+ </header>
+
+ <footer>
+ <a href="https://github.com/robohornet/robohornet"><img id="fork" src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png" alt="Fork me on GitHub"></a>
+ </footer>
+
+ <section>
+ <div id="tags-special"><span>Suite: </span></div>
+ <div id="tags-technology"><span>Technology: </span></div>
+ <div id="tags-app"><span>Affects: </span></div>
+ <div id="test-container">
+ <div id="iframe-wrap">
+ </div>
+ <div id="tests-wrap">
+ <table id="tests">
+ <thead>
+ <td>Benchmark</td>
+ <td>Status</td>
+ <td class="number">Time</td>
+ <td class="number">Baseline</td>
+ <td class="number">Weight</td>
+ <td class="number">Index</td>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </div>
+ </section>
+
+ <script>
+ var __robohornet__ = new robohornet.Runner(ROBOHORNET_DATA);
+ //PTS, auto start
+ window.setTimeout(function() { __robohornet__.run(); }, 500);
+ </script>
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-34237734-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ </body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.js
new file mode 100644
index 0000000..b29fdea
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.js
@@ -0,0 +1,809 @@
+var robohornet = {};
+
+robohornet.Status = {
+ LOADING: 0,
+ READY: 1,
+ RUNNING: 2
+};
+
+// Captions for the run button based on the robohornet.Status enum.
+robohornet.RunButtonCaption = {
+ 0: 'Loading...',
+ 1: 'Run',
+ 2: 'Running...'
+};
+
+robohornet.enabledBenchmarks = {
+ SUCCESS: 0,
+ LOADING: 1,
+ RUNNING: 2,
+ PENDING: 3,
+ LOAD_FAILED: 4,
+ RUN_FAILED: 5,
+ SKIPPED: 6,
+ POPUP_BLOCKED: 7,
+ ABORTED: 8
+};
+
+// Descriptions for the robohornet.enabledBenchmarks enum.
+robohornet.enabledBenchmarksDescription = {
+ 0: 'Completed successfully',
+ 1: 'Loading...',
+ 2: 'Running...',
+ 3: 'Pending',
+ 4: 'Failed to load',
+ 5: 'Failed to run',
+ 6: 'Skipped',
+ 7: 'Benchmark window blocked',
+ 8: 'Aborted by user'
+};
+
+robohornet.TagType = {
+ SPECIAL: 'special',
+ TECHNOLOGY: 'technology',
+ APP: 'app'
+};
+
+
+/**
+ * Class representing a the RoboHornet test runner.
+ *
+ * @param {string} version String describing this version of the benchmark.
+ * @param {Array.<Object>} benchmarks Array of benchmark json objects.
+ * @constructor
+ */
+robohornet.Runner = function(data) {
+ this.testsContainer = document.getElementById('tests');
+ this.statusElement_ = document.getElementById('status');
+ this.runElement_ = document.getElementById('runButton');
+ this.progressElement_ = document.getElementById('progress');
+ this.indexElement_ = document.getElementById('index');
+ this.tagsSpecialElement_ = document.getElementById('tags-special');
+ this.tagsTechElement_ = document.getElementById('tags-technology');
+ this.tagsAppElement_ = document.getElementById('tags-app');
+
+ document.getElementById('index-prefix').textContent = data.version + ':';
+
+ this.hasExtendedBenchmark_ = false;
+
+ this.initTags_(data.technologyTags, data.productTags);
+ this.initBenchmarks_(data.benchmarks);
+ this.initTagUi_();
+ this.initBenchmarkUi_();
+ this.digestHash_();
+
+ this.setStatus_(robohornet.Status.READY);
+
+ this.progressCallback_ = bind(this.progressTransitionDone_, this);
+
+ window.addEventListener('unload', bind(this.onWindowUnload_, this), false);
+};
+
+(function() {
+
+ var IS_WEBKIT = window.navigator.userAgent.indexOf('WebKit') != -1;
+
+ var TRANSITION_END_EVENT = IS_WEBKIT ? 'webkitTransitionEnd' : 'transitionend';
+
+ var requestAnimationFrameFunction = window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.oRequestAnimationFrame;
+
+ function formatNumber(n, minWidth, decimalPlaces) {
+ return Array(Math.max((minWidth + 1) - n.toFixed(0).length, 0)).join('0') +
+ n.toFixed(decimalPlaces);
+ }
+
+ function enableClass(element, className, enable) {
+ if (enable)
+ element.classList.add(className);
+ else
+ element.classList.remove(className);
+ }
+
+ function createElement(tagName, textContent, opt_className) {
+ var element = document.createElement(tagName);
+ if (opt_className)
+ element.className = opt_className;
+ element.appendChild(document.createTextNode(textContent));
+ return element;
+ }
+
+ function addChildren(element, children) {
+ for (var i = 0; i < children.length; i++) {
+ element.appendChild(children[i]);
+ }
+ }
+
+ function addToTag(benchmark, tag) {
+ benchmark.tags.push(tag);
+ tag.benchmarks.push(benchmark);
+ }
+
+ var _p = robohornet.Runner.prototype;
+
+ _p.initTagUi_ = function() {
+ for (var tag, i = 0; tag = this.tags_[i]; i++) {
+ tag.element = this.makeTagElement_(tag);
+ if (tag.type == robohornet.TagType.TECHNOLOGY) {
+ this.tagsTechElement_.appendChild(tag.element);
+ }
+ else if (tag.type == robohornet.TagType.APP) {
+ this.tagsAppElement_.appendChild(tag.element);
+ }
+ else {
+ if (tag.id == 'EXTENDED' && !this.hasExtendedBenchmark_)
+ continue;
+ this.tagsSpecialElement_.appendChild(tag.element);
+ }
+ }
+ };
+
+ _p.run = function() {
+ this.setStatus_(robohornet.Status.RUNNING);
+ this.currentIndex_ = -1;
+ this.score_ = 0;
+ this.rawScore_ = 0;
+ this.progressElement_.style.opacity = '0.1';
+ this.statusElement_.textContent = 'Please wait while the benchmark runs. For best results, close all other programs and pages while the test is running.';
+ window.setTimeout(bind(this.next_, this), 1000);
+ };
+
+ _p.next_ = function() {
+ var benchmark;
+ while (!benchmark) {
+ benchmark = this.benchmarks_[++this.currentIndex_];
+ if (!benchmark)
+ break;
+ if (!benchmark.enabled) {
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.SKIPPED);
+ benchmark = null;
+ }
+ }
+
+ var progressAmount = (this.currentIndex_ / this.benchmarks_.length) * 100;
+ this.progressElement_.style.marginLeft = '-' + (100 - progressAmount).toString() + '%';
+
+ this.activeBenchmark_ = benchmark;
+ if (benchmark) {
+ this.loadBenchmark_(benchmark);
+ } else {
+ this.done_();
+ }
+ };
+
+ _p.done_ = function() {
+ this.progressElement_.addEventListener(TRANSITION_END_EVENT, this.progressCallback_, false);
+ this.progressElement_.style.opacity = '0.0';
+ var skippedExtendedRuns = 0;
+
+ var successfulRuns = 0, failedRuns = 0, blockedRuns = 0;
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ if (benchmark.status == robohornet.enabledBenchmarks.SUCCESS)
+ successfulRuns++;
+ else if (benchmark.extended)
+ skippedExtendedRuns++;
+ else if (benchmark.status == robohornet.enabledBenchmarks.POPUP_BLOCKED)
+ blockedRuns++;
+ else if (benchmark.status != robohornet.enabledBenchmarks.SKIPPED)
+ failedRuns++;
+ }
+
+ if (successfulRuns + skippedExtendedRuns == this.benchmarks_.length) {
+ this.setScore_(true /* opt_finalScore */);
+ this.statusElement_.innerHTML = 'The RoboHornet index is normalized to 100 and roughly shows your browser\'s performance compared to other modern browsers on reference hardware. <a href="https://github.com/robohornet/robohornet/wiki/Benchmark-Scoring" target="_blank">Learn more</a>';
+ } else if (blockedRuns) {
+ this.statusElement_.textContent = "Your browser's popup blocker prevented some of the benchmarks from running. Disable your popup blocker and run the test again to see the index.";
+ } else if (failedRuns) {
+ this.statusElement_.textContent = failedRuns + ' out of ' + this.benchmarks_.length + ' benchmark(s) failed.';
+ } else {
+ this.statusElement_.textContent = 'Ran ' + successfulRuns + ' out of ' + this.benchmarks_.length + ' benchmarks. Enable all benchmarks to compute the index.';
+ }
+ this.setStatus_(robohornet.Status.READY);
+ };
+
+ _p.progressTransitionDone_ = function() {
+ // Wait until the progress bar fades out to put it back to the left.
+ this.progressElement_.style.marginLeft = '-100%';
+ this.progressElement_.removeEventListener(TRANSITION_END_EVENT, this.progressCallback_, false);
+ };
+
+ _p.benchmarkLoaded = function() {
+ var benchmark = this.activeBenchmark_;
+ if (!benchmark)
+ return;
+
+ var self = this;
+ var suite = new Benchmark.Suite(this.name, {
+ onComplete: function() { self.onBenchmarkComplete_(this, benchmark); },
+ onAbort: function() { self.onBenchmarkAbort_(this, benchmark); }
+ });
+
+ var callFunction = function(win, fn, arg, deferred) {
+ win[fn] && win[fn].call(win, arg);
+ if (fn == 'setUp' && win['resetMathRandom'])
+ win['resetMathRandom']();
+ if (deferred)
+ deferred.resolve();
+ };
+
+ var callTest = function(win, arg, deferred) {
+ if (win['testAsync']) {
+ win['testAsync'].call(win, deferred, arg);
+ }
+ else if (win['test']) {
+ win['test'].call(win, arg);
+ if (deferred)
+ deferred.resolve();
+ }
+ else
+ this.abort();
+ };
+
+ var win = this.benchmarkWindow_;
+ for (var run, i = 0; run = benchmark.runs[i]; i++) {
+ var arg = run[1];
+ suite.add(run[0], {
+ defer: true,
+ fn: bind(callTest, suite, win, arg),
+ setup: bind(callFunction, suite, win, 'setUp', arg),
+ teardown: bind(callFunction, suite, win, 'tearDown', arg),
+ reset: bind(callFunction, suite, win, "reset", arg)
+ });
+ }
+
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.RUNNING);
+ suite.run(true);
+ };
+
+ _p.initTags_ = function(technologyTags, productTags) {
+ this.tags_ = [];
+ this.tagsById_ = {};
+
+ var f = function(tags, type) {
+ for (var id in tags) {
+ var tag = tags[id];
+ tag.id = id;
+ tag.type = type;
+ tag.benchmarks = [];
+ this.tags_.push(tag);
+ this.tagsById_[id] = tag;
+ }
+ };
+
+ var specialTags = {
+ CORE: { name: 'Core' },
+ EXTENDED: { name: 'Extended' },
+ NONE: { name: 'None' }
+ }
+
+ f.call(this, technologyTags, robohornet.TagType.TECHNOLOGY);
+ f.call(this, productTags, robohornet.TagType.APP);
+ f.call(this, specialTags, robohornet.TagType.SPECIAL);
+ };
+
+ _p.initBenchmarks_ = function(benchmarks) {
+ var totalWeight = 0;
+ var benchmark;
+
+ for (var details, i = 0; details = benchmarks[i]; i++) {
+ totalWeight += details.weight;
+ }
+
+ this.benchmarks_ = [];
+ this.benchmarksById_ = {};
+ for (var details, i = 0; details = benchmarks[i]; i++) {
+ benchmark = new robohornet.Benchmark(details);
+ benchmark.index = i;
+ benchmark.computedWeight = (benchmark.weight / totalWeight) * 100;
+
+ for (var tagId, j = 0; tagId = details.tags[j]; j++) {
+ addToTag(benchmark, this.tagsById_[tagId]);
+ }
+ if (!benchmark.extended)
+ addToTag(benchmark, this.tagsById_['CORE']);
+ addToTag(benchmark, this.tagsById_['EXTENDED']);
+
+ this.benchmarks_.push(benchmark);
+ this.benchmarksById_[benchmark.id] = benchmark;
+
+ if (benchmark.extended)
+ this.hasExtendedBenchmark_ = true;
+ }
+ };
+
+ _p.initBenchmarkUi_ = function() {
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ this.registerBenchmark_(benchmark);
+ }
+ var finalRow = document.createElement("tr");
+ finalRow.className = "summary-row";
+ var cell = document.createElement("td");
+ cell.colSpan = 5;
+ cell.innerHTML = "<em>Raw score</em>";
+ finalRow.appendChild(cell);
+ cell = document.createElement("td");
+ cell.className = "number";
+ cell.textContent = "-";
+ finalRow.appendChild(cell);
+ this.rawScoreElement_ = cell;
+ this.testsContainer.tBodies[0].appendChild(finalRow);
+ };
+
+ _p.loadBenchmark_ = function(benchmark) {
+ if (this.benchmarkWindow_) {
+ this.benchmarkWindow_.close();
+ this.benchmarkWindow_ = null
+ }
+
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.LOADING);
+ this.activeBenchmark_ = benchmark;
+
+ // delay each execution to yield for UI
+ window.setTimeout(bind(function() {
+
+ // clear old test iframes
+ _p.clearTestIframes_();
+
+ // create fresh test environment iframe
+ var iframe = document.createElement('iframe');
+ document.getElementById('iframe-wrap').appendChild(iframe);
+ iframe.src = benchmark.filename + '?use_test_runner';
+ this.benchmarkWindow_ = iframe.contentWindow;
+
+ if (this.benchmarkWindow_) {
+ this.benchmarkWindow_.onload = function(){
+ if (this.benchmarkWindow_.innerHeight <= 0)
+ this.onPopupBlock_();
+ };
+ } else {
+ this.onPopupBlock_();
+ }
+ }, this), 25);
+ };
+
+ _p.clearTestIframes_ = function() {
+ var iframes = document.querySelectorAll('iframe');
+ [].forEach.call(iframes, function(elem){
+ elem.parentNode.removeChild(elem);
+ });
+ };
+
+
+ _p.onPopupBlock_ = function() {
+ // Reclaim window's name so we can use it again
+ this.benchmarkWindow_ && this.benchmarkWindow_.close();
+
+ this.setBenchmarkStatus_(this.activeBenchmark_, robohornet.enabledBenchmarks.POPUP_BLOCKED);
+ this.activeBenchmark_ = null;
+ window.setTimeout(bind(this.next_, this), 25);
+ };
+
+ _p.onBenchmarkAbort_ = function(suite, benchmark) {
+ if (benchmark.status == robohornet.enabledBenchmarks.ABORTED)
+ return;
+
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.ABORTED);
+ if (this.benchmarkWindow_)
+ this.benchmarkWindow_.close();
+ this.benchmarkWindow_ = null;
+ window.setTimeout(bind(this.next_, this), 250);
+ };
+
+ _p.onBenchmarkComplete_ = function(suite, benchmark) {
+ if (!this.benchmarkWindow_) {
+ this.onBenchmarkAbort_(suite, benchmark);
+ return;
+ }
+
+ _p.clearTestIframes_();
+
+ this.benchmarkWindow_ = null;
+
+ var results = [];
+ for (var run, i = 0; run = suite[i]; i++) {
+ results.push({
+ name: run.name,
+ mean: run.stats.mean * 1000,
+ rme: run.stats.rme,
+ runs: run.stats.sample.length
+ });
+ }
+ benchmark.results = results;
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.SUCCESS);
+ this.showBenchmarkResults_(benchmark);
+ window.setTimeout(bind(this.next_, this), 25);
+ };
+
+ _p.onBenchmarkToggle_ = function(benchmark, event) {
+ this.setBenchmarkEnabled_(benchmark, benchmark.toggleElement_.checked);
+ this.updateHash_();
+ }
+
+ _p.setBenchmarkEnabled_ = function(benchmark, enabled) {
+ benchmark.toggleElement_.checked = enabled;
+ benchmark.enabled = enabled;
+ enableClass(benchmark.detailsElement_, 'disabled', !benchmark.enabled);
+ enableClass(benchmark.summaryRow_, 'disabled', !benchmark.enabled);
+ }
+
+ _p.registerBenchmark_ = function(benchmark) {
+ var identifier = 'benchmark-' + benchmark.index;
+
+ // Append summary row.
+ var row = document.createElement('tr');
+ row.id = identifier;
+
+ var cell = document.createElement('td');
+ var checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = identifier + '-toggle';
+ checkbox.checked = true;
+ checkbox.addEventListener('click', bind(this.onBenchmarkToggle_, this, benchmark), false);
+ cell.appendChild(checkbox);
+ benchmark.toggleElement_ = checkbox;
+
+ var label = document.createElement('span');
+ label.appendChild(document.createTextNode(benchmark.name));
+ label.addEventListener('click', bind(this.toggleBenchmarkDetails_, this, benchmark), false);
+ cell.appendChild(label);
+
+ row.appendChild(cell);
+
+ addChildren(row, [
+ createElement('td', '-'),
+ createElement('td', '-', 'number'),
+ createElement('td', benchmark.baselineTime.toFixed(2) + 'ms', 'number'),
+ createElement('td', benchmark.computedWeight.toFixed(2) + '%', 'number'),
+ createElement('td', '-', 'number')
+ ]);
+ this.testsContainer.tBodies[0].appendChild(row);
+ benchmark.summaryRow_ = row;
+
+ // Append details row.
+ row = document.createElement('tr');
+ cell = document.createElement('td');
+ cell.className = 'details';
+ cell.colSpan = 7;
+
+ var detailsElement = document.createElement('div');
+ detailsElement.className = '';
+ cell.appendChild(detailsElement);
+ detailsElement.appendChild(document.createTextNode(benchmark.description));
+ detailsElement.appendChild(document.createElement("br"));
+
+ var issueLink = document.createElement("a");
+ issueLink.href = "http://github.com/robohornet/robohornet/issues/" + benchmark.issueNumber;
+ issueLink.target = "_blank";
+ issueLink.appendChild(document.createTextNode("View issue details on GitHub"));
+ detailsElement.appendChild(issueLink);
+ detailsElement.appendChild(document.createElement("br"));
+
+ if (benchmark.extended)
+ detailsElement.appendChild(this.makeTagElement_(this.tagsById_['EXTENDED']));
+
+ for (var tag, i = 0; tag = benchmark.tags[i]; i++) {
+ if (tag.type != robohornet.TagType.SPECIAL)
+ detailsElement.appendChild(this.makeTagElement_(tag));
+ }
+
+ // Append list of runs/parameters.
+ var runsTable = document.createElement('table');
+ runsTable.id = identifier + '-runs';
+ runsTable.className = 'runs';
+ runsTable.appendChild(document.createElement('thead'));
+
+ var headerRow = document.createElement('tr');
+ addChildren(headerRow, [
+ createElement('td', 'Parameters'),
+ createElement('td', 'Runs', 'number'),
+ createElement('td', 'Error', 'number'),
+ createElement('td', 'Mean', 'number')
+ ]);
+ runsTable.tHead.appendChild(headerRow);
+
+ runsTable.appendChild(document.createElement('tbody'));
+ for (i = 0; i < benchmark.runs.length; i++) {
+ var runsRow = document.createElement('tr');
+ addChildren(runsRow, [
+ createElement('td', benchmark.runs[i][0], 'name'),
+ createElement('td', '0', 'number'),
+ createElement('td', '0', 'number'),
+ createElement('td', '0', 'number')
+ ]);
+ runsTable.tBodies[0].appendChild(runsRow);
+ }
+ detailsElement.appendChild(runsTable);
+ var linkElement = document.createElement('a');
+ linkElement.target = '_new';
+ linkElement.href = benchmark.filename;
+ linkElement.appendChild(document.createTextNode('Open test in new window'));
+ detailsElement.appendChild(linkElement);
+ benchmark.detailsElement_ = detailsElement;
+
+ row.appendChild(cell);
+ this.testsContainer.tBodies[0].appendChild(row);
+ row.className = 'details';
+ };
+
+ _p.showBenchmarkResults_ = function(benchmark) {
+ var results = benchmark.results;
+
+ var row = benchmark.summaryRow_;
+ row.cells[1].textContent = 'Computing Index...';
+
+ var accumulatedMean = 0;
+ var runsTable = document.getElementById(row.id + '-runs');
+ for (var result, i = 0; result = results[i]; i++) {
+ var runCells = runsTable.tBodies[0].rows[i].cells;
+ runCells[1].textContent = result.runs;
+ runCells[2].textContent = String.fromCharCode(177) +
+ result.rme.toFixed(2) + '%';
+ runCells[3].textContent = result.mean.toFixed(2) + 'ms';
+ accumulatedMean += result.mean;
+ }
+
+ var diff = accumulatedMean - benchmark.baselineTime;
+ var score = benchmark.baselineTime * benchmark.computedWeight / accumulatedMean;
+ this.score_ += score;
+ var rawScore = accumulatedMean * benchmark.computedWeight;
+ this.rawScore_ += rawScore;
+
+ this.setScore_();
+
+ row.cells[1].textContent = 'Completed';
+ row.cells[2].textContent = accumulatedMean.toFixed(2) + 'ms';
+ row.cells[5].textContent = score.toFixed(2);
+ //PTS
+ console.log(row.cells[0].textContent + ':' + row.cells[2].textContent);
+ };
+
+ _p.setBenchmarkStatus_ = function(benchmark, status) {
+ benchmark.status = status;
+ var caption = robohornet.enabledBenchmarksDescription[benchmark.status] ||
+ 'Unknown failure';
+ benchmark.summaryRow_.cells[1].textContent = caption;
+ };
+
+ _p.setStatus_ = function(status) {
+ this.status_ = status;
+
+ var isRunning = status == robohornet.Status.RUNNING;
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ benchmark.toggleElement_.disabled = isRunning;
+ if (isRunning)
+ this.setBenchmarkStatus_(benchmark, robohornet.enabledBenchmarks.PENDING);
+ }
+
+ document.body.className = this.status_ == robohornet.Status.READY ? 'ready' : 'running';
+ this.runElement_.textContent = robohornet.RunButtonCaption[status];
+ this.runElement_.disabled = this.status_ != robohornet.Status.READY;
+ };
+
+ _p.setScore_ = function(opt_finalScore) {
+ // Ensure that we have 4 digits in front of the dot and 2 after.
+ this.indexElement_.textContent = formatNumber(this.score_, 4, 2);
+ this.indexElement_.className = opt_finalScore ? 'final' : '';
+ this.rawScoreElement_.textContent = this.rawScore_.toFixed(2);
+
+ // PTS, print the result
+ if (opt_finalScore) {
+ console.log('RoboHornet Score:' + this.score_);
+ }
+ if (this.rawScoreElement_.classList.contains('final') != opt_finalScore)
+ this.rawScoreElement_.classList.toggle('final');
+ }
+
+ _p.toggleBenchmarkDetails_ = function(benchmark, e) {
+ var rowEle = benchmark.detailsElement_.parentElement.parentElement;
+ rowEle.classList.toggle("expanded");
+ benchmark.summaryRow_.classList.toggle("expanded");
+ };
+
+ _p.makeTagElement_ = function(tag) {
+ var tagElement = createElement('span', tag.name, 'tag ' + tag.type);
+ var callback = function(tag, event) {
+ if (event.shiftKey) {
+ this.addBenchmarksToSelectionByTag(tag);
+ } else {
+ this.selectBenchmarksByTag(tag);
+ }
+ // Undo the text selection from a shift-click.
+ window.getSelection().removeAllRanges();
+ };
+ tagElement.addEventListener('click', bind(callback, this, tag), false);
+ return tagElement;
+ }
+
+ _p.selectBenchmarksByTag = function(tag) {
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ this.setBenchmarkEnabled_(benchmark, false);
+ }
+ this.addBenchmarksToSelectionByTag(tag);
+ }
+
+ _p.addBenchmarksToSelectionByTag = function(tag) {
+ for (var benchmark, i = 0; benchmark = tag.benchmarks[i]; i++) {
+ this.setBenchmarkEnabled_(benchmark, true);
+ }
+ this.updateHash_();
+ }
+
+ _p.updateTagSelection_ = function() {
+ // Get number of enabled benchmarks per tag.
+ var enabledBenchmarksByTag = [];
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ if (!benchmark.enabled)
+ continue;
+ for (var tag, j = 0; tag = benchmark.tags[j]; j++) {
+ enabledBenchmarksByTag[tag.id] = (enabledBenchmarksByTag[tag.id] || 0) + 1;
+ }
+ }
+
+ // Highlight tags based on selection.
+ for (var identifier in this.tagsById_) {
+ var tag = this.tagsById_[identifier];
+ var n = enabledBenchmarksByTag[identifier] || 0;
+ enableClass(tag.element, 'partially-inactive', n && n != tag.benchmarks.length);
+ enableClass(tag.element, 'inactive', n == 0);
+ }
+ };
+
+ _p.updateHash_ = function() {
+ // We'll keep track of how many benchmarks each of the tag has enabled.
+ var enabledTagCount = {};
+ var enabledBenchmarkIDs = [];
+ var disabledBenchmarkIDs = [];
+ for (var benchmark, i = 0; benchmark = this.benchmarks_[i]; i++) {
+ if (benchmark.enabled) {
+ enabledBenchmarkIDs.push(benchmark.id);
+ for (var tag, k = 0; tag = benchmark.tags[k]; k++) {
+ enabledTagCount[tag.id] = (enabledTagCount[tag.id] || 0) + 1;
+ }
+ }
+ else {
+ disabledBenchmarkIDs.push(benchmark.id);
+ }
+ }
+
+ if (enabledBenchmarkIDs.length == 0) {
+ window.location.hash = '#et=none';
+ this.updateTagSelection_();
+ return;
+ }
+
+ var maxTagId = 'NONE'
+ var maxTagCount = 0;
+
+ // See which of the tags has the most coverage.
+ for (var tagId in enabledTagCount) {
+ if (enabledTagCount[tagId] < this.tagsById_[tagId].benchmarks.length) {
+ continue;
+ }
+ if (enabledTagCount[tagId] > maxTagCount) {
+ maxTagCount = enabledTagCount[tagId];
+ maxTagId = tagId;
+ }
+ }
+
+ // Check if that maxTagId has coverage of all enabled benchmarks.
+ if (maxTagCount == enabledBenchmarkIDs.length) {
+ if (maxTagId == 'CORE') {
+ // All is the default.
+ window.location.hash = '';
+ } else {
+ window.location.hash = '#et=' + maxTagId.toLowerCase();
+ }
+ } else {
+ // Fall back on covering the benchmarks one by one.
+ // We want to encode as few IDs as possible in the hash.
+ // This also gives us a good default to follow for new benchmarks.
+ if (disabledBenchmarkIDs.length) {
+ if (disabledBenchmarkIDs.length < enabledBenchmarkIDs.length) {
+ window.location.hash = '#d=' + disabledBenchmarkIDs.join(',');
+ } else {
+ window.location.hash = '#e=' + enabledBenchmarkIDs.join(',');
+ }
+ } else {
+ window.location.hash = '';
+ }
+ }
+
+ this.updateTagSelection_();
+ }
+
+ _p.digestHash_ = function() {
+ var hash = window.location.hash;
+
+ if (!hash) {
+ //The core set should be selected by default.
+ this.selectBenchmarksByTag(this.tagsById_['CORE']);
+ return;
+ }
+ hash = hash.replace('#', '').toLowerCase().split('&');
+ var enableBenchmarks;
+ var benchmark;
+ var segment;
+
+ //First, checkx if "enabled-tags" is in, because we do special processing if it is.
+ for (segment, i = 0; segment = hash[i]; i++) {
+ hash[i] = hash[i].split('=');
+ if (hash[i][0] == "et") {
+ var tag = this.tagsById_[hash[i][1].toUpperCase()];
+ if (!tag) continue;
+ this.selectBenchmarksByTag(tag);
+ return;
+ }
+ }
+
+ // There wasn't a single enabled tag. Let's see if there are any individual enabled/disabled benchmarks.
+ for (var segment, i = 0; segment = hash[i]; i++) {
+ enableBenchmarks = false;
+ switch (hash[i][0]) {
+ case 'e':
+ enableBenchmarks = true;
+ // We set all benchmarks to disable and then only enable some.
+ for (var k = 0; benchmark = this.benchmarks_[k]; k++) {
+ this.setBenchmarkEnabled_(benchmark, false, true);
+ }
+ case 'd':
+ var ids = hash[i][1].split(',');
+ for (var benchmarkID, j = 0; benchmarkID = ids[j]; j++) {
+ benchmark = this.benchmarksById_[benchmarkID];
+ if (!benchmark)
+ continue;
+ this.setBenchmarkEnabled_(benchmark, enableBenchmarks, true);
+ }
+ break;
+ }
+ }
+
+ this.updateTagSelection_();
+ }
+
+ _p.onWindowUnload_ = function() {
+ if (this.benchmarkWindow_)
+ this.benchmarkWindow_.close();
+ }
+
+})();
+
+
+/**
+ * Class representing a single benchmark.
+ *
+ * @param {Object} details Benchmarks details as json object.
+ * @constructor
+ */
+robohornet.Benchmark = function(details) {
+ if (!details)
+ details = {};
+ this.name = details.name;
+ this.description = details.description;
+ this.filename = details.filename;
+ this.runs = details.runs;
+ this.weight = details.weight;
+ this.baselineTime = details.baselineTime;
+ this.tags = details.tags;
+ this.issueNumber = details.issueNumber;
+ this.extended = details.extended;
+ this.enabled = true;
+ this.tags = [];
+
+ this.id = this.filename.match(/\/([A-z]+)\./)[1].toLowerCase();
+};
+
+function bind(fn, opt_scope, var_args) {
+ var scope = opt_scope || window;
+ var len = arguments.length;
+ var args = [];
+ for (var i = 2; i < len; i++) {
+ args.push(arguments[i]);
+ }
+ return function(arguments) {
+ var a = args.slice();
+ a.push.call(a, arguments);
+ fn.apply(scope, a);
+ };
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.png b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.png
new file mode 100644
index 0000000..f989ee2
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.rb b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.rb
new file mode 100644
index 0000000..54a2fc7
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/robohornet.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+require 'rubygems'
+require 'sinatra'
+
+set :root, File.dirname(__FILE__)
+set :public_folder, Proc.new { root }
+
+get '/' do
+ redirect '/robohornet.html'
+end
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/test.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/test.js
new file mode 100644
index 0000000..801bdc8
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/test.js
@@ -0,0 +1,57 @@
+var requestAnimationFrameFunction =
+ window.requestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+
+if (window.parent && window.parent.__robohornet__ && window.location.search == '?use_test_runner') {
+ document.body.appendChild(document.createTextNode('Running test using test runner...'));
+ window.parent.__robohornet__.benchmarkLoaded();
+} else {
+
+ document.body.appendChild(document.createTextNode('Running test standalone...'));
+
+ resetMathRandom();
+ setUp();
+ window.setTimeout(function() {
+ if (window['testAsync']) {
+ var startTime = new Date().getTime();
+ var deferred = {
+ startTime: 0,
+ resolve: function() {
+ var endTime = new Date().getTime();
+ document.body.appendChild(document.createTextNode('Ran test in ' + (endTime - startTime) + ' ms.'));
+ }
+ };
+ window['testAsync'](deferred);
+ } else {
+ var startTime = new Date().getTime();
+ window['test']();
+ var endTime = new Date().getTime();
+ document.body.appendChild(document.createTextNode('Ran test in ' + (endTime - startTime) + ' ms.'));
+ }
+ }, 0);
+}
+
+// To make the benchmark results predictable, we replace Math.random with a
+// 100% deterministic alternative.
+function resetMathRandom() {
+ Math.random = (function() {
+ var seed = 49734321;
+ return function() {
+ // Robert Jenkins' 32 bit integer hash function.
+ seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
+ seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
+ seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
+ seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
+ seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
+ seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
+ return (seed & 0xfffffff) / 0x10000000;
+ };
+ })();
+}
+
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addcol.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addcol.html
new file mode 100644
index 0000000..c11fff4
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addcol.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Adding columns to table</title>
+<style>
+ td { border: 1px; margin: 5px; }
+</style>
+</head>
+<body>
+<script>
+var DEFAULT_ROW_COUNT = 1000;
+var tableElement;
+
+function setUp(opt_rowCount) {
+ var rowCount = opt_rowCount || DEFAULT_ROW_COUNT;
+ var tableBody = document.createElement('tbody');
+ for (var i = 0; i < rowCount; i++) {
+ var tableRow = document.createElement('tr');
+ tableBody.appendChild(tableRow);
+ var tableCell = document.createElement('td');
+ tableCell.appendChild(document.createTextNode(i));
+ tableRow.appendChild(tableCell);
+ }
+
+ tableElement = document.createElement('table');
+ tableElement.appendChild(tableBody);
+ document.body.appendChild(tableElement);
+}
+
+function tearDown() {
+ tableElement.parentNode.removeChild(tableElement);
+ tableElement = null;
+}
+
+function reset() {
+ if (!tableElement) return;
+ for (var i = 0; i < tableElement.rows.length; i++) {
+ var tableRow = tableElement.rows[i];
+ if (tableRow.lastChild != tableRow.firstChild) {
+ tableRow.removeChild(tableRow.lastChild);
+ }
+ }
+}
+
+function test(opt_rowCount) {
+ var rowCount = opt_rowCount || DEFAULT_ROW_COUNT;
+ for (var i = 0; i < tableElement.rows.length; i++) {
+ var tableRow = tableElement.rows[i];
+ var tableCell = document.createElement('td');
+ tableCell.appendChild(document.createTextNode(i));
+ tableRow.appendChild(tableCell);
+ }
+ var tableRow = document.createElement('tr');
+ tableElement.tBodies[0].appendChild(tableRow);
+ var tableCell = document.createElement('td');
+ tableCell.appendChild(document.createTextNode(tableElement.offsetHeight));
+ tableRow.appendChild(tableCell);
+ }
+
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addrow.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addrow.html
new file mode 100644
index 0000000..27cb356
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/addrow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Adding rows to table</title>
+</head>
+<body>
+<script>
+var DEFAULT_ROW_COUNT = 1000;
+var tableElement;
+
+function setUp() {
+ var tableBody = document.createElement('tbody');
+ tableElement = document.createElement('table');
+ tableElement.appendChild(tableBody);
+ document.body.appendChild(tableElement);
+}
+
+function tearDown() {
+ tableElement.parentNode.removeChild(tableElement);
+ tableElement = null;
+}
+
+function test(opt_rowCount) {
+ var rowCount = opt_rowCount || DEFAULT_ROW_COUNT;
+ var tableBody = tableElement.tBodies[0];
+ for (var i = 0; i < rowCount; i++) {
+ var tableRow = document.createElement('tr');
+ tableBody.appendChild(tableRow);
+ var tableCell = document.createElement('td');
+ tableCell.appendChild(document.createTextNode(i));
+ tableRow.appendChild(tableCell);
+ }
+
+ var tableRow = document.createElement('tr');
+ tableElement.tBodies[0].appendChild(tableRow);
+
+ var tableCell = document.createElement('td');
+ tableCell.appendChild(document.createTextNode(tableElement.offsetHeight));
+ tableRow.appendChild(tableCell);
+ }
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/animated_gifs.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/animated_gifs.html
new file mode 100644
index 0000000..c8bcf06
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/animated_gifs.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Animated gifs.</title>
+
+<!--
+ GIF being used is in public domain.
+ See: http://commons.wikimedia.org/wiki/File:Throbber_allbackgrounds_circledots_32.gif
+-->
+
+<style>
+#container { height: 300px; width: 300px; overflow: auto; }
+</style>
+</head>
+<body>
+<div id="container"></div>
+<script>
+var img_src = 'data:image/gif;base64,' +
+ 'R0lGODlhIAAgALMJAAAAAHt7e4SEhJSUlKWlpa2trb29vcbGxtbW1gAAAAAAAAAAAAAAAAAAAAAAAAAA' +
+ 'ACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAJACwAAAAAIAAgAAAEsDDJSam4ourNE8ZdqH2faHrkdVZD' +
+ 'O0ipmgR0ILoums413eE4HajX4wBzQgmxZjy+NkvfxvnURG3NYGd5Q4aKG4KYsNIgzojJeFyeoNGJ9br9' +
+ 'fsvZ5Tr6LqbrEXxkeX9xcm0Semp4hwlvHAWQBSYHlAcikZEhlZUdmJgcm5uPnpCgoZSjpBIGrAYSp6gb' +
+ 'pKWtrQmwlqmZtbW3oZeZCby2vrFtw6yMGsiuyhXDzhu9JxEAACH5BAUKAAkALAAAAAAgACAAAASwMMlJ' +
+ '6bij6s0Txl2ofZ9oeuR1VkRLSKmaCLQgui6azjXd4TgdqNfjAHNCCbFmPL42S9/G+dREbc1gZ3lDhoqb' +
+ 'griw0gTOgcl4XJ6g0Yn1uv1+y9nlOvoupusDfGR5f3FybRJ6aniHCW8cBpAGJgiUCCKRkSGVlR2YmByb' +
+ 'm4+ekKChlKOkEgesBxKnqBukpa2tCbCWqZm1tbehl5kJvLa+sW3DrIwayK7KFcPOG70nEQAAIfkEBQoA' +
+ 'CQAsAAAAACAAIAAABLAwyUkpuaTqzRPGXah9n2h65HVWRVtIqZoM9CC6LprONd3hOB2o1+MAc0IJsWY8' +
+ 'vjZL38b51ERtzWBneUOGihuD2LDSCM6CyXhcnqDRifW6/X7L2eU6+i6m6wV8ZHl/cXJtEnpqeIcJbxwH' +
+ 'kAcmAZQBIpGRIZWVHZiYHJubj56QoKGUo6QSCKwIEqeoG6Slra0JsJapmbW1t6GXmQm8tr6xbcOsjBrI' +
+ 'rsoVw84bvScRAAAh+QQFCgAJACwAAAAAIAAgAAAEsDDJSWm5perNE8ZdqH2faHrkdVZGa0ipmhA0Ibou' +
+ 'ms413eE4HajX4wBzQgmxZjy+NkvfxvnURG3NYGd5Q4aKm4P4sNIMzoPJeFyeoNGJ9br9fsvZ5Tr6Lqbr' +
+ 'B3xkeX9xcm0Semp4hwlvHAiQCCYClAIikZEhlZUdmJgcm5uPnpCgoZSjpBIBrAESp6gbpKWtrQmwlqmZ' +
+ 'tbW3oZeZCby2vrFtw6yMGsiuyhXDzhu9JxEAACH5BAUKAAkALAAAAAAgACAAAASwMMlJqbmm6s0Txl2o' +
+ 'fZ9oeuR1Vkd7SKmaFHQhui6azjXd4TgdqNfjAHNCCbFmPL42S9/G+dREbc1gZ3lDhoobhBix0hDOhMl4' +
+ 'XJ6g0Yn1uv1+y9nlOvoupusJfGR5f3FybRJ6aniHCW8cAZABJgOUAyKRkSGVlR2YmBybm4+ekKChlKOk' +
+ 'EgKsAhKnqBukpa2tCbCWqZm1tbehl5kJvLa+sW3DrIwayK7KFcPOG70nEQAAIfkEBQoACQAsAAAAACAA' +
+ 'IAAABLAwyUnpuafqzRPGXah9n2h65HVWSItIqZoYtCG6LprONd3hOB2o1+MAc0IJsWY8vjZL38b51ERt' +
+ 'zWBneUOGipuAOLDSFM6FyXhcnqDRifW6/X7L2eU6+i6m6wt8ZHl/cXJtEnpqeIcJbxwCkAImBJQEIpGR' +
+ 'IZWVHZiYHJubj56QoKGUo6QSA6wDEqeoG6Slra0JsJapmbW1t6GXmQm8tr6xbcOsjBrIrsoVw84bvScR' +
+ 'AAAh+QQFCgAJACwAAAAAIAAgAAAEsDDJSSm6qOrNE8ZdqH2faHrkdVZBG0ipmhz0Iboums413eE4HajX' +
+ '4wBzQgmxZjy+NkvfxvnURG3NYGd5Q4aKG4FYsNIYzobJeFyeoNGJ9br9fsvZ5Tr6LqbrDXxkeX9xcm0S' +
+ 'emp4hwlvHAOQAyYFlAUikZEhlZUdmJgcm5uPnpCgoZSjpBIErAQSp6gbpKWtrQmwlqmZtbW3oZeZCby2' +
+ 'vrFtw6yMGsiuyhXDzhu9JxEAACH5BAUKAAkALAAAAAAgACAAAASwMMlJabih6s0Txl2ofZ9oeuR1VkIr' +
+ 'SKmaIDQiui6azjXd4TgdqNfjAHNCCbFmPL42S9/G+dREbc1gZ3lDhoqbgXiw0hzOh8l4XJ6g0Yn1uv1+' +
+ 'y9nlOvoupusPfGR5f3FybRJ6aniHCW8cBJAEJgaUBiKRkSGVlR2YmBybm4+ekKChlKOkEgWsBRKnqBuk' +
+ 'pa2tCbCWqZm1tbehl5kJvLa+sW3DrIwayK7KFcPOG70nEQAAOw==';
+
+var container;
+var num;
+var DEFAULT_LINES = 50;
+var DEFAULT_COLUMNS = 10;
+
+function setUp(opt_size) {
+ var size = opt_size || [DEFAULT_LINES, DEFAULT_COLUMNS];
+ var lines = size[0];
+ var columns = size[1];
+ container = document.getElementById('container');
+ container.appendChild(document.createElement('div'));
+ var subContainer = container.firstChild;
+ for (var i = 0; i < lines; i++) {
+ for (var j = 0; j < columns; j++) {
+ var image = document.createElement('img');
+ image.src = img_src;
+ subContainer.appendChild(image);
+ }
+ subContainer.appendChild(document.createElement('br'));
+ }
+}
+
+function reset() {
+ if (!container) return;
+ container.innerHTML = '';
+ container.scrollTop = 0;
+}
+
+function tearDown() {
+ container.innerHTML = '';
+ container.scrollTop = 0;
+}
+
+function test() {
+ while (container.scrollTop < (container.firstChild.offsetHeight - container.offsetHeight)) {
+ container.scrollTop += 1000;
+ }
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasclear.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasclear.html
new file mode 100644
index 0000000..294c734
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasclear.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Canvas clearing benchmark</title>
+<script>
+
+var canvas = null, context = null, size = 0;
+var DEFAULT_SIZE = 512;
+
+function setUp(canvasSize) {
+ var container = null, canvasStyle = null;
+
+ // ensure we have a canvas size
+ size = canvasSize || DEFAULT_SIZE;
+
+ // set the container
+ container = document.getElementById('container');
+ container.style.width = container.style.height = size + 'px';
+
+ // create a canvas
+ canvas = document.createElement('canvas');
+ context = canvas.getContext('2d');
+ context.fillStyle = "#000000";
+ canvasStyle = canvas.style;
+
+ // set its width and height using CSS
+ canvasStyle.position = 'absolute';
+ canvasStyle.left = '0px';
+ canvasStyle.top = '0px';
+
+ // and then set the backing store
+ canvas.width = canvas.height = size;
+ canvasStyle.width = canvasStyle.height = size + 'px';
+ container.appendChild(canvas);
+}
+
+function tearDown() {
+ var container = document.getElementById('container');
+ while (container.firstChild) { container.removeChild(container.lastChild); }
+}
+
+function test() {
+ context.fillRect(0, 0, 1, 1);
+ context.clearRect(0, 0, size, size);
+}
+
+</script>
+</head>
+<body>
+<div id="container" style="position: absolute; width: 256px; height: 256px; overflow:hidden; border: 1px solid black;"></div>
+</body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasdrawline.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasdrawline.html
new file mode 100644
index 0000000..0d4c04d
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvasdrawline.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Canvas painting benchmark</title>
+<script>
+
+var canvas, vertices;
+var DEFAULT_NUMBER_OF_VERTICES = 2500;
+
+function setUp(opt_numberOfVertices) {
+ var numberOfVertices = opt_numberOfVertices || DEFAULT_NUMBER_OF_VERTICES;
+ var container = document.getElementById('container');
+ container.style.overflow = 'hidden';
+
+ vertices = makeRandomPath(512, 512, numberOfVertices);
+
+ canvas = document.createElement('canvas');
+ var canvasStyle = canvas.style;
+ canvasStyle.position = 'absolute';
+ canvasStyle.left = '0px';
+ canvasStyle.top = '0px';
+ canvas.width = canvas.height = 512;
+ canvasStyle.width = canvasStyle.height = '512px';
+
+ container.appendChild(canvas);
+}
+
+function tearDown() {
+ var container = document.getElementById('container');
+ while (container.firstChild) { container.removeChild(container.lastChild); }
+}
+
+function test(opt_numberOfVertices) {
+ var numberOfVertices = opt_numberOfVertices || DEFAULT_NUMBER_OF_VERTICES;
+ var path = vertices;
+ var context = canvas.getContext('2d');
+
+ context.lineCap = context.lineJoin = 'round';
+ context.strokeStyle = 'rgba(255, 170, 85, 0.2)';
+ context.lineWidth = 1;
+
+ var x = path[0];
+ var y = path[1]
+ for (var i = 2, I = path.length; i < I;) {
+ context.moveTo(x, y);
+ x = path[i];
+ y = path[i + 1];
+ context.lineTo(x, y);
+ context.stroke();
+ i += 2;
+ }
+}
+
+function makeRandomPath(width, height, numVerts) {
+ var path = [];
+ for (var i = 0; i < numVerts; ++i) {
+ path.push(Math.round(Math.random() * width),
+ Math.round(Math.random() * height));
+ }
+ return path;
+}
+</script>
+</head>
+<body>
+<div id="container" style="position: absolute; width:512px; height: 512px; overflow:hidden; border: 1px solid black;"></div>
+</body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvastodataurl.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvastodataurl.html
new file mode 100644
index 0000000..b381bc0
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/canvastodataurl.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Canvas painting benchmark</title>
+<script>
+
+var canvas;
+var DEFAULT_NUMBER_OF_VERTICES = 1000;
+var DEFAULT_HEIGHT = 512;
+
+function setUp(opt_sizes) {
+ var opt_sizes = opt_sizes || [];
+ var numberOfVertices = opt_sizes[0] || DEFAULT_NUMBER_OF_VERTICES;
+ var height = opt_sizes[1] || DEFAULT_HEIGHT;
+
+ var container = document.getElementById('container');
+ container.style.width = container.style.height = height;
+ container.width = container.height = height;
+ //container.style.overflow = 'hidden';
+
+ canvas = document.createElement('canvas');
+ var canvasStyle = canvas.style;
+ canvasStyle.position = 'absolute';
+ canvasStyle.left = '0px';
+ canvasStyle.top = '0px';
+ canvas.width = canvas.height = height;
+ canvasStyle.width = canvasStyle.height = height + 'px';
+ container.appendChild(canvas);
+
+ draw(height, height, numberOfVertices);
+}
+
+function tearDown() {
+ var container = document.getElementById('container');
+ while (container.firstChild) { container.removeChild(container.lastChild); }
+}
+
+function test(opt_numberOfVertices) {
+ canvas.toDataURL("image/png");
+}
+
+function draw(width, height, numVerts) {
+ var path = [];
+ for (var i = 0; i < numVerts; ++i) {
+ path.push(Math.round(Math.random() * width),
+ Math.round(Math.random() * height));
+ }
+
+ var context = canvas.getContext('2d');
+ context.lineCap = context.lineJoin = 'round';
+
+ context.moveTo(path[0], path[1]);
+ for (var i = 2, I = path.length; i < I; ) {
+ context.lineTo(path[i++], path[i++]);
+ }
+ context.strokeStyle = 'rgba(255, 170, 85, 0.2)';
+ context.lineWidth = 1;
+ context.stroke();
+}
+</script>
+</head>
+<body>
+<div id="container" style="position: absolute; overflow:hidden; border: 1px solid black;"></div>
+</body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/createtable.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/createtable.html
new file mode 100644
index 0000000..012b474
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/createtable.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Adding a large table</title>
+<style>
+ td { border: 1px; margin: 5px; overflow:hidden; }
+</style>
+</head>
+<body>
+<div id="table-container"></div>
+<script>
+
+var tableContainer;
+var tableString;
+var DEFAULT_SIZE = [200, 100];
+
+function setUp(opt_size) {
+ var size = opt_size || DEFAULT_SIZE;
+ var rowCount = size[0];
+ var colCount = size[1];
+
+ tableContainer = document.getElementById('table-container');
+ var htmlBuffer = ['<table><tbody>'];
+ htmlBuffer.push('<tr>');
+ for (var col = 0; col < colCount; col++) {
+ // Build table headers
+ htmlBuffer.push('<th style="width:200px;">H</th>');
+ }
+ htmlBuffer.push('</tr>');
+ for (var i = 0; i < rowCount; i++) {
+ htmlBuffer.push('<tr>');
+ for (var j = 0; j < colCount; j++) {
+ htmlBuffer.push('<td>');
+ htmlBuffer.push(i + ', ' + j);
+ htmlBuffer.push('</td>');
+ }
+ htmlBuffer.push('</tr>');
+ }
+ htmlBuffer.push('</tbody></table>');
+ tableString = htmlBuffer.join('');
+}
+
+function reset() {
+ if (!tableContainer) return;
+ tableContainer.innerHTML = '';
+}
+
+function tearDown() {
+ tableContainer.innerHTML = '';
+ tableContainer = null;
+ tableString = null;
+}
+
+function test() {
+ tableContainer.innerHTML = tableString;
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/descendantselector.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/descendantselector.html
new file mode 100644
index 0000000..a927d91
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/descendantselector.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Descendant selectors</title>
+<style>
+div { padding: 1px; }
+.test div { padding: 2px; }
+.test2 div { padding: 3px; }
+.test div div div { background: orange; }
+.test div div div div { background: white; }
+</style>
+</head>
+<body>
+<script>
+
+var rootElement;
+var totalHeight;
+var DEFAULT_DEPTH = 1000;
+
+function setUp(opt_depth) {
+ var depth = opt_depth || DEFAULT_DEPTH;
+ rootElement = document.createElement('div');
+ var parentElement = rootElement;
+ for (var i = 0; i < depth; i++) {
+ var element = document.createElement('div');
+ element.appendChild(document.createTextNode(i));
+ parentElement.appendChild(element);
+ parentElement = element;
+ }
+ document.body.appendChild(rootElement);
+ totalHeight = 0
+}
+
+function tearDown() {
+ rootElement.parentNode.removeChild(rootElement);
+ rootElement = null;
+}
+
+function test(opt_depth) {
+ var depth = opt_depth || DEFAULT_DEPTH;
+ rootElement.className = rootElement.className == 'test' ? 'test2' : 'test';
+ totalHeight += rootElement.offsetHeight;
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_read.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_read.html
new file mode 100644
index 0000000..2cfe328
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_read.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Reading from localStorage</title>
+<style>
+</style>
+</head>
+<body>
+<script>
+
+var DEFAULT_ITEMS = 100;
+var keySet = {};
+
+function setUp(opt_items) {
+ var items = opt_items || DEFAULT_ITEMS;
+ for (var i = 0; i < items; i++) {
+ var key = 'key' + i;
+ keySet[i] = key;
+ localStorage.setItem(key, i);
+ }
+}
+
+function reset() {
+ keySet = {};
+ localStorage.clear();
+}
+
+function tearDown() {
+ localStorage.clear();
+}
+
+function test(opt_items) {
+ var items = opt_items || DEFAULT_ITEMS;
+ for (var i = 0; i < items; i++) {
+ localStorage.getItem(keySet[i]);
+ }
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_write.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_write.html
new file mode 100644
index 0000000..f7a7f88
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/localstorage_write.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Writing to localStorage</title>
+<style>
+</style>
+</head>
+<body>
+<script>
+
+var DEFAULT_ITEMS = 100;
+var keySet = {};
+
+function setUp(opt_items) {
+ var items = opt_items || DEFAULT_ITEMS;
+ for (var i = 0; i < items; i++) {
+ keySet[i] = 'key' + i;
+ }
+}
+
+function reset() {
+ keySet = {};
+ localStorage.clear();
+}
+
+function tearDown() {
+ keySet = {};
+ localStorage.clear();
+}
+
+function test(opt_items) {
+ var items = opt_items || DEFAULT_ITEMS;
+ for (var i = 0; i < items; i++) {
+ localStorage.setItem(keySet[i], i);
+ }
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/object_scope.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/object_scope.html
new file mode 100644
index 0000000..53159f7
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/object_scope.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Object Scope Access Test</title>
+<script>
+
+// This test is pretty slow in Chrome, a lot faster in Opera and even faster in Firefox.
+function ClassWithThisEverywhere(n) {
+ this.a = 1, this.sum = 0, this.count = n || 10000000;
+ this.unnamed = function() {
+ for (var i = 0; i < this.count; i++) this.sum += this.a;
+ }
+ this.named = function calcWithThisEverywhere() {
+ for (var i = 0; i < this.count; i++) this.sum += this.a;
+ }
+}
+
+// This test runs relatively fast in Chrome and Firefox. This is the only test with a difference between the
+// unnamed and named versions. The named version is horribly slow in Opera.
+function ClassWithoutThis(n) {
+ var a = 1, sum = 0, count = n || 10000000;
+ this.unnamed = function() {
+ for (var i = 0; i < count; i++) sum += a;
+ }
+ this.named = function calcWithoutThis() {
+ for (var i = 0; i < count; i++) sum += a;
+ }
+}
+
+// This is the fastest of the three tests in all browsers.
+function ClassLocal(n) {
+ this.unnamed = function() {
+ var a = 1, sum = 0, count = n || 10000000;
+ for (var i = 0; i < count; i++) sum += a;
+ }
+ this.named = function calcLocal() {
+ var a = 1, sum = 0, count = n || 10000000;
+ for (var i = 0; i < count; i++) sum += a;
+ }
+}
+
+// Benchmark stuff and a bit eye candy...
+function setUp(n) {
+ objectWithThisEverywhere = new ClassWithThisEverywhere(n);
+ objectWithoutThis = new ClassWithoutThis(n);
+ objectLocal = new ClassLocal(n);
+}
+
+function prettyTest(id, t) {
+ var t1 = new Date().getTime();
+ if (!this.times) {
+ this.times = [];
+ }
+
+ if (typeof id !== 'undefined') {
+ this.times[id] = t1 - t;
+ }
+ var min = -1, max = -1;
+ for (var i = this.times.length; i--; ) {
+ min = min < 0 ? this.times[i] : Math.min(min, this.times[i]);
+ max = Math.max(max, this.times[i]);
+ }
+ for (var i = this.times.length; i--; ) {
+ var e = document.getElementById('id' + i);
+ if (this.times.length < 6) {
+ e.firstChild.data = this.times[i] ? this.times[i] + ' ms' : '?';
+ } else {
+ e.firstChild.data = this.times[i] ? Math.round(this.times[i] * 10 / min) * 10 + ' percent of the time' : '?';
+ }
+
+ if (this.times[i] > min * 6) {
+ e.className = 'bad';
+ } else if (this.times[i] < min * 2) {
+ e.className = 'top';
+ } else {
+ e.className = '';
+ }
+ }
+ return new Date().getTime();
+}
+
+function test() {
+ var t = prettyTest();
+ objectWithThisEverywhere.unnamed();
+ t = prettyTest(0, t);
+ objectWithThisEverywhere.named();
+ t = prettyTest(1, t);
+ objectWithoutThis.unnamed();
+ t = prettyTest(2, t);
+ objectWithoutThis.named();
+ t = prettyTest(3, t);
+ objectLocal.unnamed();
+ t = prettyTest(4, t);
+ objectLocal.named();
+ prettyTest(5, t);
+}
+</script>
+
+<style>
+ body { font-family: sans-serif; line-height: 1.4em; }
+ sup { color: #666; font-weight: bold; line-height: 0; }
+ var { background: #DDD; border-radius: .2em; font-style: normal; font-weight: bold; padding: 0 .5em; }
+ .top { background: #6D0; }
+ .bad { background: #D60; color: #FFF; }
+</style>
+</head>
+<body>
+ <ul>
+ <li><var id="id0">?</var> for accessing class variables with <code>this</code> added everywhere<sup>[w]</sup></li>
+ <li><var id="id1">?</var> for accessing class variables with <code>this</code> added everywhere (using a named function)<sup>[w]</sup></li>
+ <li><var id="id2">?</var> for accessing class variables without using <code>this</code><sup>[m]</sup></li>
+ <li><var id="id3">?</var> for accessing class variables without using <code>this</code> (using a named function)<sup>[m]</sup><sup>[o]</sup></li>
+ <li><var id="id4">?</var> for accessing local variables</li>
+ <li><var id="id5">?</var> for accessing local variables (using a named function)</li>
+ </ul>
+ <p>
+ <sup>[m]</sup> Mozilla Firefox loses here (about 4 times slower than the fastest method).<br>
+ <sup>[o]</sup> Triggers a bug in Opera (100 times slower than the fastest method).<br>
+ <sup>[w]</sup> WebKit loses here.<br>
+ </p>
+</body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/offsetreflow.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/offsetreflow.html
new file mode 100644
index 0000000..1e16dbc
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/offsetreflow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Force layout with offsetHeight</title>
+<style>
+ #container { width: 200px; height: 800px; overflow-y: scroll; }
+</style>
+</head>
+<body>
+<div id="container">
+</div>
+<script>
+
+var container;
+var DEFAULT_RUNS = 10000;
+
+function setUp(opt_numResizes) {
+ container = document.getElementById('container');
+}
+
+function reset() {
+ if (!container) return;
+ container.style.width = 200 + 'px';
+ container.style.height = 800 + 'px';
+}
+
+function test(opt_numResizes) {
+ var runs = opt_numResizes || DEFAULT_RUNS;
+ for (var i = 0; i < runs; i++) {
+ container.style.width = Math.random() * 500 + 'px';
+ container.style.height = Math.random() * 500 + 'px';
+ container.offsetHeight;
+ }
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/property_accessors.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/property_accessors.html
new file mode 100644
index 0000000..365c308
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/property_accessors.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>ES5 Property Accessors</title>
+<script>
+
+"use strict"
+var TIMES = 1e5;
+var OBJ;
+
+function setUp() {
+ OBJ = {
+ prop: 1,
+ get bar() {
+ return this.prop;
+ },
+ set bar(value) {
+ this.prop = value;
+ }
+ };
+}
+
+function reset() {
+ if (!OBJ) return;
+ OBJ.prop = 1;
+}
+
+function test_get() {
+ for (var i = 0; i < TIMES; i++) {
+ var x = OBJ.bar + 1;
+ }
+}
+
+function test_set() {
+ for (var i = 0; i < TIMES; i++) {
+ OBJ.bar = 42;
+ }
+}
+
+function test_combined() {
+ for (var i = 0; i < TIMES; i++) {
+ OBJ.bar = OBJ.bar + 1;
+ }
+}
+
+function test(opt_mode) {
+ switch(opt_mode) {
+ case 'GET':
+ test_get();
+ break;
+ case 'SET':
+ test_set();
+ break;
+ default:
+ test_combined();
+ break;
+ }
+}
+
+</script>
+</head>
+<body></body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/range.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/range.html
new file mode 100644
index 0000000..4c64e02
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/range.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>DOM Range API</title>
+<script src="../third_party/rangy/rangy-core.js"></script>
+<script>
+
+var ITERATIONS = 100;
+var MIN_NODES = 50;
+var BODY_HTML = generateHtml(MIN_NODES, true /** opt_withIds */);
+var REPLACEMENT_HTML = generateHtml(MIN_NODES);
+
+function reset() { document.body.innerHTML = BODY_HTML; }
+function setUp() { reset(); }
+
+function test(opt_nodes) {
+ // Replace a section of the DOM using the Range API.
+ // Inspired by metamorph.js.
+ var nodes = opt_nodes || MIN_NODES;
+ var start = document.getElementById('node' + Math.floor(MIN_NODES * 0.1));
+ var end = document.getElementById('node' + Math.floor(MIN_NODES * 0.9));
+ var r = rangy.createRange(); // Use Rangy as a workaround for IE <= 9
+ for (var i = 0; i < ITERATIONS; i++) {
+ r.setStartAfter(start);
+ r.setEndBefore(end);
+ r.deleteContents();
+ r.insertNode(r.createContextualFragment(REPLACEMENT_HTML));
+ }
+}
+
+function generateHtml(nodes, opt_withIds) {
+ var lorem =
+ 'Cupcake ipsum dolor sit amet icing carrot cake gummi ' +
+ 'bears jelly. Sugar plum sesame snaps candy canes.';
+ var s = '';
+ for (var i = 0; i < nodes; i++) {
+ s += '<p id=node' + (opt_withIds ? i : '') + '>' + lorem + '</p>';
+ }
+ return s;
+}
+
+</script>
+</head>
+<body></body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/resizecol.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/resizecol.html
new file mode 100644
index 0000000..fc5ecba
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/resizecol.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Resizing columns in a table</title>
+<style>
+ td { border: 1px; margin: 5px; }
+</style>
+</head>
+<body>
+<script>
+
+var container;
+var DEFAULT_SIZE = [500, 100];
+
+function setUp(opt_size) {
+ var size = opt_size || DEFAULT_SIZE;
+ var rowCount = size[0];
+ var colCount = size[1];
+
+ var buff = [];
+ buff.push('<table><tbody>');
+ buff.push('<tr>');
+ for (var col = 0; col < colCount; col++) {
+ // Build table headers
+ buff.push('<th style="width:200px;">H</th>');
+ }
+ buff.push('</tr>');
+ for (var i = 0; i < rowCount; i++) {
+ buff.push('<tr>');
+ for (var j = 0; j < colCount; j++) {
+ buff.push('<td>');
+ buff.push(i + ', ' + j);
+ buff.push('</td>');
+ }
+ buff.push('</tr>');
+ }
+ buff.push('</tbody></table>');
+
+ container = document.createElement('div');
+ container.innerHTML = buff.join('');
+ document.body.appendChild(container);
+}
+
+function tearDown() {
+ container.parentNode.removeChild(container);
+ container = null;
+}
+
+function reset() {
+ if (!container) return;
+ var tbody = container.firstChild.firstChild;
+ var firstRow = tbody.firstChild;
+ for (var i = 0; i < size[1] /** colCount */; i++) {
+ firstRow.cells[i].style.width = '200px';
+ var forceLayout = tbody.offsetTop;
+ }
+}
+
+function test(opt_size) {
+ var size = opt_size || DEFAULT_SIZE;
+ var tbody = container.firstChild.firstChild;
+ var firstRow = tbody.firstChild;
+ for (var i = 0; i < size[1] /** colCount */; i++) {
+ firstRow.cells[i].style.width = '50px';
+ var forceLayout = tbody.offsetTop;
+ }
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/svgresize.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/svgresize.html
new file mode 100644
index 0000000..d855223
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/svgresize.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Resize an SVG</title>
+<style>
+ #container { width: 200px; height: 800px; overflow-y: scroll; }
+ .slide { width: 180px; height: 180px; }
+ td { border: 1px; margin: 5px; overflow:hidden; }
+</style>
+</head>
+<body>
+<div id="container">
+ <svg version="1.1" fill-rule="evenodd" fill="none" stroke="none"
+ stroke-linecap="square" stroke-miterlimit="10" overflow="hidden" preserveAspectRatio="none"
+ width="100%" height="100%" shape-rendering="optimize-speed">
+ <g id="content"></g>
+ </svg>
+</div>
+<svg>
+ <g class="slide">
+ <g class="content"></g>
+ <g class="info"></g>
+ </g>
+</svg>
+<script>
+
+var COLORS = [
+ '#fa8',
+ '#f8a',
+ '#af8',
+ '#a8f',
+ '#8af',
+ '#8fa'
+];
+var NUM_ELEMENTS = 500;
+var DEFAULT_NUM_SVGS = 500;
+
+var container;
+var contentContainer;
+var template;
+
+function setUp(opt_numSvgs) {
+ var numSvgs = opt_numSvgs || DEFAULT_NUM_SVGS;
+ container = document.getElementById('container');
+ contentContainer = document.getElementById('content');
+ template = document.getElementsByClassName('slide')[0];
+ for (var i = 0; i < numSvgs; i++) {
+ render(i);
+ }
+}
+
+function reset() {
+ if (!contentContainer) return;
+ contentContainer.innerHTML = '';
+}
+
+function tearDown() {
+ while(contentContainer.firstChild) {
+ contentContainer.firstChild.parentNode.removeChild(contentContainer.firstChild);
+ }
+ container = null;
+ contentContainer = null;
+ template = null;
+}
+
+function test(opt_numSvgs) {
+ var numSvgs = opt_numSvgs || DEFAULT_NUM_SVGS;
+ for (var i = 0; i < 10; i++) {
+ var size = Math.random() * 300 + 100;
+ container.style.width = size + 'px';
+ contentContainer.setAttribute('transform', 'scale(' + (size / 180) + ')');
+ }
+}
+
+// Utility functions below
+function render(index) {
+ var copy = template.cloneNode(true);
+ // The number being multipled by the index should match the height in the .slide class above.
+ copy.setAttribute('transform', 'translate(0,' + (180 * index) + ')');
+ contentContainer.appendChild(copy);
+ var newSvg = makeSvg(NUM_ELEMENTS, index);
+ var svgContainer = copy.querySelectorAll('.content')[0];
+ svgContainer.innerHTML = '';
+ svgContainer.appendChild(newSvg);
+}
+
+function makeSvg(numElements, index) {
+ var points = [[71, 0], [100, 0]];
+ var str = '<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">';
+ var elemCount = numElements;
+ while (elemCount > 0) {
+ var p = points.shift();
+ var q = points.shift();
+ var next = nextPoints(p, q);
+ str += createPath(COLORS[index & COLORS.length], p, q, next[2], next[0]);
+ points.push(next[0], next[1], next[1], next[2]);
+ elemCount--;
+ }
+ str += "</svg>"
+ var svgDoc = new DOMParser().parseFromString(str, 'text/xml');
+ return svgDoc.removeChild(svgDoc.firstChild);
+}
+
+function nextPoints(p, q) {
+ var d = [p[1] - q[1], q[0] - p[0]];
+ var pPrime = [p[0] + d[0], p[1] + d[1]];
+ var qPrime = [q[0] + d[0], q[1] + d[1]];
+ var t = [(pPrime[0] + qPrime[0] + d[0]) / 2, (pPrime[1] + qPrime[1] + d[1]) / 2];
+ return [pPrime, t, qPrime];
+}
+
+function createPath(color, a, b, c, d) {
+ var id = Math.random();
+ return '<path id="' + id + '" stroke="#000" stroke-width="1" stroke-opacity=".5" fill-opacity=".5" ' +
+ 'fill="' + color + '" d="' +
+ 'M' + a[0] + ' ' + a[1] +
+ 'L' + b[0] + ' ' + b[1] +
+ 'L' + c[0] + ' ' + c[1] +
+ 'L' + d[0] + ' ' + d[1] +
+ 'Z"></path>';
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/table_scrolltop.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/table_scrolltop.html
new file mode 100644
index 0000000..e41f4b8
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/table_scrolltop.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Scrolling a large table with scrollTop</title>
+<style>
+ td { border: 1px; margin: 5px; overflow:hidden; }
+ #container { height: 284px; width: 284px; overflow: auto; }
+ #main-table { table-layout: fixed; width: 0; }
+</style>
+</head>
+<body>
+<div id="container"></div>
+<script>
+
+var container;
+
+var DEFAULT_SIZE = [1000, 100];
+
+function setUp(opt_size) {
+ var size = opt_size || DEFAULT_SIZE;
+ var rowCount = size[0];
+ var colCount = size[1];
+
+ container = document.getElementById('container')
+ var htmlBuffer = ['<table id="main-table"><tbody>'];
+ htmlBuffer.push('<tr>');
+ for (var col = 0; col < colCount; col++) {
+ // Build table headers
+ htmlBuffer.push('<th style="width:200px;">H</th>');
+ }
+ htmlBuffer.push('</tr>');
+ for (var i = 0; i < rowCount; i++) {
+ htmlBuffer.push('<tr>');
+ for (var j = 0; j < colCount; j++) {
+ htmlBuffer.push('<td>');
+ htmlBuffer.push(i + ', ' + j);
+ htmlBuffer.push('</td>');
+ }
+ htmlBuffer.push('</tr>');
+ }
+ htmlBuffer.push('</tbody></table>');
+ container.innerHTML = htmlBuffer.join('');
+}
+
+function reset() {
+ if (!container) return;
+ container.innerHTML = '';
+ container.scrollTop = 0;
+}
+
+function tearDown() {
+ container.innerHTML = '';
+ container.scrollTop = 0;
+}
+
+function testAsync(deferred, opt_size) {
+ var size = opt_size || DEFAULT_SIZE;
+ var maxScroll = container.firstChild.offsetHeight - container.offsetHeight;
+ while (container.scrollTop < maxScroll) {
+ container.scrollTop = container.scrollTop + 1000;
+ }
+ requestAnimationFrameFunction(function() { deferred.resolve(); });
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/template.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/template.html
new file mode 100644
index 0000000..9c0d741
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/template.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test Title</title>
+<script>
+function setUp() {}
+function tearDown() {}
+function test() {}
+function reset() {}
+</script>
+</head>
+<body>
+</body>
+<script src="../test.js"></script>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/varargs.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/varargs.html
new file mode 100644
index 0000000..0dcd483
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/tests/varargs.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>arguments instantiation is slow</title>
+<style>
+</style>
+</head>
+<body>
+<script>
+
+var args;
+var runs;
+var DEFAULT_RUNS = 100000;
+
+function setUp(opt_runs) {
+ runs = opt_runs || DEFAULT_RUNS;
+ args = new Array(runs);
+}
+
+function reset() {
+ args = new Array(runs);
+}
+
+function tearDown() {
+ delete args;
+}
+
+function test() {
+ for (var i = 0; i < runs; i++) {
+ checkArgs(args[i], args[i], args[i]);
+ }
+}
+
+function checkArgs() {
+ return Array.prototype.slice.call(arguments);
+}
+</script>
+<script src="../test.js"></script>
+</body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/LICENSE.txt b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/LICENSE.txt
new file mode 100644
index 0000000..e33e63f
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright 2010-2012 Mathias Bynens <http://mathiasbynens.be/>
+Based on JSLitmus.js, copyright Robert Kieffer <http://broofa.com/>
+Modified by John-David Dalton <http://allyoucanleet.com/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/README.md
new file mode 100644
index 0000000..62a2d1a
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/README.md
@@ -0,0 +1,135 @@
+# Benchmark.js <sup>v1.0.0</sup>
+
+A [robust](http://calendar.perfplanet.com/2010/bulletproof-javascript-benchmarks/ "Bulletproof JavaScript benchmarks") benchmarking library that works on nearly all JavaScript platforms<sup><a name="fnref1" href="#fn1">1</a></sup>, supports high-resolution timers, and returns statistically significant results. As seen on [jsPerf](http://jsperf.com/).
+
+## Download
+
+ * [Development source](https://raw.github.com/bestiejs/benchmark.js/v1.0.0/benchmark.js)
+
+## Dive in
+
+We’ve got [API docs](http://benchmarkjs.com/docs) and [unit tests](http://benchmarkjs.com/tests).
+
+For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/benchmark.js/wiki/Roadmap).
+
+## Support
+
+Benchmark.js has been tested in at least Adobe AIR 3.1, Chrome 5-21, Firefox 1.5-13, IE 6-9, Opera 9.25-12.01, Safari 3-6, Node.js 0.8.6, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
+
+## Installation and usage
+
+In a browser or Adobe AIR:
+
+```html
+<script src="benchmark.js"></script>
+```
+
+Optionally, expose Java’s nanosecond timer by adding the `nano` applet to the `<body>`:
+
+```html
+<applet code="nano" archive="nano.jar"></applet>
+```
+
+Or enable Chrome’s microsecond timer by using the [command line switch](http://peter.sh/experiments/chromium-command-line-switches/#enable-benchmarking):
+
+ --enable-benchmarking
+
+Via [npm](http://npmjs.org/):
+
+```bash
+npm install benchmark
+```
+
+In [Node.js](http://nodejs.org/) and [RingoJS v0.8.0+](http://ringojs.org/):
+
+```js
+var Benchmark = require('benchmark');
+```
+
+Optionally, use the [microtime module](https://github.com/wadey/node-microtime) by Wade Simmons:
+
+```bash
+npm install microtime
+```
+
+In [RingoJS v0.7.0-](http://ringojs.org/):
+
+```js
+var Benchmark = require('benchmark').Benchmark;
+```
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+```js
+load('benchmark.js');
+```
+
+In an AMD loader like [RequireJS](http://requirejs.org/):
+
+```js
+require({
+ 'paths': {
+ 'benchmark': 'path/to/benchmark'
+ }
+},
+['benchmark'], function(Benchmark) {
+ console.log(Benchmark.version);
+});
+
+// or with platform.js
+// https://github.com/bestiejs/platform.js
+require({
+ 'paths': {
+ 'benchmark': 'path/to/benchmark',
+ 'platform': 'path/to/platform'
+ }
+},
+['benchmark', 'platform'], function(Benchmark, platform) {
+ Benchmark.platform = platform;
+ console.log(Benchmark.platform.name);
+});
+```
+
+Usage example:
+
+```js
+var suite = new Benchmark.Suite;
+
+// add tests
+suite.add('RegExp#test', function() {
+ /o/.test('Hello World!');
+})
+.add('String#indexOf', function() {
+ 'Hello World!'.indexOf('o') > -1;
+})
+// add listeners
+.on('cycle', function(event) {
+ console.log(String(event.target));
+})
+.on('complete', function() {
+ console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+})
+// run async
+.run({ 'async': true });
+
+// logs:
+// > RegExp#test x 4,161,532 +-0.99% (59 cycles)
+// > String#indexOf x 6,139,623 +-1.00% (131 cycles)
+// > Fastest is String#indexOf
+```
+
+## BestieJS
+
+Benchmark.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+
+## Authors
+
+* [Mathias Bynens](http://mathiasbynens.be/)
+ [](https://twitter.com/mathias "Follow @mathias on Twitter")
+* [John-David Dalton](http://allyoucanleet.com/)
+ [](https://twitter.com/jdalton "Follow @jdalton on Twitter")
+
+## Contributors
+
+* [Kit Cambridge](http://kitcambridge.github.com/)
+ [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter")
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/benchmark.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/benchmark.js
new file mode 100644
index 0000000..db2c995
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/benchmark.js
@@ -0,0 +1,3919 @@
+/*!
+ * Benchmark.js v1.0.0 <http://benchmarkjs.com/>
+ * Copyright 2010-2012 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, undefined) {
+ 'use strict';
+
+ /** Used to assign each benchmark an incrimented id */
+ var counter = 0;
+
+ /** Detect DOM document object */
+ var doc = isHostType(window, 'document') && document;
+
+ /** Detect free variable `define` */
+ var freeDefine = typeof define == 'function' &&
+ typeof define.amd == 'object' && define.amd && define;
+
+ /** Detect free variable `exports` */
+ var freeExports = typeof exports == 'object' && exports &&
+ (typeof global == 'object' && global && global == global.global && (window = global), exports);
+
+ /** Detect free variable `require` */
+ var freeRequire = typeof require == 'function' && require;
+
+ /** Used to crawl all properties regardless of enumerability */
+ var getAllKeys = Object.getOwnPropertyNames;
+
+ /** Used to get property descriptors */
+ var getDescriptor = Object.getOwnPropertyDescriptor;
+
+ /** Used in case an object doesn't have its own method */
+ var hasOwnProperty = {}.hasOwnProperty;
+
+ /** Used to check if an object is extensible */
+ var isExtensible = Object.isExtensible || function() { return true; };
+
+ /** Used to access Wade Simmons' Node microtime module */
+ var microtimeObject = req('microtime');
+
+ /** Used to access the browser's high resolution timer */
+ var perfObject = isHostType(window, 'performance') && performance;
+
+ /** Used to call the browser's high resolution timer */
+ var perfName = perfObject && (
+ perfObject.now && 'now' ||
+ perfObject.webkitNow && 'webkitNow'
+ );
+
+ /** Used to access Node's high resolution timer */
+ var processObject = isHostType(window, 'process') && process;
+
+ /** Used to check if an own property is enumerable */
+ var propertyIsEnumerable = {}.propertyIsEnumerable;
+
+ /** Used to set property descriptors */
+ var setDescriptor = Object.defineProperty;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Used to prevent a `removeChild` memory leak in IE < 9 */
+ var trash = doc && doc.createElement('div');
+
+ /** Used to integrity check compiled tests */
+ var uid = 'uid' + (+new Date);
+
+ /** Used to avoid infinite recursion when methods call each other */
+ var calledBy = {};
+
+ /** Used to avoid hz of Infinity */
+ var 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
+ */
+ var tTable = {
+ '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.16, '14': 2.145, '15': 2.131, '16': 2.12, '17': 2.11, '18': 2.101,
+ '19': 2.093, '20': 2.086, '21': 2.08, '22': 2.074, '23': 2.069, '24': 2.064,
+ '25': 2.06, '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,
+ 'infinity': 1.96
+ };
+
+ /**
+ * Critical Mann-Whitney U-values for 95% confidence
+ * http://www.saburchill.com/IBbiology/stats/003.html
+ */
+ var uTable = {
+ '5': [0, 1, 2],
+ '6': [1, 2, 3, 5],
+ '7': [1, 3, 5, 6, 8],
+ '8': [2, 4, 6, 8, 10, 13],
+ '9': [2, 4, 7, 10, 12, 15, 17],
+ '10': [3, 5, 8, 11, 14, 17, 20, 23],
+ '11': [3, 6, 9, 13, 16, 19, 23, 26, 30],
+ '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37],
+ '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45],
+ '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55],
+ '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64],
+ '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75],
+ '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87],
+ '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99],
+ '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113],
+ '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119, 127],
+ '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126, 134, 142],
+ '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 133, 141, 150, 158],
+ '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 140, 149, 157, 166, 175],
+ '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138, 147, 156, 165, 174, 183, 192],
+ '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145, 154, 163, 173, 182, 192, 201, 211],
+ '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151, 161, 171, 181, 191, 200, 210, 220, 230],
+ '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158, 168, 178, 188, 199, 209, 219, 230, 240, 250],
+ '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272],
+ '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294],
+ '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317]
+ };
+
+ /**
+ * An object used to flag environments/features.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ var support = {};
+
+ (function() {
+
+ /**
+ * Detect Adobe AIR.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.air = isClassOf(window.runtime, 'ScriptBridgingProxyObject');
+
+ /**
+ * Detect if `arguments` objects have the correct internal [[Class]] value.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.argumentsClass = isClassOf(arguments, 'Arguments');
+
+ /**
+ * Detect if in a browser environment.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.browser = doc && isHostType(window, 'navigator');
+
+ /**
+ * Detect if strings support accessing characters by index.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.charByIndex =
+ // IE 8 supports indexes on string literals but not string objects
+ ('x'[0] + Object('x')[0]) == 'xx';
+
+ /**
+ * Detect if strings have indexes as own properties.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.charByOwnIndex =
+ // Narwhal, Rhino, RingoJS, IE 8, and Opera < 10.52 support indexes on
+ // strings but don't detect them as own properties
+ support.charByIndex && hasKey('x', '0');
+
+ /**
+ * Detect if Java is enabled/exposed.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.java = isClassOf(window.java, 'JavaPackage');
+
+ /**
+ * Detect if the Timers API exists.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.timeout = isHostType(window, 'setTimeout') && isHostType(window, 'clearTimeout');
+
+ /**
+ * Detect if functions support decompilation.
+ *
+ * @name decompilation
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ try {
+ // Safari 2.x removes commas in object literals
+ // from Function#toString results
+ // http://webk.it/11609
+ // Firefox 3.6 and Opera 9.25 strip grouping
+ // parentheses from Function#toString results
+ // http://bugzil.la/559438
+ support.decompilation = Function(
+ 'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')'
+ )()(0).x === '1';
+ } catch(e) {
+ support.decompilation = false;
+ }
+
+ /**
+ * Detect ES5+ property descriptor API.
+ *
+ * @name descriptors
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ try {
+ var o = {};
+ support.descriptors = (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
+ } catch(e) {
+ support.descriptors = false;
+ }
+
+ /**
+ * Detect ES5+ Object.getOwnPropertyNames().
+ *
+ * @name getAllKeys
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ try {
+ support.getAllKeys = /\bvalueOf\b/.test(getAllKeys(Object.prototype));
+ } catch(e) {
+ support.getAllKeys = false;
+ }
+
+ /**
+ * Detect if own properties are iterated before inherited properties (all but IE < 9).
+ *
+ * @name iteratesOwnLast
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.iteratesOwnFirst = (function() {
+ var props = [];
+ function ctor() { this.x = 1; }
+ ctor.prototype = { 'y': 1 };
+ for (var prop in new ctor) { props.push(prop); }
+ return props[0] == 'x';
+ }());
+
+ /**
+ * Detect if a node's [[Class]] is resolvable (all but IE < 9)
+ * and that the JS engine errors when attempting to coerce an object to a
+ * string without a `toString` property value of `typeof` "function".
+ *
+ * @name nodeClass
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ try {
+ support.nodeClass = ({ 'toString': 0 } + '', toString.call(doc || 0) != '[object Object]');
+ } catch(e) {
+ support.nodeClass = true;
+ }
+ }());
+
+ /**
+ * Timer object used by `clock()` and `Deferred#resolve`.
+ *
+ * @private
+ * @type Object
+ */
+ var timer = {
+
+ /**
+ * The timer namespace object or constructor.
+ *
+ * @private
+ * @memberOf timer
+ * @type Function|Object
+ */
+ 'ns': Date,
+
+ /**
+ * Starts the deferred timer.
+ *
+ * @private
+ * @memberOf timer
+ * @param {Object} deferred The deferred instance.
+ */
+ 'start': null, // lazy defined in `clock()`
+
+ /**
+ * Stops the deferred timer.
+ *
+ * @private
+ * @memberOf timer
+ * @param {Object} deferred The deferred instance.
+ */
+ 'stop': null // lazy defined in `clock()`
+ };
+
+ /** Shortcut for inverse results */
+ var noArgumentsClass = !support.argumentsClass,
+ noCharByIndex = !support.charByIndex,
+ noCharByOwnIndex = !support.charByOwnIndex;
+
+ /** Math shortcuts */
+ var abs = Math.abs,
+ floor = Math.floor,
+ max = Math.max,
+ min = Math.min,
+ pow = Math.pow,
+ sqrt = Math.sqrt;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The Benchmark constructor.
+ *
+ * @constructor
+ * @param {String} name A name to identify the benchmark.
+ * @param {Function|String} fn The test to benchmark.
+ * @param {Object} [options={}] Options object.
+ * @example
+ *
+ * // basic usage (the `new` operator is optional)
+ * 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 name and options
+ * var bench = new Benchmark('foo', {
+ *
+ * // a flag to indicate the benchmark is deferred
+ * 'defer': true,
+ *
+ * // benchmark test function
+ * 'fn': function(deferred) {
+ * // call resolve() when the deferred test is finished
+ * deferred.resolve();
+ * }
+ * });
+ *
+ * // or options only
+ * var bench = new Benchmark({
+ *
+ * // benchmark name
+ * 'name': 'foo',
+ *
+ * // benchmark test as a string
+ * 'fn': '[1,2,3,4].sort()'
+ * });
+ *
+ * // 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) {
+ var me = this;
+
+ // allow instance creation without the `new` operator
+ if (me == null || me.constructor != Benchmark) {
+ return new Benchmark(name, fn, options);
+ }
+ // juggle arguments
+ if (isClassOf(name, 'Object')) {
+ // 1 argument (options)
+ options = name;
+ }
+ else if (isClassOf(name, 'Function')) {
+ // 2 arguments (fn, options)
+ options = fn;
+ fn = name;
+ }
+ else if (isClassOf(fn, 'Object')) {
+ // 2 arguments (name, options)
+ options = fn;
+ fn = null;
+ me.name = name;
+ }
+ else {
+ // 3 arguments (name, fn [, options])
+ me.name = name;
+ }
+ setOptions(me, options);
+ me.id || (me.id = ++counter);
+ me.fn == null && (me.fn = fn);
+ me.stats = deepClone(me.stats);
+ me.times = deepClone(me.times);
+ }
+
+ /**
+ * The Deferred constructor.
+ *
+ * @constructor
+ * @memberOf Benchmark
+ * @param {Object} clone The cloned benchmark instance.
+ */
+ function Deferred(clone) {
+ var me = this;
+ if (me == null || me.constructor != Deferred) {
+ return new Deferred(clone);
+ }
+ me.benchmark = clone;
+ clock(me);
+ }
+
+ /**
+ * The Event constructor.
+ *
+ * @constructor
+ * @memberOf Benchmark
+ * @param {String|Object} type The event type.
+ */
+ function Event(type) {
+ var me = this;
+ return (me == null || me.constructor != Event)
+ ? new Event(type)
+ : (type instanceof Event)
+ ? type
+ : extend(me, { 'timeStamp': +new Date }, typeof type == 'string' ? { 'type': type } : type);
+ }
+
+ /**
+ * The Suite constructor.
+ *
+ * @constructor
+ * @memberOf Benchmark
+ * @param {String} name A name to identify the suite.
+ * @param {Object} [options={}] Options object.
+ * @example
+ *
+ * // basic usage (the `new` operator is optional)
+ * 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) {
+ var me = this;
+
+ // allow instance creation without the `new` operator
+ if (me == null || me.constructor != Suite) {
+ return new Suite(name, options);
+ }
+ // juggle arguments
+ if (isClassOf(name, 'Object')) {
+ // 1 argument (options)
+ options = name;
+ } else {
+ // 2 arguments (name [, options])
+ me.name = name;
+ }
+ setOptions(me, options);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Note: Some array methods have been implemented in plain JavaScript to avoid
+ * bugs in IE, Opera, Rhino, and Mobile Safari.
+ *
+ * IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()`
+ * functions that fail to remove the last element, `object[0]`, of
+ * array-like-objects even though the `length` property is set to `0`.
+ * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
+ * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+ *
+ * In Opera < 9.50 and some older/beta Mobile Safari versions using `unshift()`
+ * generically to augment the `arguments` object will pave the value at index 0
+ * without incrimenting the other values's indexes.
+ * https://github.com/documentcloud/underscore/issues/9
+ *
+ * Rhino and environments it powers, like Narwhal and RingoJS, may have
+ * buggy Array `concat()`, `reverse()`, `shift()`, `slice()`, `splice()` and
+ * `unshift()` functions that make sparse arrays non-sparse by assigning the
+ * undefined indexes a value of undefined.
+ * https://github.com/mozilla/rhino/commit/702abfed3f8ca043b2636efd31c14ba7552603dd
+ */
+
+ /**
+ * Creates an array containing the elements of the host array followed by the
+ * elements of each argument in order.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Array} The new array.
+ */
+ function concat() {
+ var value,
+ j = -1,
+ length = arguments.length,
+ result = slice.call(this),
+ index = result.length;
+
+ while (++j < length) {
+ value = arguments[j];
+ if (isClassOf(value, 'Array')) {
+ for (var k = 0, l = value.length; k < l; k++, index++) {
+ if (k in value) {
+ result[index] = value[k];
+ }
+ }
+ } else {
+ result[index++] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Utility function used by `shift()`, `splice()`, and `unshift()`.
+ *
+ * @private
+ * @param {Number} start The index to start inserting elements.
+ * @param {Number} deleteCount The number of elements to delete from the insert point.
+ * @param {Array} elements The elements to insert.
+ * @returns {Array} An array of deleted elements.
+ */
+ function insert(start, deleteCount, elements) {
+ // `result` should have its length set to the `deleteCount`
+ // see https://bugs.ecmascript.org/show_bug.cgi?id=332
+ var deleteEnd = start + deleteCount,
+ elementCount = elements ? elements.length : 0,
+ index = start - 1,
+ length = start + elementCount,
+ object = this,
+ result = Array(deleteCount),
+ tail = slice.call(object, deleteEnd);
+
+ // delete elements from the array
+ while (++index < deleteEnd) {
+ if (index in object) {
+ result[index - start] = object[index];
+ delete object[index];
+ }
+ }
+ // insert elements
+ index = start - 1;
+ while (++index < length) {
+ object[index] = elements[index - start];
+ }
+ // append tail elements
+ start = index--;
+ length = max(0, (object.length >>> 0) - deleteCount + elementCount);
+ while (++index < length) {
+ if ((index - start) in tail) {
+ object[index] = tail[index - start];
+ } else if (index in object) {
+ delete object[index];
+ }
+ }
+ // delete excess elements
+ deleteCount = deleteCount > elementCount ? deleteCount - elementCount : 0;
+ while (deleteCount--) {
+ index = length + deleteCount;
+ if (index in object) {
+ delete object[index];
+ }
+ }
+ object.length = length;
+ return result;
+ }
+
+ /**
+ * Rearrange the host array's elements in reverse order.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Array} The reversed array.
+ */
+ function reverse() {
+ var upperIndex,
+ value,
+ index = -1,
+ object = Object(this),
+ length = object.length >>> 0,
+ middle = floor(length / 2);
+
+ if (length > 1) {
+ while (++index < middle) {
+ upperIndex = length - index - 1;
+ value = upperIndex in object ? object[upperIndex] : uid;
+ if (index in object) {
+ object[upperIndex] = object[index];
+ } else {
+ delete object[upperIndex];
+ }
+ if (value != uid) {
+ object[index] = value;
+ } else {
+ delete object[index];
+ }
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Removes the first element of the host array and returns it.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Mixed} The first element of the array.
+ */
+ function shift() {
+ return insert.call(this, 0, 1)[0];
+ }
+
+ /**
+ * Creates an array of the host array's elements from the start index up to,
+ * but not including, the end index.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {Number} start The starting index.
+ * @param {Number} end The end index.
+ * @returns {Array} The new array.
+ */
+ function slice(start, end) {
+ var index = -1,
+ object = Object(this),
+ length = object.length >>> 0,
+ result = [];
+
+ start = toInteger(start);
+ start = start < 0 ? max(length + start, 0) : min(start, length);
+ start--;
+ end = end == null ? length : toInteger(end);
+ end = end < 0 ? max(length + end, 0) : min(end, length);
+
+ while ((++index, ++start) < end) {
+ if (start in object) {
+ result[index] = object[start];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Allows removing a range of elements and/or inserting elements into the
+ * host array.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {Number} start The start index.
+ * @param {Number} deleteCount The number of elements to delete.
+ * @param {Mixed} [val1, val2, ...] values to insert at the `start` index.
+ * @returns {Array} An array of removed elements.
+ */
+ function splice(start, deleteCount) {
+ var object = Object(this),
+ length = object.length >>> 0;
+
+ start = toInteger(start);
+ start = start < 0 ? max(length + start, 0) : min(start, length);
+
+ // support the de-facto SpiderMonkey extension
+ // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice#Parameters
+ // https://bugs.ecmascript.org/show_bug.cgi?id=429
+ deleteCount = arguments.length == 1
+ ? length - start
+ : min(max(toInteger(deleteCount), 0), length - start);
+
+ return insert.call(object, start, deleteCount, slice.call(arguments, 2));
+ }
+
+ /**
+ * Converts the specified `value` to an integer.
+ *
+ * @private
+ * @param {Mixed} value The value to convert.
+ * @returns {Number} The resulting integer.
+ */
+ function toInteger(value) {
+ value = +value;
+ return value === 0 || !isFinite(value) ? value || 0 : value - (value % 1);
+ }
+
+ /**
+ * Appends arguments to the host array.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Number} The new length.
+ */
+ function unshift() {
+ var object = Object(this);
+ insert.call(object, 0, 0, arguments);
+ return object.length;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic `Function#bind` like method.
+ *
+ * @private
+ * @param {Function} fn The function to be bound to `thisArg`.
+ * @param {Mixed} thisArg The `this` binding for the given function.
+ * @returns {Function} The bound function.
+ */
+ function bind(fn, thisArg) {
+ return function() { fn.apply(thisArg, 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() {
+ // lazy define
+ createFunction = function(args, body) {
+ var result,
+ anchor = freeDefine ? define.amd : Benchmark,
+ prop = uid + 'createFunction';
+
+ runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');
+ result = anchor[prop];
+ delete anchor[prop];
+ return result;
+ };
+ // fix JaegerMonkey bug
+ // http://bugzil.la/639720
+ createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || noop)() == uid ? createFunction : Function;
+ return createFunction.apply(null, arguments);
+ }
+
+ /**
+ * Delay the execution of a function based on the benchmark's `delay` property.
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} fn The function to execute.
+ */
+ function delay(bench, fn) {
+ bench._timerId = setTimeout(fn, bench.delay * 1e3);
+ }
+
+ /**
+ * Destroys the given element.
+ *
+ * @private
+ * @param {Element} element The element to destroy.
+ */
+ function destroyElement(element) {
+ trash.appendChild(element);
+ trash.innerHTML = '';
+ }
+
+ /**
+ * Iterates over an object's properties, executing the `callback` for each.
+ * Callbacks may terminate the loop by explicitly returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function executed per own property.
+ * @param {Object} options The options object.
+ * @returns {Object} Returns the object iterated over.
+ */
+ function forProps() {
+ var forShadowed,
+ skipSeen,
+ forArgs = true,
+ shadowed = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf'];
+
+ (function(enumFlag, key) {
+ // must use a non-native constructor to catch the Safari 2 issue
+ function Klass() { this.valueOf = 0; };
+ Klass.prototype.valueOf = 0;
+ // check various for-in bugs
+ for (key in new Klass) {
+ enumFlag += key == 'valueOf' ? 1 : 0;
+ }
+ // check if `arguments` objects have non-enumerable indexes
+ for (key in arguments) {
+ key == '0' && (forArgs = false);
+ }
+ // Safari 2 iterates over shadowed properties twice
+ // http://replay.waybackmachine.org/20090428222941/http://tobielangel.com/2007/1/29/for-in-loop-broken-in-safari/
+ skipSeen = enumFlag == 2;
+ // IE < 9 incorrectly makes an object's properties non-enumerable if they have
+ // the same name as other non-enumerable properties in its prototype chain.
+ forShadowed = !enumFlag;
+ }(0));
+
+ // lazy define
+ forProps = function(object, callback, options) {
+ options || (options = {});
+
+ var result = object;
+ object = Object(object);
+
+ var ctor,
+ key,
+ keys,
+ skipCtor,
+ done = !result,
+ which = options.which,
+ allFlag = which == 'all',
+ index = -1,
+ iteratee = object,
+ length = object.length,
+ ownFlag = allFlag || which == 'own',
+ seen = {},
+ skipProto = isClassOf(object, 'Function'),
+ thisArg = options.bind;
+
+ if (thisArg !== undefined) {
+ callback = bind(callback, thisArg);
+ }
+ // iterate all properties
+ if (allFlag && support.getAllKeys) {
+ for (index = 0, keys = getAllKeys(object), length = keys.length; index < length; index++) {
+ key = keys[index];
+ if (callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ }
+ // else iterate only enumerable properties
+ else {
+ for (key in object) {
+ // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+ // (if the prototype or a property on the prototype has been set)
+ // incorrectly set a function's `prototype` property [[Enumerable]] value
+ // to `true`. Because of this we standardize on skipping the `prototype`
+ // property of functions regardless of their [[Enumerable]] value.
+ if ((done =
+ !(skipProto && key == 'prototype') &&
+ !(skipSeen && (hasKey(seen, key) || !(seen[key] = true))) &&
+ (!ownFlag || ownFlag && hasKey(object, key)) &&
+ callback(object[key], key, object) === false)) {
+ break;
+ }
+ }
+ // in IE < 9 strings don't support accessing characters by index
+ if (!done && (forArgs && isArguments(object) ||
+ ((noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String') &&
+ (iteratee = noCharByIndex ? object.split('') : object)))) {
+ while (++index < length) {
+ if ((done =
+ callback(iteratee[index], String(index), object) === false)) {
+ break;
+ }
+ }
+ }
+ if (!done && forShadowed) {
+ // Because IE < 9 can't set the `[[Enumerable]]` attribute of an existing
+ // property and the `constructor` property of a prototype defaults to
+ // non-enumerable, we manually skip the `constructor` property when we
+ // think we are iterating over a `prototype` object.
+ ctor = object.constructor;
+ skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ctor;
+ for (index = 0; index < 7; index++) {
+ key = shadowed[index];
+ if (!(skipCtor && key == 'constructor') &&
+ hasKey(object, key) &&
+ callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ };
+ return forProps.apply(null, arguments);
+ }
+
+ /**
+ * Gets the name of the first argument from a function's source.
+ *
+ * @private
+ * @param {Function} fn The function.
+ * @returns {String} The argument name.
+ */
+ function getFirstArgument(fn) {
+ return (!hasKey(fn, 'toString') &&
+ (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || '';
+ }
+
+ /**
+ * 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;
+ }) / sample.length || 0;
+ }
+
+ /**
+ * Gets the source code of a function.
+ *
+ * @private
+ * @param {Function} fn The function.
+ * @param {String} altSource A string used when a function's source code is unretrievable.
+ * @returns {String} The function's source code.
+ */
+ function getSource(fn, altSource) {
+ var result = altSource;
+ if (isStringable(fn)) {
+ result = String(fn);
+ } else if (support.decompilation) {
+ // escape the `{` for Firefox 1
+ result = (/^[^{]+\{([\s\S]*)}\s*$/.exec(fn) || 0)[1];
+ }
+ // trim string
+ result = (result || '').replace(/^\s+|\s+$/g, '');
+
+ // detect strings containing only the "use strict" directive
+ return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result)
+ ? ''
+ : result;
+ }
+
+ /**
+ * Checks if a value is an `arguments` object.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the value is an `arguments` object, else `false`.
+ */
+ function isArguments() {
+ // lazy define
+ isArguments = function(value) {
+ return toString.call(value) == '[object Arguments]';
+ };
+ if (noArgumentsClass) {
+ isArguments = function(value) {
+ return hasKey(value, 'callee') &&
+ !(propertyIsEnumerable && propertyIsEnumerable.call(value, 'callee'));
+ };
+ }
+ return isArguments(arguments[0]);
+ }
+
+ /**
+ * Checks if an object is of the specified class.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @param {String} name The name of the class.
+ * @returns {Boolean} Returns `true` if the value is of the specified class, else `false`.
+ */
+ function isClassOf(value, name) {
+ return value != null && toString.call(value) == '[object ' + 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.
+ *
+ * @private
+ * @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) {
+ var type = object != null ? typeof object[property] : 'number';
+ return !/^(?:boolean|number|string|undefined)$/.test(type) &&
+ (type == 'object' ? !!object[property] : true);
+ }
+
+ /**
+ * Checks if a given `value` is an object created by the `Object` constructor
+ * assuming objects created by the `Object` constructor have no inherited
+ * enumerable properties and that there are no `Object.prototype` extensions.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, else `false`.
+ */
+ function isPlainObject(value) {
+ // avoid non-objects and false positives for `arguments` objects in IE < 9
+ var result = false;
+ if (!(value && typeof value == 'object') || isArguments(value)) {
+ return result;
+ }
+ // IE < 9 presents DOM nodes as `Object` objects except they have `toString`
+ // methods that are `typeof` "string" and still can coerce nodes to strings.
+ // Also check that the constructor is `Object` (i.e. `Object instanceof Object`)
+ var ctor = value.constructor;
+ if ((support.nodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) &&
+ (!isClassOf(ctor, 'Function') || ctor instanceof ctor)) {
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ if (support.iteratesOwnFirst) {
+ forProps(value, function(subValue, subKey) {
+ result = subKey;
+ });
+ return result === false || hasKey(value, result);
+ }
+ // IE < 9 iterates inherited properties before own properties. If the first
+ // iterated property is an object's own property then there are no inherited
+ // enumerable properties.
+ forProps(value, function(subValue, subKey) {
+ result = !hasKey(value, subKey);
+ return false;
+ });
+ return result === false;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a value can be safely coerced to a string.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the value can be coerced, else `false`.
+ */
+ function isStringable(value) {
+ return hasKey(value, 'toString') || isClassOf(value, 'String');
+ }
+
+ /**
+ * 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() {
+ var args = [this];
+ args.push.apply(args, arguments);
+ return fn.apply(null, args);
+ };
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @private
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * A wrapper around require() to suppress `module missing` errors.
+ *
+ * @private
+ * @param {String} id The module id.
+ * @returns {Mixed} The exported module or `null`.
+ */
+ function req(id) {
+ try {
+ var result = freeExports && freeRequire(id);
+ } catch(e) { }
+ return result || null;
+ }
+
+ /**
+ * Runs a snippet of JavaScript via script injection.
+ *
+ * @private
+ * @param {String} code The code to run.
+ */
+ function runScript(code) {
+ var anchor = freeDefine ? define.amd : Benchmark,
+ script = doc.createElement('script'),
+ sibling = doc.getElementsByTagName('script')[0],
+ parent = sibling.parentNode,
+ prop = uid + 'runScript',
+ prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';
+
+ // Firefox 2.0.0.2 cannot use script injection as intended because it executes
+ // asynchronously, but that's OK because script injection is only used to avoid
+ // the previously commented JaegerMonkey bug.
+ try {
+ // remove the inserted script *before* running the code to avoid differences
+ // in the expected script element count/order of the document.
+ script.appendChild(doc.createTextNode(prefix + code));
+ anchor[prop] = function() { destroyElement(script); };
+ } catch(e) {
+ parent = parent.cloneNode(false);
+ sibling = null;
+ script.text = code;
+ }
+ parent.insertBefore(script, sibling);
+ delete anchor[prop];
+ }
+
+ /**
+ * A helper function for setting options/event handlers.
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} [options={}] Options object.
+ */
+ function setOptions(bench, options) {
+ options = extend({}, bench.constructor.options, options);
+ bench.options = forOwn(options, function(value, key) {
+ if (value != null) {
+ // add event listeners
+ if (/^on[A-Z]/.test(key)) {
+ forEach(key.split(' '), function(key) {
+ bench.on(key.slice(2).toLowerCase(), value);
+ });
+ } else if (!hasKey(bench, key)) {
+ bench[key] = deepClone(value);
+ }
+ }
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Handles cycling/completing the deferred benchmark.
+ *
+ * @memberOf Benchmark.Deferred
+ */
+ function resolve() {
+ var me = this,
+ clone = me.benchmark,
+ bench = clone._original;
+
+ if (bench.aborted) {
+ // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete
+ me.teardown();
+ clone.running = false;
+ cycle(me);
+ }
+ else if (++me.cycles < clone.count) {
+ // continue the test loop
+ if (support.timeout) {
+ // use setTimeout to avoid a call stack overflow if called recursively
+ setTimeout(function() { clone.compiled.call(me, timer); }, 0);
+ } else {
+ clone.compiled.call(me, timer);
+ }
+ }
+ else {
+ timer.stop(me);
+ me.teardown();
+ delay(clone, function() { cycle(me); });
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A deep clone utility.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Mixed} value The value to clone.
+ * @returns {Mixed} The cloned value.
+ */
+ function deepClone(value) {
+ var accessor,
+ circular,
+ clone,
+ ctor,
+ descriptor,
+ extensible,
+ key,
+ length,
+ markerKey,
+ parent,
+ result,
+ source,
+ subIndex,
+ data = { 'value': value },
+ index = 0,
+ marked = [],
+ queue = { 'length': 0 },
+ unmarked = [];
+
+ /**
+ * An easily detectable decorator for cloned values.
+ */
+ function Marker(object) {
+ this.raw = object;
+ }
+
+ /**
+ * The callback used by `forProps()`.
+ */
+ function forPropsCallback(subValue, subKey) {
+ // exit early to avoid cloning the marker
+ if (subValue && subValue.constructor == Marker) {
+ return;
+ }
+ // add objects to the queue
+ if (subValue === Object(subValue)) {
+ queue[queue.length++] = { 'key': subKey, 'parent': clone, 'source': value };
+ }
+ // assign non-objects
+ else {
+ try {
+ // will throw an error in strict mode if the property is read-only
+ clone[subKey] = subValue;
+ } catch(e) { }
+ }
+ }
+
+ /**
+ * Gets an available marker key for the given object.
+ */
+ function getMarkerKey(object) {
+ // avoid collisions with existing keys
+ var result = uid;
+ while (object[result] && object[result].constructor != Marker) {
+ result += 1;
+ }
+ return result;
+ }
+
+ do {
+ key = data.key;
+ parent = data.parent;
+ source = data.source;
+ clone = value = source ? source[key] : data.value;
+ accessor = circular = descriptor = false;
+
+ // create a basic clone to filter out functions, DOM elements, and
+ // other non `Object` objects
+ if (value === Object(value)) {
+ // use custom deep clone function if available
+ if (isClassOf(value.deepClone, 'Function')) {
+ clone = value.deepClone();
+ } else {
+ ctor = value.constructor;
+ switch (toString.call(value)) {
+ case '[object Array]':
+ clone = new ctor(value.length);
+ break;
+
+ case '[object Boolean]':
+ clone = new ctor(value == true);
+ break;
+
+ case '[object Date]':
+ clone = new ctor(+value);
+ break;
+
+ case '[object Object]':
+ isPlainObject(value) && (clone = {});
+ break;
+
+ case '[object Number]':
+ case '[object String]':
+ clone = new ctor(value);
+ break;
+
+ case '[object RegExp]':
+ clone = ctor(value.source,
+ (value.global ? 'g' : '') +
+ (value.ignoreCase ? 'i' : '') +
+ (value.multiline ? 'm' : ''));
+ }
+ }
+ // continue clone if `value` doesn't have an accessor descriptor
+ // http://es5.github.com/#x8.10.1
+ if (clone && clone != value &&
+ !(descriptor = source && support.descriptors && getDescriptor(source, key),
+ accessor = descriptor && (descriptor.get || descriptor.set))) {
+ // use an existing clone (circular reference)
+ if ((extensible = isExtensible(value))) {
+ markerKey = getMarkerKey(value);
+ if (value[markerKey]) {
+ circular = clone = value[markerKey].raw;
+ }
+ } else {
+ // for frozen/sealed objects
+ for (subIndex = 0, length = unmarked.length; subIndex < length; subIndex++) {
+ data = unmarked[subIndex];
+ if (data.object === value) {
+ circular = clone = data.clone;
+ break;
+ }
+ }
+ }
+ if (!circular) {
+ // mark object to allow quickly detecting circular references and tie it to its clone
+ if (extensible) {
+ value[markerKey] = new Marker(clone);
+ marked.push({ 'key': markerKey, 'object': value });
+ } else {
+ // for frozen/sealed objects
+ unmarked.push({ 'clone': clone, 'object': value });
+ }
+ // iterate over object properties
+ forProps(value, forPropsCallback, { 'which': 'all' });
+ }
+ }
+ }
+ if (parent) {
+ // for custom property descriptors
+ if (accessor || (descriptor && !(descriptor.configurable && descriptor.enumerable && descriptor.writable))) {
+ if ('value' in descriptor) {
+ descriptor.value = clone;
+ }
+ setDescriptor(parent, key, descriptor);
+ }
+ // for default property descriptors
+ else {
+ parent[key] = clone;
+ }
+ } else {
+ result = clone;
+ }
+ } while ((data = queue[index++]));
+
+ // remove markers
+ for (index = 0, length = marked.length; index < length; index++) {
+ data = marked[index];
+ delete data.object[data.key];
+ }
+ return result;
+ }
+
+ /**
+ * An iteration utility for arrays and objects.
+ * Callbacks may terminate the loop by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @returns {Array|Object} Returns the object iterated over.
+ */
+ function each(object, callback, thisArg) {
+ var result = object;
+ object = Object(object);
+
+ var fn = callback,
+ index = -1,
+ length = object.length,
+ isSnapshot = !!(object.snapshotItem && (length = object.snapshotLength)),
+ isSplittable = (noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String'),
+ isConvertable = isSnapshot || isSplittable || 'item' in object,
+ origObject = object;
+
+ // in Opera < 10.5 `hasKey(object, 'length')` returns `false` for NodeLists
+ if (length === length >>> 0) {
+ if (isConvertable) {
+ // the third argument of the callback is the original non-array object
+ callback = function(value, index) {
+ return fn.call(this, value, index, origObject);
+ };
+ // in IE < 9 strings don't support accessing characters by index
+ if (isSplittable) {
+ object = object.split('');
+ } else {
+ object = [];
+ while (++index < length) {
+ // in Safari 2 `index in object` is always `false` for NodeLists
+ object[index] = isSnapshot ? result.snapshotItem(index) : result[index];
+ }
+ }
+ }
+ forEach(object, callback, thisArg);
+ } else {
+ forOwn(object, callback, thisArg);
+ }
+ return result;
+ }
+
+ /**
+ * Copies enumerable properties from the source(s) object to the destination object.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Object} destination The destination object.
+ * @param {Object} [source={}] The source object.
+ * @returns {Object} The destination object.
+ */
+ function extend(destination, source) {
+ // Chrome < 14 incorrectly sets `destination` to `undefined` when we `delete arguments[0]`
+ // http://code.google.com/p/v8/issues/detail?id=839
+ var result = destination;
+ delete arguments[0];
+
+ forEach(arguments, function(source) {
+ forProps(source, function(value, key) {
+ result[key] = value;
+ });
+ });
+ return result;
+ }
+
+ /**
+ * A generic `Array#filter` like method.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} array The array to iterate over.
+ * @param {Function|String} callback The function/alias called per iteration.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @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, thisArg) {
+ var result;
+
+ if (callback == 'successful') {
+ // callback to exclude those that are errored, unrun, or have hz of Infinity
+ callback = function(bench) { return bench.cycles && isFinite(bench.hz); };
+ }
+ else if (callback == 'fastest' || callback == 'slowest') {
+ // 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.moe > b.mean + b.moe ? 1 : -1) * (callback == 'fastest' ? 1 : -1);
+ });
+ result = filter(result, function(bench) {
+ return result[0].compare(bench) == 0;
+ });
+ }
+ return result || reduce(array, function(result, value, index) {
+ return callback.call(thisArg, value, index, array) ? (result.push(value), result) : result;
+ }, []);
+ }
+
+ /**
+ * A generic `Array#forEach` like method.
+ * Callbacks may terminate the loop by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @returns {Array} Returns the array iterated over.
+ */
+ function forEach(array, callback, thisArg) {
+ var index = -1,
+ length = (array = Object(array)).length >>> 0;
+
+ if (thisArg !== undefined) {
+ callback = bind(callback, thisArg);
+ }
+ while (++index < length) {
+ if (index in array &&
+ callback(array[index], index, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Iterates over an object's own properties, executing the `callback` for each.
+ * Callbacks may terminate the loop by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function executed per own property.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @returns {Object} Returns the object iterated over.
+ */
+ function forOwn(object, callback, thisArg) {
+ return forProps(object, callback, { 'bind': thisArg, 'which': 'own' });
+ }
+
+ /**
+ * Converts a number to a more readable comma-separated string representation.
+ *
+ * @static
+ * @memberOf 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
+ * @memberOf 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() {
+ // lazy define for worst case fallback (not as accurate)
+ hasKey = function(object, key) {
+ var parent = object != null && (object.constructor || Object).prototype;
+ return !!parent && key in Object(object) && !(key in parent && object[key] === parent[key]);
+ };
+ // for modern browsers
+ if (isClassOf(hasOwnProperty, 'Function')) {
+ hasKey = function(object, key) {
+ return object != null && hasOwnProperty.call(object, key);
+ };
+ }
+ // for Safari 2
+ else if ({}.__proto__ == Object.prototype) {
+ hasKey = function(object, key) {
+ var result = false;
+ if (object != null) {
+ object = Object(object);
+ object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
+ }
+ return result;
+ };
+ }
+ return hasKey.apply(this, arguments);
+ }
+
+ /**
+ * A generic `Array#indexOf` like method.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} array The array to iterate over.
+ * @param {Mixed} value The value to search for.
+ * @param {Number} [fromIndex=0] The index to start searching from.
+ * @returns {Number} The index of the matched value or `-1`.
+ */
+ function indexOf(array, value, fromIndex) {
+ var index = toInteger(fromIndex),
+ length = (array = Object(array)).length >>> 0;
+
+ index = (index < 0 ? max(0, length + index) : index) - 1;
+ while (++index < length) {
+ if (index in array && value === array[index]) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Modify a string by replacing named tokens with matching object property values.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {String} string The string to modify.
+ * @param {Object} object The template object.
+ * @returns {String} The modified string.
+ */
+ function interpolate(string, object) {
+ forOwn(object, function(value, key) {
+ // escape regexp special characters in `key`
+ string = string.replace(RegExp('#\\{' + key.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1') + '\\}', 'g'), value);
+ });
+ return string;
+ }
+
+ /**
+ * Invokes a method on all items in an array.
+ *
+ * @static
+ * @memberOf 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,
+ bench,
+ queued,
+ index = -1,
+ eventProps = { 'currentTarget': benches },
+ options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
+ result = map(benches, function(bench) { return bench; });
+
+ /**
+ * Invokes the method of the current object and if synchronous, fetches the next.
+ */
+ function execute() {
+ var listeners,
+ async = isAsync(bench);
+
+ if (async) {
+ // use `getNext` as the first listener
+ bench.on('complete', getNext);
+ listeners = bench.events.complete;
+ listeners.splice(0, 0, listeners.pop());
+ }
+ // execute method
+ result[index] = isClassOf(bench && bench[name], 'Function') ? bench[name].apply(bench, args) : undefined;
+ // if synchronous return true until finished
+ return !async && getNext();
+ }
+
+ /**
+ * Fetches the next bench or executes `onComplete` callback.
+ */
+ function getNext(event) {
+ var cycleEvent,
+ last = bench,
+ async = isAsync(last);
+
+ if (async) {
+ last.off('complete', getNext);
+ last.emit('complete');
+ }
+ // emit "cycle" event
+ eventProps.type = 'cycle';
+ eventProps.target = last;
+ cycleEvent = Event(eventProps);
+ options.onCycle.call(benches, cycleEvent);
+
+ // choose next benchmark if not exiting early
+ if (!cycleEvent.aborted && raiseIndex() !== false) {
+ bench = queued ? benches[0] : result[index];
+ if (isAsync(bench)) {
+ delay(bench, execute);
+ }
+ else if (async) {
+ // resume execution if previously asynchronous but now synchronous
+ while (execute()) { }
+ }
+ else {
+ // continue synchronous execution
+ return true;
+ }
+ } else {
+ // emit "complete" event
+ eventProps.type = 'complete';
+ options.onComplete.call(benches, Event(eventProps));
+ }
+ // When used as a listener `event.aborted = true` will cancel the rest of
+ // the "complete" listeners because they were already called above and when
+ // used as part of `getNext` the `return false` will exit the execution while-loop.
+ if (event) {
+ event.aborted = true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if invoking `Benchmark#run` with asynchronous cycles.
+ */
+ function isAsync(object) {
+ // avoid using `instanceof` here because of IE memory leak issues with host objects
+ var async = args[0] && args[0].async;
+ return Object(object).constructor == Benchmark && name == 'run' &&
+ ((async == null ? object.options.async : async) && support.timeout || object.defer);
+ }
+
+ /**
+ * Raises `index` to the next defined index or returns `false`.
+ */
+ function raiseIndex() {
+ var length = result.length;
+ if (queued) {
+ // if queued remove the previous bench and subsequent skipped non-entries
+ do {
+ ++index > 0 && shift.call(benches);
+ } while ((length = benches.length) && !('0' in benches));
+ }
+ else {
+ while (++index < length && !(index in result)) { }
+ }
+ // if we reached the last index then return `false`
+ return (queued ? length : index < length) ? index : (index = false);
+ }
+
+ // juggle arguments
+ if (isClassOf(name, 'String')) {
+ // 2 arguments (array, name)
+ args = slice.call(arguments, 2);
+ } else {
+ // 2 arguments (array, options)
+ options = extend(options, name);
+ name = options.name;
+ args = isClassOf(args = 'args' in options ? options.args : [], 'Array') ? args : [args];
+ queued = options.queued;
+ }
+
+ // start iterating over the array
+ if (raiseIndex() !== false) {
+ // emit "start" event
+ bench = result[index];
+ eventProps.type = 'start';
+ eventProps.target = bench;
+ options.onStart.call(benches, Event(eventProps));
+
+ // end early if the suite was aborted in an "onStart" listener
+ if (benches.aborted && benches.constructor == Suite && name == 'run') {
+ // emit "cycle" event
+ eventProps.type = 'cycle';
+ options.onCycle.call(benches, Event(eventProps));
+ // emit "complete" event
+ eventProps.type = 'complete';
+ options.onComplete.call(benches, Event(eventProps));
+ }
+ // else start
+ else {
+ if (isAsync(bench)) {
+ delay(bench, execute);
+ } else {
+ while (execute()) { }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates a string of joined array values or object key-value pairs.
+ *
+ * @static
+ * @memberOf 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 result = [],
+ length = (object = Object(object)).length,
+ arrayLike = length === length >>> 0;
+
+ separator2 || (separator2 = ': ');
+ each(object, function(value, key) {
+ result.push(arrayLike ? value : key + separator2 + value);
+ });
+ return result.join(separator1 || ',');
+ }
+
+ /**
+ * A generic `Array#map` like method.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @returns {Array} A new array of values returned by the callback.
+ */
+ function map(array, callback, thisArg) {
+ return reduce(array, function(result, value, index) {
+ result[index] = callback.call(thisArg, value, index, array);
+ return result;
+ }, Array(Object(array).length >>> 0));
+ }
+
+ /**
+ * Retrieves the value of a specified property from all items in an array.
+ *
+ * @static
+ * @memberOf 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 == null ? undefined : object[property];
+ });
+ }
+
+ /**
+ * A generic `Array#reduce` like method.
+ *
+ * @static
+ * @memberOf 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) {
+ var noaccum = arguments.length < 3;
+ forEach(array, function(value, index) {
+ accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, array);
+ });
+ return accumulator;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Aborts all benchmarks in the suite.
+ *
+ * @name abort
+ * @memberOf Benchmark.Suite
+ * @returns {Object} The suite instance.
+ */
+ function abortSuite() {
+ var event,
+ me = this,
+ resetting = calledBy.resetSuite;
+
+ if (me.running) {
+ event = Event('abort');
+ me.emit(event);
+ if (!event.cancelled || resetting) {
+ // avoid infinite recursion
+ calledBy.abortSuite = true;
+ me.reset();
+ delete calledBy.abortSuite;
+
+ if (!resetting) {
+ me.aborted = true;
+ invoke(me, 'abort');
+ }
+ }
+ }
+ return me;
+ }
+
+ /**
+ * Adds a test to the benchmark suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {String} name A name to identify the benchmark.
+ * @param {Function|String} 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
+ * });
+ *
+ * // or name and options
+ * suite.add('foo', {
+ * 'fn': fn,
+ * 'onCycle': onCycle,
+ * 'onComplete': onComplete
+ * });
+ *
+ * // or options only
+ * suite.add({
+ * 'name': 'foo',
+ * 'fn': fn,
+ * 'onCycle': onCycle,
+ * 'onComplete': onComplete
+ * });
+ */
+ function add(name, fn, options) {
+ var me = this,
+ bench = Benchmark(name, fn, options),
+ event = Event({ 'type': 'add', 'target': bench });
+
+ if (me.emit(event), !event.cancelled) {
+ me.push(bench);
+ }
+ return me;
+ }
+
+ /**
+ * Creates a new suite with cloned benchmarks.
+ *
+ * @name clone
+ * @memberOf 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({}, me.options, options));
+
+ // copy own properties
+ forOwn(me, function(value, key) {
+ if (!hasKey(result, key)) {
+ result[key] = value && isClassOf(value.clone, 'Function')
+ ? value.clone()
+ : deepClone(value);
+ }
+ });
+ return result;
+ }
+
+ /**
+ * An `Array#filter` like method.
+ *
+ * @name filter
+ * @memberOf 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
+ * @memberOf Benchmark.Suite
+ * @returns {Object} The suite instance.
+ */
+ function resetSuite() {
+ var event,
+ me = this,
+ aborting = calledBy.abortSuite;
+
+ if (me.running && !aborting) {
+ // no worries, `resetSuite()` is called within `abortSuite()`
+ calledBy.resetSuite = true;
+ me.abort();
+ delete calledBy.resetSuite;
+ }
+ // reset if the state has changed
+ else if ((me.aborted || me.running) &&
+ (me.emit(event = Event('reset')), !event.cancelled)) {
+ me.running = false;
+ if (!aborting) {
+ invoke(me, 'reset');
+ }
+ }
+ return me;
+ }
+
+ /**
+ * Runs the suite.
+ *
+ * @name run
+ * @memberOf Benchmark.Suite
+ * @param {Object} [options={}] Options object.
+ * @returns {Object} The suite instance.
+ * @example
+ *
+ * // basic usage
+ * suite.run();
+ *
+ * // or with options
+ * suite.run({ 'async': true, 'queued': true });
+ */
+ function runSuite(options) {
+ var me = this;
+
+ me.reset();
+ me.running = true;
+ options || (options = {});
+
+ invoke(me, {
+ 'name': 'run',
+ 'args': options,
+ 'queued': options.queued,
+ 'onStart': function(event) {
+ me.emit(event);
+ },
+ 'onCycle': function(event) {
+ var bench = event.target;
+ if (bench.error) {
+ me.emit({ 'type': 'error', 'target': bench });
+ }
+ me.emit(event);
+ event.aborted = me.aborted;
+ },
+ 'onComplete': function(event) {
+ me.running = false;
+ me.emit(event);
+ }
+ });
+ return me;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Executes all registered listeners of the specified event type.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String|Object} type The event type or object.
+ * @returns {Mixed} Returns the return value of the last listener executed.
+ */
+ function emit(type) {
+ var listeners,
+ me = this,
+ event = Event(type),
+ events = me.events,
+ args = (arguments[0] = event, arguments);
+
+ event.currentTarget || (event.currentTarget = me);
+ event.target || (event.target = me);
+ delete event.result;
+
+ if (events && (listeners = hasKey(events, event.type) && events[event.type])) {
+ forEach(listeners.slice(), function(listener) {
+ if ((event.result = listener.apply(me, args)) === false) {
+ event.cancelled = true;
+ }
+ return !event.aborted;
+ });
+ }
+ return event.result;
+ }
+
+ /**
+ * Returns an array of event listeners for a given type that can be manipulated
+ * to add or remove listeners.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} type The event type.
+ * @returns {Array} The listeners array.
+ */
+ function listeners(type) {
+ var me = this,
+ events = me.events || (me.events = {});
+
+ return hasKey(events, type) ? events[type] : (events[type] = []);
+ }
+
+ /**
+ * Unregisters a listener for the specified event type(s),
+ * or unregisters all listeners for the specified event type(s),
+ * or unregisters all listeners for all event types.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} [type] The event type.
+ * @param {Function} [listener] The function to unregister.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // unregister a listener for an event type
+ * bench.off('cycle', listener);
+ *
+ * // unregister a listener for multiple event types
+ * bench.off('start cycle', listener);
+ *
+ * // unregister all listeners for an event type
+ * bench.off('cycle');
+ *
+ * // unregister all listeners for multiple event types
+ * bench.off('start cycle complete');
+ *
+ * // unregister all listeners for all event types
+ * bench.off();
+ */
+ function off(type, listener) {
+ var me = this,
+ events = me.events;
+
+ events && each(type ? type.split(' ') : events, function(listeners, type) {
+ var index;
+ if (typeof listeners == 'string') {
+ type = listeners;
+ listeners = hasKey(events, type) && events[type];
+ }
+ if (listeners) {
+ if (listener) {
+ index = indexOf(listeners, listener);
+ if (index > -1) {
+ listeners.splice(index, 1);
+ }
+ } else {
+ listeners.length = 0;
+ }
+ }
+ });
+ return me;
+ }
+
+ /**
+ * Registers a listener for the specified event type(s).
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} type The event type.
+ * @param {Function} listener The function to register.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // register a listener for an event type
+ * bench.on('cycle', listener);
+ *
+ * // register a listener for multiple event types
+ * bench.on('start cycle', listener);
+ */
+ function on(type, listener) {
+ var me = this,
+ events = me.events || (me.events = {});
+
+ forEach(type.split(' '), function(type) {
+ (hasKey(events, type)
+ ? events[type]
+ : (events[type] = [])
+ ).push(listener);
+ });
+ return me;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Aborts the benchmark without recording times.
+ *
+ * @memberOf Benchmark
+ * @returns {Object} The benchmark instance.
+ */
+ function abort() {
+ var event,
+ me = this,
+ resetting = calledBy.reset;
+
+ if (me.running) {
+ event = Event('abort');
+ me.emit(event);
+ if (!event.cancelled || resetting) {
+ // avoid infinite recursion
+ calledBy.abort = true;
+ me.reset();
+ delete calledBy.abort;
+
+ if (support.timeout) {
+ clearTimeout(me._timerId);
+ delete me._timerId;
+ }
+ if (!resetting) {
+ me.aborted = true;
+ me.running = false;
+ }
+ }
+ }
+ return me;
+ }
+
+ /**
+ * Creates a new benchmark using the same test and options.
+ *
+ * @memberOf 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(extend({}, me, options));
+
+ // correct the `options` object
+ result.options = extend({}, me.options, options);
+
+ // copy own custom properties
+ forOwn(me, function(value, key) {
+ if (!hasKey(result, key)) {
+ result[key] = deepClone(value);
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Determines if a benchmark is faster than another.
+ *
+ * @memberOf Benchmark
+ * @param {Object} other The benchmark to compare.
+ * @returns {Number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+ */
+ function compare(other) {
+ var critical,
+ zStat,
+ me = this,
+ sample1 = me.stats.sample,
+ sample2 = other.stats.sample,
+ size1 = sample1.length,
+ size2 = sample2.length,
+ maxSize = max(size1, size2),
+ minSize = min(size1, size2),
+ u1 = getU(sample1, sample2),
+ u2 = getU(sample2, sample1),
+ u = min(u1, u2);
+
+ function getScore(xA, sampleB) {
+ return reduce(sampleB, function(total, xB) {
+ return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);
+ }, 0);
+ }
+
+ function getU(sampleA, sampleB) {
+ return reduce(sampleA, function(total, xA) {
+ return total + getScore(xA, sampleB);
+ }, 0);
+ }
+
+ function getZ(u) {
+ return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);
+ }
+
+ // exit early if comparing the same benchmark
+ if (me == other) {
+ return 0;
+ }
+ // reject the null hyphothesis the two samples come from the
+ // same population (i.e. have the same median) if...
+ if (size1 + size2 > 30) {
+ // ...the z-stat is greater than 1.96 or less than -1.96
+ // http://www.statisticslectures.com/topics/mannwhitneyu/
+ zStat = getZ(u);
+ return abs(zStat) > 1.96 ? (zStat > 0 ? -1 : 1) : 0;
+ }
+ // ...the U value is less than or equal the critical U value
+ // http://www.geoib.com/mann-whitney-u-test.html
+ critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];
+ return u <= critical ? (u == u1 ? 1 : -1) : 0;
+ }
+
+ /**
+ * Reset properties and abort if running.
+ *
+ * @memberOf Benchmark
+ * @returns {Object} The benchmark instance.
+ */
+ function reset() {
+ var data,
+ event,
+ me = this,
+ index = 0,
+ changes = { 'length': 0 },
+ queue = { 'length': 0 };
+
+ if (me.running && !calledBy.abort) {
+ // no worries, `reset()` is called within `abort()`
+ calledBy.reset = true;
+ me.abort();
+ delete calledBy.reset;
+ }
+ else {
+ // a non-recursive solution to check if properties have changed
+ // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4
+ data = { 'destination': me, 'source': extend({}, me.constructor.prototype, me.options) };
+ do {
+ forOwn(data.source, function(value, key) {
+ var changed,
+ destination = data.destination,
+ currValue = destination[key];
+
+ if (value && typeof value == 'object') {
+ if (isClassOf(value, 'Array')) {
+ // check if an array value has changed to a non-array value
+ if (!isClassOf(currValue, 'Array')) {
+ changed = currValue = [];
+ }
+ // or has changed its length
+ if (currValue.length != value.length) {
+ changed = currValue = currValue.slice(0, value.length);
+ currValue.length = value.length;
+ }
+ }
+ // check if an object has changed to a non-object value
+ else if (!currValue || typeof currValue != 'object') {
+ changed = currValue = {};
+ }
+ // register a changed object
+ if (changed) {
+ changes[changes.length++] = { 'destination': destination, 'key': key, 'value': currValue };
+ }
+ queue[queue.length++] = { 'destination': currValue, 'source': value };
+ }
+ // register a changed primitive
+ else if (value !== currValue && !(value == null || isClassOf(value, 'Function'))) {
+ changes[changes.length++] = { 'destination': destination, 'key': key, 'value': value };
+ }
+ });
+ }
+ while ((data = queue[index++]));
+
+ // if changed emit the `reset` event and if it isn't cancelled reset the benchmark
+ if (changes.length && (me.emit(event = Event('reset')), !event.cancelled)) {
+ forEach(changes, function(data) {
+ data.destination[data.key] = data.value;
+ });
+ }
+ }
+ return me;
+ }
+
+ /**
+ * Displays relevant benchmark information when coerced to a string.
+ *
+ * @name toString
+ * @memberOf Benchmark
+ * @returns {String} A string representation of the benchmark instance.
+ */
+ function toStringBench() {
+ var me = this,
+ error = me.error,
+ hz = me.hz,
+ id = me.id,
+ stats = me.stats,
+ size = stats.sample.length,
+ pm = support.java ? '+/-' : '\xb1',
+ result = me.name || (isNaN(id) ? id : '<Test #' + id + '>');
+
+ 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;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Clocks the time taken to execute a test per cycle (secs).
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @returns {Number} The time taken.
+ */
+ function clock() {
+ var applet,
+ options = Benchmark.options,
+ template = { 'begin': 's$=new n$', 'end': 'r$=(new n$-s$)/1e3', 'uid': uid },
+ timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];
+
+ // lazy define for hi-res timers
+ clock = function(clone) {
+ var deferred;
+ if (clone instanceof Deferred) {
+ deferred = clone;
+ clone = deferred.benchmark;
+ }
+
+ var bench = clone._original,
+ fn = bench.fn,
+ fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '',
+ stringable = isStringable(fn);
+
+ var source = {
+ 'setup': getSource(bench.setup, preprocess('m$.setup()')),
+ 'fn': getSource(fn, preprocess('m$.fn(' + fnArg + ')')),
+ 'fnArg': fnArg,
+ 'teardown': getSource(bench.teardown, preprocess('m$.teardown()'))
+ };
+
+ var count = bench.count = clone.count,
+ decompilable = support.decompilation || stringable,
+ id = bench.id,
+ isEmpty = !(source.fn || stringable),
+ name = bench.name || (typeof id == 'number' ? '<Test #' + id + '>' : id),
+ ns = timer.ns,
+ result = 0;
+
+ // init `minTime` if needed
+ clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);
+
+ // repair nanosecond timer
+ // (some Chrome builds erase the `ns` variable after millions of executions)
+ if (applet) {
+ try {
+ ns.nanoTime();
+ } catch(e) {
+ // use non-element to avoid issues with libs that augment them
+ ns = timer.ns = new applet.Packages.nano;
+ }
+ }
+
+ // Compile in setup/teardown functions and the test loop.
+ // Create a new compiled test, instead of using the cached `bench.compiled`,
+ // to avoid potential engine optimizations enabled over the life of the test.
+ var compiled = bench.compiled = createFunction(preprocess('t$'), interpolate(
+ preprocess(deferred
+ ? 'var d$=this,#{fnArg}=d$,m$=d$.benchmark._original,f$=m$.fn,su$=m$.setup,td$=m$.teardown;' +
+ // when `deferred.cycles` is `0` then...
+ 'if(!d$.cycles){' +
+ // set `deferred.fn`
+ 'd$.fn=function(){var #{fnArg}=d$;if(typeof f$=="function"){try{#{fn}\n}catch(e$){f$(d$)}}else{#{fn}\n}};' +
+ // set `deferred.teardown`
+ 'd$.teardown=function(){d$.cycles=0;if(typeof td$=="function"){try{#{teardown}\n}catch(e$){td$()}}else{#{teardown}\n}};' +
+ // execute the benchmark's `setup`
+ 'if(typeof su$=="function"){try{#{setup}\n}catch(e$){su$()}}else{#{setup}\n};' +
+ // start timer
+ 't$.start(d$);' +
+ // execute `deferred.fn` and return a dummy object
+ '}d$.fn();return{}'
+
+ : 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count,n$=t$.ns;#{setup}\n#{begin};' +
+ 'while(i$--){#{fn}\n}#{end};#{teardown}\nreturn{elapsed:r$,uid:"#{uid}"}'),
+ source
+ ));
+
+ try {
+ if (isEmpty) {
+ // Firefox may remove dead code from Function#toString results
+ // http://bugzil.la/536085
+ throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.');
+ }
+ else if (!deferred) {
+ // pretest to determine if compiled code is exits early, usually by a
+ // rogue `return` statement, by checking for a return object with the uid
+ bench.count = 1;
+ compiled = (compiled.call(bench, timer) || {}).uid == uid && compiled;
+ bench.count = count;
+ }
+ } catch(e) {
+ compiled = null;
+ clone.error = e || new Error(String(e));
+ bench.count = count;
+ }
+ // fallback when a test exits early or errors during pretest
+ if (decompilable && !compiled && !deferred && !isEmpty) {
+ compiled = createFunction(preprocess('t$'), interpolate(
+ preprocess(
+ (clone.error && !stringable
+ ? 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count'
+ : 'function f$(){#{fn}\n}var r$,s$,m$=this,i$=m$.count'
+ ) +
+ ',n$=t$.ns;#{setup}\n#{begin};m$.f$=f$;while(i$--){m$.f$()}#{end};' +
+ 'delete m$.f$;#{teardown}\nreturn{elapsed:r$}'
+ ),
+ source
+ ));
+
+ try {
+ // pretest one more time to check for errors
+ bench.count = 1;
+ compiled.call(bench, timer);
+ bench.compiled = compiled;
+ bench.count = count;
+ delete clone.error;
+ }
+ catch(e) {
+ bench.count = count;
+ if (clone.error) {
+ compiled = null;
+ } else {
+ bench.compiled = compiled;
+ clone.error = e || new Error(String(e));
+ }
+ }
+ }
+ // assign `compiled` to `clone` before calling in case a deferred benchmark
+ // immediately calls `deferred.resolve()`
+ clone.compiled = compiled;
+ // if no errors run the full test loop
+ if (!clone.error) {
+ result = compiled.call(deferred || bench, timer).elapsed;
+ }
+ return result;
+ };
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Gets the current timer's minimum resolution (secs).
+ */
+ function getRes(unit) {
+ var measured,
+ begin,
+ count = 30,
+ divisor = 1e3,
+ ns = timer.ns,
+ sample = [];
+
+ // get average smallest measurable time
+ while (count--) {
+ if (unit == 'us') {
+ divisor = 1e6;
+ if (ns.stop) {
+ ns.start();
+ while (!(measured = ns.microseconds())) { }
+ } else if (ns[perfName]) {
+ divisor = 1e3;
+ measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.' + perfName + '()-s)){};return r')(ns);
+ } else {
+ begin = ns();
+ while (!(measured = ns() - begin)) { }
+ }
+ }
+ else if (unit == 'ns') {
+ divisor = 1e9;
+ if (ns.nanoTime) {
+ begin = ns.nanoTime();
+ while (!(measured = ns.nanoTime() - begin)) { }
+ } else {
+ begin = (begin = ns())[0] + (begin[1] / divisor);
+ while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) { }
+ divisor = 1;
+ }
+ }
+ else {
+ begin = new ns;
+ while (!(measured = new ns - begin)) { }
+ }
+ // 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;
+ }
+
+ /**
+ * Replaces all occurrences of `$` with a unique number and
+ * template tokens with content.
+ */
+ function preprocess(code) {
+ return interpolate(code, template).replace(/\$/g, /\d+/.exec(uid));
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // detect nanosecond support from a Java applet
+ each(doc && doc.applets || [], function(element) {
+ return !(timer.ns = applet = 'nanoTime' in element && element);
+ });
+
+ // check type in case Safari returns an object instead of a number
+ try {
+ if (typeof timer.ns.nanoTime() == 'number') {
+ timers.push({ 'ns': timer.ns, '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 ((timer.ns = new (window.chrome || window.chromium).Interval)) {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
+ } catch(e) { }
+
+ // detect `performance.now` microsecond resolution timer
+ if ((timer.ns = perfName && perfObject)) {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
+
+ // detect Node's nanosecond resolution timer available in Node >= 0.8
+ if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {
+ timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+ }
+
+ // detect Wade Simmons' Node microtime module
+ if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
+
+ // pick timer with highest resolution
+ timer = reduce(timers, function(timer, other) {
+ return other.res < timer.res ? other : timer;
+ });
+
+ // remove unused applet
+ if (timer.unit != 'ns' && applet) {
+ applet = destroyElement(applet);
+ }
+ // 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') {
+ if (timer.ns.nanoTime) {
+ extend(template, {
+ 'begin': 's$=n$.nanoTime()',
+ 'end': 'r$=(n$.nanoTime()-s$)/1e9'
+ });
+ } else {
+ extend(template, {
+ 'begin': 's$=n$()',
+ 'end': 'r$=n$(s$);r$=r$[0]+(r$[1]/1e9)'
+ });
+ }
+ }
+ else if (timer.unit == 'us') {
+ if (timer.ns.stop) {
+ extend(template, {
+ 'begin': 's$=n$.start()',
+ 'end': 'r$=n$.microseconds()/1e6'
+ });
+ } else if (perfName) {
+ extend(template, {
+ 'begin': 's$=n$.' + perfName + '()',
+ 'end': 'r$=(n$.' + perfName + '()-s$)/1e3'
+ });
+ } else {
+ extend(template, {
+ 'begin': 's$=n$()',
+ 'end': 'r$=(n$()-s$)/1e6'
+ });
+ }
+ }
+
+ // define `timer` methods
+ timer.start = createFunction(preprocess('o$'),
+ preprocess('var n$=this.ns,#{begin};o$.elapsed=0;o$.timeStamp=s$'));
+
+ timer.stop = createFunction(preprocess('o$'),
+ preprocess('var n$=this.ns,s$=o$.timeStamp,#{end};o$.elapsed=r$'));
+
+ // resolve time span required to achieve a percent uncertainty of at most 1%
+ // http://spiff.rit.edu/classes/phys273/uncert/uncert.html
+ options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));
+ return clock.apply(null, arguments);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Computes stats on benchmark results.
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} options The options object.
+ */
+ function compute(bench, options) {
+ options || (options = {});
+
+ var async = options.async,
+ elapsed = 0,
+ initCount = bench.initCount,
+ minSamples = bench.minSamples,
+ queue = [],
+ sample = bench.stats.sample;
+
+ /**
+ * Adds a clone to the queue.
+ */
+ function enqueue() {
+ queue.push(bench.clone({
+ '_original': bench,
+ 'events': {
+ 'abort': [update],
+ 'cycle': [update],
+ 'error': [update],
+ 'start': [update]
+ }
+ }));
+ }
+
+ /**
+ * Updates the clone/original benchmarks to keep their data in sync.
+ */
+ function update(event) {
+ var clone = this,
+ type = event.type;
+
+ if (bench.running) {
+ if (type == 'start') {
+ // Note: `clone.minTime` prop is inited in `clock()`
+ clone.count = bench.initCount;
+ }
+ else {
+ if (type == 'error') {
+ bench.error = clone.error;
+ }
+ if (type == 'abort') {
+ bench.abort();
+ bench.emit('cycle');
+ } else {
+ event.currentTarget = event.target = bench;
+ bench.emit(event);
+ }
+ }
+ } else if (bench.aborted) {
+ // clear abort listeners to avoid triggering bench's abort/cycle again
+ clone.events.abort.length = 0;
+ clone.abort();
+ }
+ }
+
+ /**
+ * Determines if more clones should be queued or if cycling should stop.
+ */
+ function evaluate(event) {
+ var critical,
+ df,
+ mean,
+ moe,
+ rme,
+ sd,
+ sem,
+ variance,
+ clone = event.target,
+ done = bench.aborted,
+ now = +new Date,
+ size = sample.push(clone.times.period),
+ maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,
+ times = bench.times,
+ varOf = function(sum, x) { return sum + pow(x - mean, 2); };
+
+ // exit early for aborted or unclockable tests
+ if (done || clone.hz == Infinity) {
+ maxedOut = !(size = sample.length = queue.length = 0);
+ }
+
+ if (!done) {
+ // sample mean (estimate of the population mean)
+ mean = getMean(sample);
+ // sample variance (estimate of the population variance)
+ variance = reduce(sample, varOf, 0) / (size - 1) || 0;
+ // sample standard deviation (estimate of the population standard deviation)
+ sd = sqrt(variance);
+ // standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean)
+ sem = sd / sqrt(size);
+ // degrees of freedom
+ df = size - 1;
+ // critical value
+ critical = tTable[Math.round(df) || 1] || tTable.infinity;
+ // margin of error
+ moe = sem * critical;
+ // relative margin of error
+ rme = (moe / mean) * 100 || 0;
+
+ extend(bench.stats, {
+ 'deviation': sd,
+ 'mean': mean,
+ 'moe': moe,
+ 'rme': rme,
+ 'sem': sem,
+ 'variance': variance
+ });
+
+ // Abort the cycle loop when the minimum sample size has been collected
+ // and the elapsed time exceeds the maximum time allowed per benchmark.
+ // We don't count cycle delays toward the max time because delays may be
+ // increased by browsers that clamp timeouts for inactive tabs.
+ // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs
+ if (maxedOut) {
+ // reset the `initCount` in case the benchmark is rerun
+ bench.initCount = initCount;
+ bench.running = false;
+ done = true;
+ times.elapsed = (now - times.timeStamp) / 1e3;
+ }
+ if (bench.hz != Infinity) {
+ bench.hz = 1 / mean;
+ times.cycle = mean * bench.count;
+ times.period = mean;
+ }
+ }
+ // if time permits, increase sample size to reduce the margin of error
+ if (queue.length < 2 && !maxedOut) {
+ enqueue();
+ }
+ // abort the invoke cycle when done
+ event.aborted = done;
+ }
+
+ // init queue and begin
+ enqueue();
+ invoke(queue, {
+ 'name': 'run',
+ 'args': { 'async': async },
+ 'queued': true,
+ 'onCycle': evaluate,
+ 'onComplete': function() { bench.emit('complete'); }
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Cycles a benchmark until a run `count` can be established.
+ *
+ * @private
+ * @param {Object} clone The cloned benchmark instance.
+ * @param {Object} options The options object.
+ */
+ function cycle(clone, options) {
+ options || (options = {});
+
+ var deferred;
+ if (clone instanceof Deferred) {
+ deferred = clone;
+ clone = clone.benchmark;
+ }
+
+ var clocked,
+ cycles,
+ divisor,
+ event,
+ minTime,
+ period,
+ async = options.async,
+ bench = clone._original,
+ count = clone.count,
+ times = clone.times;
+
+ // continue, if not aborted between cycles
+ if (clone.running) {
+ // `minTime` is set to `Benchmark.options.minTime` in `clock()`
+ cycles = ++clone.cycles;
+ clocked = deferred ? deferred.elapsed : clock(clone);
+ minTime = clone.minTime;
+
+ if (cycles > bench.cycles) {
+ bench.cycles = cycles;
+ }
+ if (clone.error) {
+ event = Event('error');
+ event.message = clone.error;
+ clone.emit(event);
+ if (!event.cancelled) {
+ clone.abort();
+ }
+ }
+ }
+
+ // continue, if not errored
+ if (clone.running) {
+ // time taken to complete last test cycle
+ bench.times.cycle = times.cycle = clocked;
+ // seconds per operation
+ period = bench.times.period = times.period = clocked / count;
+ // ops per second
+ bench.hz = clone.hz = 1 / period;
+ // avoid working our way up to this next time
+ bench.initCount = clone.initCount = count;
+ // do we need to do another cycle?
+ clone.running = clocked < minTime;
+
+ if (clone.running) {
+ // tests may clock at `0` when `initCount` is a small number,
+ // to avoid that we set its count to something a bit higher
+ if (!clocked && (divisor = divisors[clone.cycles]) != null) {
+ count = floor(4e6 / divisor);
+ }
+ // calculate how many more iterations it will take to achive the `minTime`
+ if (count <= clone.count) {
+ count += Math.ceil((minTime - clocked) / period);
+ }
+ clone.running = count != Infinity;
+ }
+ }
+ // should we exit early?
+ event = Event('cycle');
+ clone.emit(event);
+ if (event.aborted) {
+ clone.abort();
+ }
+ // figure out what to do next
+ if (clone.running) {
+ // start a new cycle
+ clone.count = count;
+ if (deferred) {
+ clone.compiled.call(deferred, timer);
+ } else if (async) {
+ delay(clone, function() { cycle(clone, options); });
+ } else {
+ cycle(clone);
+ }
+ }
+ else {
+ // fix TraceMonkey bug associated with clock fallbacks
+ // http://bugzil.la/509069
+ if (support.browser) {
+ runScript(uid + '=1;delete ' + uid);
+ }
+ // done
+ clone.emit('complete');
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Runs the benchmark.
+ *
+ * @memberOf Benchmark
+ * @param {Object} [options={}] Options object.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // basic usage
+ * bench.run();
+ *
+ * // or with options
+ * bench.run({ 'async': true });
+ */
+ function run(options) {
+ var me = this,
+ event = Event('start');
+
+ // set `running` to `false` so `reset()` won't call `abort()`
+ me.running = false;
+ me.reset();
+ me.running = true;
+
+ me.count = me.initCount;
+ me.times.timeStamp = +new Date;
+ me.emit(event);
+
+ if (!event.cancelled) {
+ options = { 'async': ((options = options && options.async) == null ? me.async : options) && support.timeout };
+
+ // for clones created within `compute()`
+ if (me._original) {
+ if (me.defer) {
+ Deferred(me);
+ } else {
+ cycle(me, options);
+ }
+ }
+ // for original benchmarks
+ else {
+ compute(me, options);
+ }
+ }
+ return me;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // Firefox 1 erroneously defines variable and argument names of functions on
+ // the function itself as non-configurable properties with `undefined` values.
+ // The bugginess continues as the `Benchmark` constructor has an argument
+ // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,
+ // making it non-writable in the process, unless it is the first property
+ // assigned by for-in loop of `extend()`.
+ extend(Benchmark, {
+
+ /**
+ * The default options copied by benchmark instances.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'options': {
+
+ /**
+ * A flag to indicate that benchmark cycles will execute asynchronously
+ * by default.
+ *
+ * @memberOf Benchmark.options
+ * @type Boolean
+ */
+ 'async': false,
+
+ /**
+ * A flag to indicate that the benchmark clock is deferred.
+ *
+ * @memberOf Benchmark.options
+ * @type Boolean
+ */
+ 'defer': false,
+
+ /**
+ * The delay between test cycles (secs).
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'delay': 0.005,
+
+ /**
+ * Displayed by Benchmark#toString when a `name` is not available
+ * (auto-generated if absent).
+ *
+ * @memberOf Benchmark.options
+ * @type String
+ */
+ 'id': undefined,
+
+ /**
+ * The default number of times to execute a test on a benchmark's first cycle.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'initCount': 1,
+
+ /**
+ * The maximum time a benchmark is allowed to run before finishing (secs).
+ *
+ * Note: Cycle delays aren't counted toward the maximum time.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'maxTime': 5,
+
+ /**
+ * The minimum sample size required to perform statistical analysis.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'minSamples': 5,
+
+ /**
+ * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'minTime': 0,
+
+ /**
+ * The name of the benchmark.
+ *
+ * @memberOf Benchmark.options
+ * @type String
+ */
+ 'name': undefined,
+
+ /**
+ * An event listener called when the benchmark is aborted.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onAbort': undefined,
+
+ /**
+ * An event listener called when the benchmark completes running.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onComplete': undefined,
+
+ /**
+ * An event listener called after each run cycle.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onCycle': undefined,
+
+ /**
+ * An event listener called when a test errors.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onError': undefined,
+
+ /**
+ * An event listener called when the benchmark is reset.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onReset': undefined,
+
+ /**
+ * An event listener called when the benchmark starts running.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onStart': undefined
+ },
+
+ /**
+ * Platform object with properties describing things like browser name,
+ * version, and operating system.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'platform': req('platform') || window.platform || {
+
+ /**
+ * The platform description.
+ *
+ * @memberOf Benchmark.platform
+ * @type String
+ */
+ 'description': window.navigator && navigator.userAgent || null,
+
+ /**
+ * The name of the browser layout engine.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'layout': null,
+
+ /**
+ * The name of the product hosting the browser.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'product': null,
+
+ /**
+ * The name of the browser/environment.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'name': null,
+
+ /**
+ * The name of the product's manufacturer.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'manufacturer': null,
+
+ /**
+ * The name of the operating system.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'os': null,
+
+ /**
+ * The alpha/beta release indicator.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'prerelease': null,
+
+ /**
+ * The browser/environment version.
+ *
+ * @memberOf Benchmark.platform
+ * @type String|Null
+ */
+ 'version': null,
+
+ /**
+ * Return platform description when the platform object is coerced to a string.
+ *
+ * @memberOf Benchmark.platform
+ * @type Function
+ * @returns {String} The platform description.
+ */
+ 'toString': function() {
+ return this.description || '';
+ }
+ },
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type String
+ */
+ 'version': '1.0.0',
+
+ // an object of environment/feature detection flags
+ 'support': support,
+
+ // clone objects
+ 'deepClone': deepClone,
+
+ // iteration utility
+ 'each': each,
+
+ // augment objects
+ 'extend': extend,
+
+ // generic Array#filter
+ 'filter': filter,
+
+ // generic Array#forEach
+ 'forEach': forEach,
+
+ // generic own property iteration utility
+ 'forOwn': forOwn,
+
+ // converts a number to a comma-separated string
+ 'formatNumber': formatNumber,
+
+ // generic Object#hasOwnProperty
+ // (trigger hasKey's lazy define before assigning it to Benchmark)
+ 'hasKey': (hasKey(Benchmark, ''), hasKey),
+
+ // generic Array#indexOf
+ 'indexOf': indexOf,
+
+ // template utility
+ 'interpolate': interpolate,
+
+ // invokes a method on each item in an array
+ 'invoke': invoke,
+
+ // generic Array#join for arrays and objects
+ 'join': join,
+
+ // generic Array#map
+ 'map': map,
+
+ // retrieves a property value from each item in an array
+ 'pluck': pluck,
+
+ // generic Array#reduce
+ 'reduce': reduce
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ extend(Benchmark.prototype, {
+
+ /**
+ * The number of times a test was executed.
+ *
+ * @memberOf Benchmark
+ * @type Number
+ */
+ 'count': 0,
+
+ /**
+ * The number of cycles performed while benchmarking.
+ *
+ * @memberOf Benchmark
+ * @type Number
+ */
+ 'cycles': 0,
+
+ /**
+ * The number of executions per second.
+ *
+ * @memberOf Benchmark
+ * @type Number
+ */
+ 'hz': 0,
+
+ /**
+ * The compiled test function.
+ *
+ * @memberOf Benchmark
+ * @type Function|String
+ */
+ 'compiled': undefined,
+
+ /**
+ * The error object if the test failed.
+ *
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'error': undefined,
+
+ /**
+ * The test to benchmark.
+ *
+ * @memberOf Benchmark
+ * @type Function|String
+ */
+ 'fn': undefined,
+
+ /**
+ * A flag to indicate if the benchmark is aborted.
+ *
+ * @memberOf Benchmark
+ * @type Boolean
+ */
+ 'aborted': false,
+
+ /**
+ * A flag to indicate if the benchmark is running.
+ *
+ * @memberOf Benchmark
+ * @type Boolean
+ */
+ 'running': false,
+
+ /**
+ * Compiled into the test and executed immediately **before** the test loop.
+ *
+ * @memberOf Benchmark
+ * @type Function|String
+ * @example
+ *
+ * // basic usage
+ * var bench = Benchmark({
+ * 'setup': function() {
+ * var c = this.count,
+ * element = document.getElementById('container');
+ * while (c--) {
+ * element.appendChild(document.createElement('div'));
+ * }
+ * },
+ * 'fn': function() {
+ * element.removeChild(element.lastChild);
+ * }
+ * });
+ *
+ * // compiles to something like:
+ * var c = this.count,
+ * element = document.getElementById('container');
+ * while (c--) {
+ * element.appendChild(document.createElement('div'));
+ * }
+ * var start = new Date;
+ * while (count--) {
+ * element.removeChild(element.lastChild);
+ * }
+ * var end = new Date - start;
+ *
+ * // or using strings
+ * var bench = Benchmark({
+ * 'setup': '\
+ * var a = 0;\n\
+ * (function() {\n\
+ * (function() {\n\
+ * (function() {',
+ * 'fn': 'a += 1;',
+ * 'teardown': '\
+ * }())\n\
+ * }())\n\
+ * }())'
+ * });
+ *
+ * // compiles to something like:
+ * var a = 0;
+ * (function() {
+ * (function() {
+ * (function() {
+ * 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.
+ *
+ * @memberOf Benchmark
+ * @type Function|String
+ */
+ 'teardown': noop,
+
+ /**
+ * An object of stats including mean, margin or error, and standard deviation.
+ *
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'stats': {
+
+ /**
+ * The margin of error.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'moe': 0,
+
+ /**
+ * The relative margin of error (expressed as a percentage of the mean).
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'rme': 0,
+
+ /**
+ * The standard error of the mean.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'sem': 0,
+
+ /**
+ * The sample standard deviation.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'deviation': 0,
+
+ /**
+ * The sample arithmetic mean.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'mean': 0,
+
+ /**
+ * The array of sampled periods.
+ *
+ * @memberOf Benchmark#stats
+ * @type Array
+ */
+ 'sample': [],
+
+ /**
+ * The sample variance.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'variance': 0
+ },
+
+ /**
+ * An object of timing data including cycle, elapsed, period, start, and stop.
+ *
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'times': {
+
+ /**
+ * The time taken to complete the last cycle (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'cycle': 0,
+
+ /**
+ * The time taken to complete the benchmark (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'elapsed': 0,
+
+ /**
+ * The time taken to execute the test once (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'period': 0,
+
+ /**
+ * A timestamp of when the benchmark started (ms).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'timeStamp': 0
+ },
+
+ // aborts benchmark (does not record times)
+ 'abort': abort,
+
+ // creates a new benchmark using the same test and options
+ 'clone': clone,
+
+ // compares benchmark's hertz with another
+ 'compare': compare,
+
+ // executes listeners
+ 'emit': emit,
+
+ // get listeners
+ 'listeners': listeners,
+
+ // unregister listeners
+ 'off': off,
+
+ // register listeners
+ 'on': on,
+
+ // reset benchmark properties
+ 'reset': reset,
+
+ // runs the benchmark
+ 'run': run,
+
+ // pretty print benchmark info
+ 'toString': toStringBench
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ extend(Deferred.prototype, {
+
+ /**
+ * The deferred benchmark instance.
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Object
+ */
+ 'benchmark': null,
+
+ /**
+ * The number of deferred cycles performed while benchmarking.
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'cycles': 0,
+
+ /**
+ * The time taken to complete the deferred benchmark (secs).
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'elapsed': 0,
+
+ /**
+ * A timestamp of when the deferred benchmark started (ms).
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'timeStamp': 0,
+
+ // cycles/completes the deferred benchmark
+ 'resolve': resolve
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ extend(Event.prototype, {
+
+ /**
+ * A flag to indicate if the emitters listener iteration is aborted.
+ *
+ * @memberOf Benchmark.Event
+ * @type Boolean
+ */
+ 'aborted': false,
+
+ /**
+ * A flag to indicate if the default action is cancelled.
+ *
+ * @memberOf Benchmark.Event
+ * @type Boolean
+ */
+ 'cancelled': false,
+
+ /**
+ * The object whose listeners are currently being processed.
+ *
+ * @memberOf Benchmark.Event
+ * @type Object
+ */
+ 'currentTarget': undefined,
+
+ /**
+ * The return value of the last executed listener.
+ *
+ * @memberOf Benchmark.Event
+ * @type Mixed
+ */
+ 'result': undefined,
+
+ /**
+ * The object to which the event was originally emitted.
+ *
+ * @memberOf Benchmark.Event
+ * @type Object
+ */
+ 'target': undefined,
+
+ /**
+ * A timestamp of when the event was created (ms).
+ *
+ * @memberOf Benchmark.Event
+ * @type Number
+ */
+ 'timeStamp': 0,
+
+ /**
+ * The event type.
+ *
+ * @memberOf Benchmark.Event
+ * @type String
+ */
+ 'type': ''
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The default options copied by suite instances.
+ *
+ * @static
+ * @memberOf Benchmark.Suite
+ * @type Object
+ */
+ Suite.options = {
+
+ /**
+ * The name of the suite.
+ *
+ * @memberOf Benchmark.Suite.options
+ * @type String
+ */
+ 'name': undefined
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ extend(Suite.prototype, {
+
+ /**
+ * The number of benchmarks in the suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Number
+ */
+ 'length': 0,
+
+ /**
+ * A flag to indicate if the suite is aborted.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Boolean
+ */
+ 'aborted': false,
+
+ /**
+ * A flag to indicate if the suite is running.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Boolean
+ */
+ 'running': false,
+
+ /**
+ * An `Array#forEach` like method.
+ * Callbacks may terminate the loop by explicitly returning `false`.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {Function} callback The function called per iteration.
+ * @returns {Object} The suite iterated over.
+ */
+ 'forEach': methodize(forEach),
+
+ /**
+ * An `Array#indexOf` like method.
+ *
+ * @memberOf 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.
+ *
+ * @memberOf 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),
+
+ /**
+ * Converts the suite of benchmarks to a string.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {String} [separator=','] A string to separate each element of the array.
+ * @returns {String} The string.
+ */
+ 'join': [].join,
+
+ /**
+ * An `Array#map` like method.
+ *
+ * @memberOf 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.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {String} property The property to pluck.
+ * @returns {Array} A new array of property values.
+ */
+ 'pluck': methodize(pluck),
+
+ /**
+ * Removes the last benchmark from the suite and returns it.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Mixed} The removed benchmark.
+ */
+ 'pop': [].pop,
+
+ /**
+ * Appends benchmarks to the suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @returns {Number} The suite's new length.
+ */
+ 'push': [].push,
+
+ /**
+ * Sorts the benchmarks of the suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {Function} [compareFn=null] A function that defines the sort order.
+ * @returns {Object} The sorted suite.
+ */
+ 'sort': [].sort,
+
+ /**
+ * An `Array#reduce` like method.
+ *
+ * @memberOf 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,
+
+ // 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,
+
+ // get listeners
+ 'listeners': listeners,
+
+ // unregister listeners
+ 'off': off,
+
+ // register listeners
+ 'on': on,
+
+ // resets all benchmarks in the suite
+ 'reset': resetSuite,
+
+ // runs all benchmarks in the suite
+ 'run': runSuite,
+
+ // array methods
+ 'concat': concat,
+
+ 'reverse': reverse,
+
+ 'shift': shift,
+
+ 'slice': slice,
+
+ 'splice': splice,
+
+ 'unshift': unshift
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose Deferred, Event and Suite
+ extend(Benchmark, {
+ 'Deferred': Deferred,
+ 'Event': Event,
+ 'Suite': Suite
+ });
+
+ // expose Benchmark
+ // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // define as an anonymous module so, through path mapping, it can be aliased
+ define(function() {
+ return Benchmark;
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports) {
+ // in Node.js or RingoJS v0.8.0+
+ if (typeof module == 'object' && module && module.exports == freeExports) {
+ (module.exports = Benchmark).Benchmark = Benchmark;
+ }
+ // in Narwhal or RingoJS v0.7.0-
+ else {
+ freeExports.Benchmark = Benchmark;
+ }
+ }
+ // in a browser or Rhino
+ else {
+ // use square bracket notation so Closure Compiler won't munge `Benchmark`
+ // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
+ window['Benchmark'] = Benchmark;
+ }
+
+ // trigger clock's lazy define early to avoid a security error
+ if (support.air) {
+ clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } });
+ }
+}(this));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/README.md
new file mode 100644
index 0000000..5ac99e3
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/README.md
@@ -0,0 +1,2629 @@
+# Benchmark.js <sup>v1.0.0</sup>
+
+<!-- div -->
+
+
+<!-- div -->
+
+## <a id="Benchmark"></a>`Benchmark`
+* [`Benchmark`](#benchmarkname-fn--options)
+* [`Benchmark.version`](#benchmarkversion)
+* [`Benchmark.deepClone`](#benchmarkdeepclonevalue)
+* [`Benchmark.each`](#benchmarkeachobject-callback-thisarg)
+* [`Benchmark.extend`](#benchmarkextenddestination--source)
+* [`Benchmark.filter`](#benchmarkfilterarray-callback-thisarg)
+* [`Benchmark.forEach`](#benchmarkforeacharray-callback-thisarg)
+* [`Benchmark.formatNumber`](#benchmarkformatnumbernumber)
+* [`Benchmark.forOwn`](#benchmarkforownobject-callback-thisarg)
+* [`Benchmark.hasKey`](#benchmarkhaskeyobject-key)
+* [`Benchmark.indexOf`](#benchmarkindexofarray-value--fromindex0)
+* [`Benchmark.interpolate`](#benchmarkinterpolatestring-object)
+* [`Benchmark.invoke`](#benchmarkinvokebenches-name--arg1-arg2-)
+* [`Benchmark.join`](#benchmarkjoinobject--separator1--separator2:)
+* [`Benchmark.map`](#benchmarkmaparray-callback-thisarg)
+* [`Benchmark.pluck`](#benchmarkpluckarray-property)
+* [`Benchmark.reduce`](#benchmarkreducearray-callback-accumulator)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype`
+* [`Benchmark.prototype.aborted`](#benchmarkprototypeaborted)
+* [`Benchmark.prototype.compiled`](#benchmarkprototypecompiled)
+* [`Benchmark.prototype.count`](#benchmarkprototypecount)
+* [`Benchmark.prototype.cycles`](#benchmarkprototypecycles)
+* [`Benchmark.prototype.fn`](#benchmarkprototypefn)
+* [`Benchmark.prototype.hz`](#benchmarkprototypehz)
+* [`Benchmark.prototype.running`](#benchmarkprototyperunning)
+* [`Benchmark.prototype.setup`](#benchmarkprototypesetup)
+* [`Benchmark.prototype.teardown`](#benchmarkprototypeteardown)
+* [`Benchmark.prototype.abort`](#benchmarkprototypeabort)
+* [`Benchmark.prototype.clone`](#benchmarkprototypecloneoptions)
+* [`Benchmark.prototype.compare`](#benchmarkprototypecompareother)
+* [`Benchmark.prototype.emit`](#benchmarkprototypeemittype)
+* [`Benchmark.prototype.listeners`](#benchmarkprototypelistenerstype)
+* [`Benchmark.prototype.off`](#benchmarkprototypeofftype-listener)
+* [`Benchmark.prototype.on`](#benchmarkprototypeontype-listener)
+* [`Benchmark.prototype.reset`](#benchmarkprototypereset)
+* [`Benchmark.prototype.run`](#benchmarkprototyperunoptions)
+* [`Benchmark.prototype.toString`](#benchmarkprototypetostring)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.options`
+* [`Benchmark.options`](#benchmarkoptions)
+* [`Benchmark.options.async`](#benchmarkoptionsasync)
+* [`Benchmark.options.defer`](#benchmarkoptionsdefer)
+* [`Benchmark.options.delay`](#benchmarkoptionsdelay)
+* [`Benchmark.options.id`](#benchmarkoptionsid)
+* [`Benchmark.options.initCount`](#benchmarkoptionsinitcount)
+* [`Benchmark.options.maxTime`](#benchmarkoptionsmaxtime)
+* [`Benchmark.options.minSamples`](#benchmarkoptionsminsamples)
+* [`Benchmark.options.minTime`](#benchmarkoptionsmintime)
+* [`Benchmark.options.name`](#benchmarkoptionsname)
+* [`Benchmark.options.onAbort`](#benchmarkoptionsonabort)
+* [`Benchmark.options.onComplete`](#benchmarkoptionsoncomplete)
+* [`Benchmark.options.onCycle`](#benchmarkoptionsoncycle)
+* [`Benchmark.options.onError`](#benchmarkoptionsonerror)
+* [`Benchmark.options.onReset`](#benchmarkoptionsonreset)
+* [`Benchmark.options.onStart`](#benchmarkoptionsonstart)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.platform`
+* [`Benchmark.platform`](#benchmarkplatform)
+* [`Benchmark.platform.description`](#benchmarkplatformdescription)
+* [`Benchmark.platform.layout`](#benchmarkplatformlayout)
+* [`Benchmark.platform.manufacturer`](#benchmarkplatformmanufacturer)
+* [`Benchmark.platform.name`](#benchmarkplatformname)
+* [`Benchmark.platform.os`](#benchmarkplatformos)
+* [`Benchmark.platform.prerelease`](#benchmarkplatformprerelease)
+* [`Benchmark.platform.product`](#benchmarkplatformproduct)
+* [`Benchmark.platform.version`](#benchmarkplatformversion)
+* [`Benchmark.platform.toString`](#benchmarkplatformtostring)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.support`
+* [`Benchmark.support`](#benchmarksupport)
+* [`Benchmark.support.air`](#benchmarksupportair)
+* [`Benchmark.support.argumentsClass`](#benchmarksupportargumentsclass)
+* [`Benchmark.support.browser`](#benchmarksupportbrowser)
+* [`Benchmark.support.charByIndex`](#benchmarksupportcharbyindex)
+* [`Benchmark.support.charByOwnIndex`](#benchmarksupportcharbyownindex)
+* [`Benchmark.support.decompilation`](#benchmarksupportdecompilation)
+* [`Benchmark.support.descriptors`](#benchmarksupportdescriptors)
+* [`Benchmark.support.getAllKeys`](#benchmarksupportgetallkeys)
+* [`Benchmark.support.iteratesOwnLast`](#benchmarksupportiteratesownfirst)
+* [`Benchmark.support.java`](#benchmarksupportjava)
+* [`Benchmark.support.nodeClass`](#benchmarksupportnodeclass)
+* [`Benchmark.support.timeout`](#benchmarksupporttimeout)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.error`
+* [`Benchmark.prototype.error`](#benchmarkprototypeerror)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.stats`
+* [`Benchmark.prototype.stats`](#benchmarkprototypestats)
+* [`Benchmark.prototype.stats.deviation`](#benchmark-statsdeviation)
+* [`Benchmark.prototype.stats.mean`](#benchmark-statsmean)
+* [`Benchmark.prototype.stats.moe`](#benchmark-statsmoe)
+* [`Benchmark.prototype.stats.rme`](#benchmark-statsrme)
+* [`Benchmark.prototype.stats.sample`](#benchmark-statssample)
+* [`Benchmark.prototype.stats.sem`](#benchmark-statssem)
+* [`Benchmark.prototype.stats.variance`](#benchmark-statsvariance)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.times`
+* [`Benchmark.prototype.times`](#benchmarkprototypetimes)
+* [`Benchmark.prototype.times.cycle`](#benchmark-timescycle)
+* [`Benchmark.prototype.times.elapsed`](#benchmark-timeselapsed)
+* [`Benchmark.prototype.times.period`](#benchmark-timesperiod)
+* [`Benchmark.prototype.times.timeStamp`](#benchmark-timestimestamp)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Deferred`
+* [`Benchmark.Deferred`](#benchmarkdeferredclone)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Deferred.prototype`
+* [`Benchmark.Deferred.prototype.benchmark`](#benchmarkdeferredprototypebenchmark)
+* [`Benchmark.Deferred.prototype.cycles`](#benchmarkdeferredprototypecycles)
+* [`Benchmark.Deferred.prototype.elapsed`](#benchmarkdeferredprototypeelapsed)
+* [`Benchmark.Deferred.prototype.resolve`](#benchmarkdeferredprototyperesolve)
+* [`Benchmark.Deferred.prototype.timeStamp`](#benchmarkdeferredprototypetimestamp)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event`
+* [`Benchmark.Event`](#benchmarkeventtype)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype`
+* [`Benchmark.Event.prototype.aborted`](#benchmarkeventprototypeaborted)
+* [`Benchmark.Event.prototype.cancelled`](#benchmarkeventprototypecancelled)
+* [`Benchmark.Event.prototype.result`](#benchmarkeventprototyperesult)
+* [`Benchmark.Event.prototype.timeStamp`](#benchmarkeventprototypetimestamp)
+* [`Benchmark.Event.prototype.type`](#benchmarkeventprototypetype)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype.currentTarget`
+* [`Benchmark.Event.prototype.currentTarget`](#benchmarkeventprototypecurrenttarget)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype.target`
+* [`Benchmark.Event.prototype.target`](#benchmarkeventprototypetarget)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite`
+* [`Benchmark.Suite`](#benchmarksuitename--options)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite.prototype`
+* [`Benchmark.Suite.prototype.aborted`](#benchmarksuiteprototypeaborted)
+* [`Benchmark.Suite.prototype.length`](#benchmarksuiteprototypelength)
+* [`Benchmark.Suite.prototype.running`](#benchmarksuiteprototyperunning)
+* [`Benchmark.Suite.prototype.abort`](#benchmarksuiteprototypeabort)
+* [`Benchmark.Suite.prototype.add`](#benchmarksuiteprototypeaddname-fn--options)
+* [`Benchmark.Suite.prototype.clone`](#benchmarksuiteprototypecloneoptions)
+* [`Benchmark.Suite.prototype.emit`](#benchmarkprototypeemittype)
+* [`Benchmark.Suite.prototype.filter`](#benchmarksuiteprototypefiltercallback)
+* [`Benchmark.Suite.prototype.forEach`](#benchmarksuiteprototypeforeachcallback)
+* [`Benchmark.Suite.prototype.indexOf`](#benchmarksuiteprototypeindexofvalue)
+* [`Benchmark.Suite.prototype.invoke`](#benchmarksuiteprototypeinvokename--arg1-arg2-)
+* [`Benchmark.Suite.prototype.join`](#benchmarksuiteprototypejoinseparator-)
+* [`Benchmark.Suite.prototype.listeners`](#benchmarkprototypelistenerstype)
+* [`Benchmark.Suite.prototype.map`](#benchmarksuiteprototypemapcallback)
+* [`Benchmark.Suite.prototype.off`](#benchmarkprototypeofftype-listener)
+* [`Benchmark.Suite.prototype.on`](#benchmarkprototypeontype-listener)
+* [`Benchmark.Suite.prototype.pluck`](#benchmarksuiteprototypepluckproperty)
+* [`Benchmark.Suite.prototype.pop`](#benchmarksuiteprototypepop)
+* [`Benchmark.Suite.prototype.push`](#benchmarksuiteprototypepush)
+* [`Benchmark.Suite.prototype.reduce`](#benchmarksuiteprototypereducecallback-accumulator)
+* [`Benchmark.Suite.prototype.reset`](#benchmarksuiteprototypereset)
+* [`Benchmark.Suite.prototype.reverse`](#benchmarksuiteprototypereverse)
+* [`Benchmark.Suite.prototype.run`](#benchmarksuiteprototyperunoptions)
+* [`Benchmark.Suite.prototype.shift`](#benchmarksuiteprototypeshift)
+* [`Benchmark.Suite.prototype.slice`](#benchmarksuiteprototypeslicestart-end)
+* [`Benchmark.Suite.prototype.sort`](#benchmarksuiteprototypesortcomparefnnull)
+* [`Benchmark.Suite.prototype.splice`](#benchmarksuiteprototypesplicestart-deletecount--val1-val2-)
+* [`Benchmark.Suite.prototype.unshift`](#benchmarksuiteprototypeunshift)
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite.options`
+* [`Benchmark.Suite.options`](#benchmarksuiteoptions)
+* [`Benchmark.Suite.options.name`](#benchmarksuiteoptionsname)
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+
+<!-- div -->
+
+## `Benchmark`
+
+<!-- div -->
+
+### <a id="benchmarkname-fn--options"></a>`Benchmark(name, fn [, options={}])`
+<a href="#benchmarkname-fn--options">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L404 "View in source") [Ⓣ][1]
+
+The Benchmark constructor.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the benchmark.
+2. `fn` *(Function|String)*: The test to benchmark.
+3. `[options={}]` *(Object)*: Options object.
+
+#### Example
+```js
+// basic usage (the `new` operator is optional)
+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 name and options
+var bench = new Benchmark('foo', {
+
+ // a flag to indicate the benchmark is deferred
+ 'defer': true,
+
+ // benchmark test function
+ 'fn': function(deferred) {
+ // call resolve() when the deferred test is finished
+ deferred.resolve();
+ }
+});
+
+// or options only
+var bench = new Benchmark({
+
+ // benchmark name
+ 'name': 'foo',
+
+ // benchmark test as a string
+ 'fn': '[1,2,3,4].sort()'
+});
+
+// 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
+});
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkversion"></a>`Benchmark.version`
+<a href="#benchmarkversion">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3268 "View in source") [Ⓣ][1]
+
+*(String)*: The semantic version number.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkdeepclonevalue"></a>`Benchmark.deepClone(value)`
+<a href="#benchmarkdeepclonevalue">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1225 "View in source") [Ⓣ][1]
+
+A deep clone utility.
+
+#### Arguments
+1. `value` *(Mixed)*: The value to clone.
+
+#### Returns
+*(Mixed)*: The cloned value.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkeachobject-callback-thisarg"></a>`Benchmark.each(object, callback, thisArg)`
+<a href="#benchmarkeachobject-callback-thisarg">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1400 "View in source") [Ⓣ][1]
+
+An iteration utility for arrays and objects. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `object` *(Array|Object)*: The object to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array, Object)*: Returns the object iterated over.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkextenddestination--source"></a>`Benchmark.extend(destination [, source={}])`
+<a href="#benchmarkextenddestination--source">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1446 "View in source") [Ⓣ][1]
+
+Copies enumerable properties from the source(s) object to the destination object.
+
+#### Arguments
+1. `destination` *(Object)*: The destination object.
+2. `[source={}]` *(Object)*: The source object.
+
+#### Returns
+*(Object)*: The destination object.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkfilterarray-callback-thisarg"></a>`Benchmark.filter(array, callback, thisArg)`
+<a href="#benchmarkfilterarray-callback-thisarg">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1485 "View in source") [Ⓣ][1]
+
+A generic `Array#filter` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function|String)*: The function/alias called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: A new array of values that passed callback filter.
+
+#### Example
+```js
+// 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');
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkforeacharray-callback-thisarg"></a>`Benchmark.forEach(array, callback, thisArg)`
+<a href="#benchmarkforeacharray-callback-thisarg">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1518 "View in source") [Ⓣ][1]
+
+A generic `Array#forEach` like method. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: Returns the array iterated over.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkformatnumbernumber"></a>`Benchmark.formatNumber(number)`
+<a href="#benchmarkformatnumbernumber">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1557 "View in source") [Ⓣ][1]
+
+Converts a number to a more readable comma-separated string representation.
+
+#### Arguments
+1. `number` *(Number)*: The number to convert.
+
+#### Returns
+*(String)*: The more readable string representation.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkforownobject-callback-thisarg"></a>`Benchmark.forOwn(object, callback, thisArg)`
+<a href="#benchmarkforownobject-callback-thisarg">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1545 "View in source") [Ⓣ][1]
+
+Iterates over an object's own properties, executing the `callback` for each. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `object` *(Object)*: The object to iterate over.
+2. `callback` *(Function)*: The function executed per own property.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Object)*: Returns the object iterated over.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkhaskeyobject-key"></a>`Benchmark.hasKey(object, key)`
+<a href="#benchmarkhaskeyobject-key">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1572 "View in source") [Ⓣ][1]
+
+Checks if an object has the specified key as a direct property.
+
+#### Arguments
+1. `object` *(Object)*: The object to check.
+2. `key` *(String)*: The key to check for.
+
+#### Returns
+*(Boolean)*: Returns `true` if key is a direct property, else `false`.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkindexofarray-value--fromindex0"></a>`Benchmark.indexOf(array, value [, fromIndex=0])`
+<a href="#benchmarkindexofarray-value--fromindex0">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1608 "View in source") [Ⓣ][1]
+
+A generic `Array#indexOf` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `value` *(Mixed)*: The value to search for.
+3. `[fromIndex=0]` *(Number)*: The index to start searching from.
+
+#### Returns
+*(Number)*: The index of the matched value or `-1`.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkinterpolatestring-object"></a>`Benchmark.interpolate(string, object)`
+<a href="#benchmarkinterpolatestring-object">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1630 "View in source") [Ⓣ][1]
+
+Modify a string by replacing named tokens with matching object property values.
+
+#### Arguments
+1. `string` *(String)*: The string to modify.
+2. `object` *(Object)*: The template object.
+
+#### Returns
+*(String)*: The modified string.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkinvokebenches-name--arg1-arg2-"></a>`Benchmark.invoke(benches, name [, arg1, arg2, ...])`
+<a href="#benchmarkinvokebenches-name--arg1-arg2-">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1677 "View in source") [Ⓣ][1]
+
+Invokes a method on all items in an array.
+
+#### Arguments
+1. `benches` *(Array)*: Array of benchmarks to iterate over.
+2. `name` *(String|Object)*: The name of the method to invoke OR options object.
+3. `[arg1, arg2, ...]` *(Mixed)*: Arguments to invoke the method with.
+
+#### Returns
+*(Array)*: A new array of values returned from each method invoked.
+
+#### Example
+```js
+// 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
+});
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkjoinobject--separator1--separator2:"></a>`Benchmark.join(object [, separator1=',', separator2=': '])`
+<a href="#benchmarkjoinobject--separator1--separator2:">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1831 "View in source") [Ⓣ][1]
+
+Creates a string of joined array values or object key-value pairs.
+
+#### Arguments
+1. `object` *(Array|Object)*: The object to operate on.
+2. `[separator1=',']` *(String)*: The separator used between key-value pairs.
+3. `[separator2=': ']` *(String)*: The separator used between keys and values.
+
+#### Returns
+*(String)*: The joined result.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkmaparray-callback-thisarg"></a>`Benchmark.map(array, callback, thisArg)`
+<a href="#benchmarkmaparray-callback-thisarg">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1853 "View in source") [Ⓣ][1]
+
+A generic `Array#map` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: A new array of values returned by the callback.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkpluckarray-property"></a>`Benchmark.pluck(array, property)`
+<a href="#benchmarkpluckarray-property">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1869 "View in source") [Ⓣ][1]
+
+Retrieves the value of a specified property from all items in an array.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `property` *(String)*: The property to pluck.
+
+#### Returns
+*(Array)*: A new array of property values.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkreducearray-callback-accumulator"></a>`Benchmark.reduce(array, callback, accumulator)`
+<a href="#benchmarkreducearray-callback-accumulator">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1885 "View in source") [Ⓣ][1]
+
+A generic `Array#reduce` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `accumulator` *(Mixed)*: Initial value of the accumulator.
+
+#### Returns
+*(Mixed)*: The accumulator.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype`
+
+<!-- div -->
+
+### <a id="benchmarkprototypeaborted"></a>`Benchmark.prototype.aborted`
+<a href="#benchmarkprototypeaborted">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3378 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the benchmark is aborted.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypecompiled"></a>`Benchmark.prototype.compiled`
+<a href="#benchmarkprototypecompiled">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3354 "View in source") [Ⓣ][1]
+
+*(Function, String)*: The compiled test function.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypecount"></a>`Benchmark.prototype.count`
+<a href="#benchmarkprototypecount">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3330 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of times a test was executed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypecycles"></a>`Benchmark.prototype.cycles`
+<a href="#benchmarkprototypecycles">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3338 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of cycles performed while benchmarking.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypefn"></a>`Benchmark.prototype.fn`
+<a href="#benchmarkprototypefn">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3370 "View in source") [Ⓣ][1]
+
+*(Function, String)*: The test to benchmark.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypehz"></a>`Benchmark.prototype.hz`
+<a href="#benchmarkprototypehz">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3346 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of executions per second.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototyperunning"></a>`Benchmark.prototype.running`
+<a href="#benchmarkprototyperunning">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3386 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the benchmark is running.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypesetup"></a>`Benchmark.prototype.setup`
+<a href="#benchmarkprototypesetup">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3449 "View in source") [Ⓣ][1]
+
+*(Function, String)*: Compiled into the test and executed immediately **before** the test loop.
+
+#### Example
+```js
+// basic usage
+var bench = Benchmark({
+ 'setup': function() {
+ var c = this.count,
+ element = document.getElementById('container');
+ while (c--) {
+ element.appendChild(document.createElement('div'));
+ }
+ },
+ 'fn': function() {
+ element.removeChild(element.lastChild);
+ }
+});
+
+// compiles to something like:
+var c = this.count,
+ element = document.getElementById('container');
+while (c--) {
+ element.appendChild(document.createElement('div'));
+}
+var start = new Date;
+while (count--) {
+ element.removeChild(element.lastChild);
+}
+var end = new Date - start;
+
+// or using strings
+var bench = Benchmark({
+ 'setup': '\
+ var a = 0;\n\
+ (function() {\n\
+ (function() {\n\
+ (function() {',
+ 'fn': 'a += 1;',
+ 'teardown': '\
+ }())\n\
+ }())\n\
+ }())'
+});
+
+// compiles to something like:
+var a = 0;
+(function() {
+ (function() {
+ (function() {
+ var start = new Date;
+ while (count--) {
+ a += 1;
+ }
+ var end = new Date - start;
+ }())
+ }())
+}())
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeteardown"></a>`Benchmark.prototype.teardown`
+<a href="#benchmarkprototypeteardown">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3457 "View in source") [Ⓣ][1]
+
+*(Function, String)*: Compiled into the test and executed immediately **after** the test loop.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeabort"></a>`Benchmark.prototype.abort()`
+<a href="#benchmarkprototypeabort">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2218 "View in source") [Ⓣ][1]
+
+Aborts the benchmark without recording times.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypecloneoptions"></a>`Benchmark.prototype.clone(options)`
+<a href="#benchmarkprototypecloneoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2257 "View in source") [Ⓣ][1]
+
+Creates a new benchmark using the same test and options.
+
+#### Arguments
+1. `options` *(Object)*: Options object to overwrite cloned options.
+
+#### Returns
+*(Object)*: The new benchmark instance.
+
+#### Example
+```js
+var bizarro = bench.clone({
+ 'name': 'doppelganger'
+});
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypecompareother"></a>`Benchmark.prototype.compare(other)`
+<a href="#benchmarkprototypecompareother">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2280 "View in source") [Ⓣ][1]
+
+Determines if a benchmark is faster than another.
+
+#### Arguments
+1. `other` *(Object)*: The benchmark to compare.
+
+#### Returns
+*(Number)*: Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeemittype"></a>`Benchmark.Suite.prototype.emit(type)`
+<a href="#benchmarkprototypeemittype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2095 "View in source") [Ⓣ][1]
+
+Executes all registered listeners of the specified event type.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type or object.
+
+#### Returns
+*(Mixed)*: Returns the return value of the last listener executed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypelistenerstype"></a>`Benchmark.Suite.prototype.listeners(type)`
+<a href="#benchmarkprototypelistenerstype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2125 "View in source") [Ⓣ][1]
+
+Returns an array of event listeners for a given type that can be manipulated to add or remove listeners.
+
+#### Arguments
+1. `type` *(String)*: The event type.
+
+#### Returns
+*(Array)*: The listeners array.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeofftype-listener"></a>`Benchmark.Suite.prototype.off([type, listener])`
+<a href="#benchmarkprototypeofftype-listener">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2158 "View in source") [Ⓣ][1]
+
+Unregisters a listener for the specified event type(s), or unregisters all listeners for the specified event type(s), or unregisters all listeners for all event types.
+
+#### Arguments
+1. `[type]` *(String)*: The event type.
+2. `[listener]` *(Function)*: The function to unregister.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// unregister a listener for an event type
+bench.off('cycle', listener);
+
+// unregister a listener for multiple event types
+bench.off('start cycle', listener);
+
+// unregister all listeners for an event type
+bench.off('cycle');
+
+// unregister all listeners for multiple event types
+bench.off('start cycle complete');
+
+// unregister all listeners for all event types
+bench.off();
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeontype-listener"></a>`Benchmark.Suite.prototype.on(type, listener)`
+<a href="#benchmarkprototypeontype-listener">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2197 "View in source") [Ⓣ][1]
+
+Registers a listener for the specified event type(s).
+
+#### Arguments
+1. `type` *(String)*: The event type.
+2. `listener` *(Function)*: The function to register.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// register a listener for an event type
+bench.on('cycle', listener);
+
+// register a listener for multiple event types
+bench.on('start cycle', listener);
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypereset"></a>`Benchmark.prototype.reset()`
+<a href="#benchmarkprototypereset">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2334 "View in source") [Ⓣ][1]
+
+Reset properties and abort if running.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototyperunoptions"></a>`Benchmark.prototype.run([options={}])`
+<a href="#benchmarkprototyperunoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3000 "View in source") [Ⓣ][1]
+
+Runs the benchmark.
+
+#### Arguments
+1. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// basic usage
+bench.run();
+
+// or with options
+bench.run({ 'async': true });
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypetostring"></a>`Benchmark.prototype.toString()`
+<a href="#benchmarkprototypetostring">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2405 "View in source") [Ⓣ][1]
+
+Displays relevant benchmark information when coerced to a string.
+
+#### Returns
+*(String)*: A string representation of the benchmark instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.options`
+
+<!-- div -->
+
+### <a id="benchmarkoptions"></a>`Benchmark.options`
+<a href="#benchmarkoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3049 "View in source") [Ⓣ][1]
+
+*(Object)*: The default options copied by benchmark instances.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsasync"></a>`Benchmark.options.async`
+<a href="#benchmarkoptionsasync">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3058 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate that benchmark cycles will execute asynchronously by default.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsdefer"></a>`Benchmark.options.defer`
+<a href="#benchmarkoptionsdefer">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3066 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate that the benchmark clock is deferred.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsdelay"></a>`Benchmark.options.delay`
+<a href="#benchmarkoptionsdelay">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3073 "View in source") [Ⓣ][1]
+
+*(Number)*: The delay between test cycles *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsid"></a>`Benchmark.options.id`
+<a href="#benchmarkoptionsid">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3082 "View in source") [Ⓣ][1]
+
+*(String)*: Displayed by Benchmark#toString when a `name` is not available *(auto-generated if absent)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsinitcount"></a>`Benchmark.options.initCount`
+<a href="#benchmarkoptionsinitcount">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3090 "View in source") [Ⓣ][1]
+
+*(Number)*: The default number of times to execute a test on a benchmark's first cycle.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsmaxtime"></a>`Benchmark.options.maxTime`
+<a href="#benchmarkoptionsmaxtime">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3100 "View in source") [Ⓣ][1]
+
+*(Number)*: The maximum time a benchmark is allowed to run before finishing *(secs)*. Note: Cycle delays aren't counted toward the maximum time.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsminsamples"></a>`Benchmark.options.minSamples`
+<a href="#benchmarkoptionsminsamples">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3108 "View in source") [Ⓣ][1]
+
+*(Number)*: The minimum sample size required to perform statistical analysis.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsmintime"></a>`Benchmark.options.minTime`
+<a href="#benchmarkoptionsmintime">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3116 "View in source") [Ⓣ][1]
+
+*(Number)*: The time needed to reduce the percent uncertainty of measurement to `1`% *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsname"></a>`Benchmark.options.name`
+<a href="#benchmarkoptionsname">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3124 "View in source") [Ⓣ][1]
+
+*(String)*: The name of the benchmark.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsonabort"></a>`Benchmark.options.onAbort`
+<a href="#benchmarkoptionsonabort">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3132 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark is aborted.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsoncomplete"></a>`Benchmark.options.onComplete`
+<a href="#benchmarkoptionsoncomplete">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3140 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark completes running.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsoncycle"></a>`Benchmark.options.onCycle`
+<a href="#benchmarkoptionsoncycle">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3148 "View in source") [Ⓣ][1]
+
+An event listener called after each run cycle.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsonerror"></a>`Benchmark.options.onError`
+<a href="#benchmarkoptionsonerror">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3156 "View in source") [Ⓣ][1]
+
+An event listener called when a test errors.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsonreset"></a>`Benchmark.options.onReset`
+<a href="#benchmarkoptionsonreset">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3164 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark is reset.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkoptionsonstart"></a>`Benchmark.options.onStart`
+<a href="#benchmarkoptionsonstart">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3172 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark starts running.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.platform`
+
+<!-- div -->
+
+### <a id="benchmarkplatform"></a>`Benchmark.platform`
+<a href="#benchmarkplatform">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3183 "View in source") [Ⓣ][1]
+
+*(Object)*: Platform object with properties describing things like browser name, version, and operating system.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformdescription"></a>`Benchmark.platform.description`
+<a href="#benchmarkplatformdescription">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3191 "View in source") [Ⓣ][1]
+
+*(String)*: The platform description.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformlayout"></a>`Benchmark.platform.layout`
+<a href="#benchmarkplatformlayout">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3199 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the browser layout engine.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformmanufacturer"></a>`Benchmark.platform.manufacturer`
+<a href="#benchmarkplatformmanufacturer">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3223 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the product's manufacturer.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformname"></a>`Benchmark.platform.name`
+<a href="#benchmarkplatformname">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3215 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the browser/environment.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformos"></a>`Benchmark.platform.os`
+<a href="#benchmarkplatformos">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3231 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the operating system.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformprerelease"></a>`Benchmark.platform.prerelease`
+<a href="#benchmarkplatformprerelease">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3239 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The alpha/beta release indicator.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformproduct"></a>`Benchmark.platform.product`
+<a href="#benchmarkplatformproduct">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3207 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the product hosting the browser.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformversion"></a>`Benchmark.platform.version`
+<a href="#benchmarkplatformversion">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3247 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The browser/environment version.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkplatformtostring"></a>`Benchmark.platform.toString()`
+<a href="#benchmarkplatformtostring">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3256 "View in source") [Ⓣ][1]
+
+Return platform description when the platform object is coerced to a string.
+
+#### Returns
+*(String)*: The platform description.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.support`
+
+<!-- div -->
+
+### <a id="benchmarksupport"></a>`Benchmark.support`
+<a href="#benchmarksupport">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L135 "View in source") [Ⓣ][1]
+
+*(Object)*: An object used to flag environments/features.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportair"></a>`Benchmark.support.air`
+<a href="#benchmarksupportair">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L145 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect Adobe AIR.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportargumentsclass"></a>`Benchmark.support.argumentsClass`
+<a href="#benchmarksupportargumentsclass">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L153 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if `arguments` objects have the correct internal [[Class]] value.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportbrowser"></a>`Benchmark.support.browser`
+<a href="#benchmarksupportbrowser">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L161 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if in a browser environment.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportcharbyindex"></a>`Benchmark.support.charByIndex`
+<a href="#benchmarksupportcharbyindex">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L169 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if strings support accessing characters by index.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportcharbyownindex"></a>`Benchmark.support.charByOwnIndex`
+<a href="#benchmarksupportcharbyownindex">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L179 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if strings have indexes as own properties.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportdecompilation"></a>`Benchmark.support.decompilation`
+<a href="#benchmarksupportdecompilation">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L207 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if functions support decompilation.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportdescriptors"></a>`Benchmark.support.descriptors`
+<a href="#benchmarksupportdescriptors">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L228 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect ES5+ property descriptor API.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportgetallkeys"></a>`Benchmark.support.getAllKeys`
+<a href="#benchmarksupportgetallkeys">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L242 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect ES5+ Object.getOwnPropertyNames().
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportiteratesownfirst"></a>`Benchmark.support.iteratesOwnFirst`
+<a href="#benchmarksupportiteratesownfirst">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L255 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if own properties are iterated before inherited properties *(all but IE < `9`)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportjava"></a>`Benchmark.support.java`
+<a href="#benchmarksupportjava">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L190 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if Java is enabled/exposed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupportnodeclass"></a>`Benchmark.support.nodeClass`
+<a href="#benchmarksupportnodeclass">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L272 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if a node's [[Class]] is resolvable *(all but IE < `9`)* and that the JS engine errors when attempting to coerce an object to a string without a `toString` property value of `typeof` "function".
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksupporttimeout"></a>`Benchmark.support.timeout`
+<a href="#benchmarksupporttimeout">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L198 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if the Timers API exists.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.error`
+
+<!-- div -->
+
+### <a id="benchmarkprototypeerror"></a>`Benchmark.prototype.error`
+<a href="#benchmarkprototypeerror">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3362 "View in source") [Ⓣ][1]
+
+*(Object)*: The error object if the test failed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.stats`
+
+<!-- div -->
+
+### <a id="benchmarkprototypestats"></a>`Benchmark.prototype.stats`
+<a href="#benchmarkprototypestats">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3465 "View in source") [Ⓣ][1]
+
+*(Object)*: An object of stats including mean, margin or error, and standard deviation.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statsdeviation"></a>`Benchmark.prototype.stats.deviation`
+<a href="#benchmark-statsdeviation">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3497 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample standard deviation.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statsmean"></a>`Benchmark.prototype.stats.mean`
+<a href="#benchmark-statsmean">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3505 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample arithmetic mean.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statsmoe"></a>`Benchmark.prototype.stats.moe`
+<a href="#benchmark-statsmoe">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3473 "View in source") [Ⓣ][1]
+
+*(Number)*: The margin of error.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statsrme"></a>`Benchmark.prototype.stats.rme`
+<a href="#benchmark-statsrme">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3481 "View in source") [Ⓣ][1]
+
+*(Number)*: The relative margin of error *(expressed as a percentage of the mean)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statssample"></a>`Benchmark.prototype.stats.sample`
+<a href="#benchmark-statssample">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3513 "View in source") [Ⓣ][1]
+
+*(Array)*: The array of sampled periods.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statssem"></a>`Benchmark.prototype.stats.sem`
+<a href="#benchmark-statssem">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3489 "View in source") [Ⓣ][1]
+
+*(Number)*: The standard error of the mean.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-statsvariance"></a>`Benchmark.prototype.stats.variance`
+<a href="#benchmark-statsvariance">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3521 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample variance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.prototype.times`
+
+<!-- div -->
+
+### <a id="benchmarkprototypetimes"></a>`Benchmark.prototype.times`
+<a href="#benchmarkprototypetimes">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3530 "View in source") [Ⓣ][1]
+
+*(Object)*: An object of timing data including cycle, elapsed, period, start, and stop.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-timescycle"></a>`Benchmark.prototype.times.cycle`
+<a href="#benchmark-timescycle">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3538 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the last cycle *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-timeselapsed"></a>`Benchmark.prototype.times.elapsed`
+<a href="#benchmark-timeselapsed">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3546 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the benchmark *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-timesperiod"></a>`Benchmark.prototype.times.period`
+<a href="#benchmark-timesperiod">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3554 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to execute the test once *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmark-timestimestamp"></a>`Benchmark.prototype.times.timeStamp`
+<a href="#benchmark-timestimestamp">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3562 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the benchmark started *(ms)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Deferred`
+
+<!-- div -->
+
+### <a id="benchmarkdeferredclone"></a>`Benchmark.Deferred(clone)`
+<a href="#benchmarkdeferredclone">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L445 "View in source") [Ⓣ][1]
+
+The Deferred constructor.
+
+#### Arguments
+1. `clone` *(Object)*: The cloned benchmark instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Deferred.prototype`
+
+<!-- div -->
+
+### <a id="benchmarkdeferredprototypebenchmark"></a>`Benchmark.Deferred.prototype.benchmark`
+<a href="#benchmarkdeferredprototypebenchmark">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3606 "View in source") [Ⓣ][1]
+
+*(Object)*: The deferred benchmark instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkdeferredprototypecycles"></a>`Benchmark.Deferred.prototype.cycles`
+<a href="#benchmarkdeferredprototypecycles">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3614 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of deferred cycles performed while benchmarking.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkdeferredprototypeelapsed"></a>`Benchmark.Deferred.prototype.elapsed`
+<a href="#benchmarkdeferredprototypeelapsed">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3622 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the deferred benchmark *(secs)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkdeferredprototyperesolve"></a>`Benchmark.Deferred.prototype.resolve`
+<a href="#benchmarkdeferredprototyperesolve">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1188 "View in source") [Ⓣ][1]
+
+*(Unknown)*: Handles cycling/completing the deferred benchmark.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkdeferredprototypetimestamp"></a>`Benchmark.Deferred.prototype.timeStamp`
+<a href="#benchmarkdeferredprototypetimestamp">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3630 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the deferred benchmark started *(ms)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event`
+
+<!-- div -->
+
+### <a id="benchmarkeventtype"></a>`Benchmark.Event(type)`
+<a href="#benchmarkeventtype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L461 "View in source") [Ⓣ][1]
+
+The Event constructor.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype`
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypeaborted"></a>`Benchmark.Event.prototype.aborted`
+<a href="#benchmarkeventprototypeaborted">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3646 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the emitters listener iteration is aborted.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypecancelled"></a>`Benchmark.Event.prototype.cancelled`
+<a href="#benchmarkeventprototypecancelled">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3654 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the default action is cancelled.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkeventprototyperesult"></a>`Benchmark.Event.prototype.result`
+<a href="#benchmarkeventprototyperesult">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3670 "View in source") [Ⓣ][1]
+
+*(Mixed)*: The return value of the last executed listener.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypetimestamp"></a>`Benchmark.Event.prototype.timeStamp`
+<a href="#benchmarkeventprototypetimestamp">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3686 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the event was created *(ms)*.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypetype"></a>`Benchmark.Event.prototype.type`
+<a href="#benchmarkeventprototypetype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3694 "View in source") [Ⓣ][1]
+
+*(String)*: The event type.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype.currentTarget`
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypecurrenttarget"></a>`Benchmark.Event.prototype.currentTarget`
+<a href="#benchmarkeventprototypecurrenttarget">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3662 "View in source") [Ⓣ][1]
+
+*(Object)*: The object whose listeners are currently being processed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Event.prototype.target`
+
+<!-- div -->
+
+### <a id="benchmarkeventprototypetarget"></a>`Benchmark.Event.prototype.target`
+<a href="#benchmarkeventprototypetarget">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3678 "View in source") [Ⓣ][1]
+
+*(Object)*: The object to which the event was originally emitted.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite`
+
+<!-- div -->
+
+### <a id="benchmarksuitename--options"></a>`Benchmark.Suite(name [, options={}])`
+<a href="#benchmarksuitename--options">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L507 "View in source") [Ⓣ][1]
+
+The Suite constructor.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the suite.
+2. `[options={}]` *(Object)*: Options object.
+
+#### Example
+```js
+// basic usage (the `new` operator is optional)
+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
+});
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite.prototype`
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeaborted"></a>`Benchmark.Suite.prototype.aborted`
+<a href="#benchmarksuiteprototypeaborted">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3735 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the suite is aborted.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypelength"></a>`Benchmark.Suite.prototype.length`
+<a href="#benchmarksuiteprototypelength">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3727 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of benchmarks in the suite.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototyperunning"></a>`Benchmark.Suite.prototype.running`
+<a href="#benchmarksuiteprototyperunning">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3743 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the suite is running.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeabort"></a>`Benchmark.Suite.prototype.abort()`
+<a href="#benchmarksuiteprototypeabort">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1902 "View in source") [Ⓣ][1]
+
+Aborts all benchmarks in the suite.
+
+#### Returns
+*(Object)*: The suite instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeaddname-fn--options"></a>`Benchmark.Suite.prototype.add(name, fn [, options={}])`
+<a href="#benchmarksuiteprototypeaddname-fn--options">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1962 "View in source") [Ⓣ][1]
+
+Adds a test to the benchmark suite.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the benchmark.
+2. `fn` *(Function|String)*: The test to benchmark.
+3. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// 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
+});
+
+// or name and options
+suite.add('foo', {
+ 'fn': fn,
+ 'onCycle': onCycle,
+ 'onComplete': onComplete
+});
+
+// or options only
+suite.add({
+ 'name': 'foo',
+ 'fn': fn,
+ 'onCycle': onCycle,
+ 'onComplete': onComplete
+});
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypecloneoptions"></a>`Benchmark.Suite.prototype.clone(options)`
+<a href="#benchmarksuiteprototypecloneoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1981 "View in source") [Ⓣ][1]
+
+Creates a new suite with cloned benchmarks.
+
+#### Arguments
+1. `options` *(Object)*: Options object to overwrite cloned options.
+
+#### Returns
+*(Object)*: The new suite instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeemittype"></a>`Benchmark.Suite.prototype.emit(type)`
+<a href="#benchmarkprototypeemittype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2095 "View in source") [Ⓣ][1]
+
+Executes all registered listeners of the specified event type.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type or object.
+
+#### Returns
+*(Mixed)*: Returns the return value of the last listener executed.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypefiltercallback"></a>`Benchmark.Suite.prototype.filter(callback)`
+<a href="#benchmarksuiteprototypefiltercallback">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2004 "View in source") [Ⓣ][1]
+
+An `Array#filter` like method.
+
+#### Arguments
+1. `callback` *(Function|String)*: The function/alias called per iteration.
+
+#### Returns
+*(Object)*: A new suite of benchmarks that passed callback filter.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeforeachcallback"></a>`Benchmark.Suite.prototype.forEach(callback)`
+<a href="#benchmarksuiteprototypeforeachcallback">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3753 "View in source") [Ⓣ][1]
+
+An `Array#forEach` like method. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+
+#### Returns
+*(Object)*: The suite iterated over.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeindexofvalue"></a>`Benchmark.Suite.prototype.indexOf(value)`
+<a href="#benchmarksuiteprototypeindexofvalue">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3762 "View in source") [Ⓣ][1]
+
+An `Array#indexOf` like method.
+
+#### Arguments
+1. `value` *(Mixed)*: The value to search for.
+
+#### Returns
+*(Number)*: The index of the matched value or `-1`.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeinvokename--arg1-arg2-"></a>`Benchmark.Suite.prototype.invoke(name [, arg1, arg2, ...])`
+<a href="#benchmarksuiteprototypeinvokename--arg1-arg2-">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3772 "View in source") [Ⓣ][1]
+
+Invokes a method on all benchmarks in the suite.
+
+#### Arguments
+1. `name` *(String|Object)*: The name of the method to invoke OR options object.
+2. `[arg1, arg2, ...]` *(Mixed)*: Arguments to invoke the method with.
+
+#### Returns
+*(Array)*: A new array of values returned from each method invoked.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypejoinseparator-"></a>`Benchmark.Suite.prototype.join([separator=','])`
+<a href="#benchmarksuiteprototypejoinseparator-">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3781 "View in source") [Ⓣ][1]
+
+Converts the suite of benchmarks to a string.
+
+#### Arguments
+1. `[separator=',']` *(String)*: A string to separate each element of the array.
+
+#### Returns
+*(String)*: The string.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypelistenerstype"></a>`Benchmark.Suite.prototype.listeners(type)`
+<a href="#benchmarkprototypelistenerstype">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2125 "View in source") [Ⓣ][1]
+
+Returns an array of event listeners for a given type that can be manipulated to add or remove listeners.
+
+#### Arguments
+1. `type` *(String)*: The event type.
+
+#### Returns
+*(Array)*: The listeners array.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypemapcallback"></a>`Benchmark.Suite.prototype.map(callback)`
+<a href="#benchmarksuiteprototypemapcallback">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3790 "View in source") [Ⓣ][1]
+
+An `Array#map` like method.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+
+#### Returns
+*(Array)*: A new array of values returned by the callback.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeofftype-listener"></a>`Benchmark.Suite.prototype.off([type, listener])`
+<a href="#benchmarkprototypeofftype-listener">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2158 "View in source") [Ⓣ][1]
+
+Unregisters a listener for the specified event type(s), or unregisters all listeners for the specified event type(s), or unregisters all listeners for all event types.
+
+#### Arguments
+1. `[type]` *(String)*: The event type.
+2. `[listener]` *(Function)*: The function to unregister.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// unregister a listener for an event type
+bench.off('cycle', listener);
+
+// unregister a listener for multiple event types
+bench.off('start cycle', listener);
+
+// unregister all listeners for an event type
+bench.off('cycle');
+
+// unregister all listeners for multiple event types
+bench.off('start cycle complete');
+
+// unregister all listeners for all event types
+bench.off();
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarkprototypeontype-listener"></a>`Benchmark.Suite.prototype.on(type, listener)`
+<a href="#benchmarkprototypeontype-listener">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2197 "View in source") [Ⓣ][1]
+
+Registers a listener for the specified event type(s).
+
+#### Arguments
+1. `type` *(String)*: The event type.
+2. `listener` *(Function)*: The function to register.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+```js
+// register a listener for an event type
+bench.on('cycle', listener);
+
+// register a listener for multiple event types
+bench.on('start cycle', listener);
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypepluckproperty"></a>`Benchmark.Suite.prototype.pluck(property)`
+<a href="#benchmarksuiteprototypepluckproperty">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3799 "View in source") [Ⓣ][1]
+
+Retrieves the value of a specified property from all benchmarks in the suite.
+
+#### Arguments
+1. `property` *(String)*: The property to pluck.
+
+#### Returns
+*(Array)*: A new array of property values.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypepop"></a>`Benchmark.Suite.prototype.pop()`
+<a href="#benchmarksuiteprototypepop">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3807 "View in source") [Ⓣ][1]
+
+Removes the last benchmark from the suite and returns it.
+
+#### Returns
+*(Mixed)*: The removed benchmark.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypepush"></a>`Benchmark.Suite.prototype.push()`
+<a href="#benchmarksuiteprototypepush">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3815 "View in source") [Ⓣ][1]
+
+Appends benchmarks to the suite.
+
+#### Returns
+*(Number)*: The suite's new length.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypereducecallback-accumulator"></a>`Benchmark.Suite.prototype.reduce(callback, accumulator)`
+<a href="#benchmarksuiteprototypereducecallback-accumulator">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3834 "View in source") [Ⓣ][1]
+
+An `Array#reduce` like method.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+2. `accumulator` *(Mixed)*: Initial value of the accumulator.
+
+#### Returns
+*(Mixed)*: The accumulator.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypereset"></a>`Benchmark.Suite.prototype.reset()`
+<a href="#benchmarksuiteprototypereset">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2019 "View in source") [Ⓣ][1]
+
+Resets all benchmarks in the suite.
+
+#### Returns
+*(Object)*: The suite instance.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypereverse"></a>`Benchmark.Suite.prototype.reverse()`
+<a href="#benchmarksuiteprototypereverse">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L638 "View in source") [Ⓣ][1]
+
+Rearrange the host array's elements in reverse order.
+
+#### Returns
+*(Array)*: The reversed array.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototyperunoptions"></a>`Benchmark.Suite.prototype.run([options={}])`
+<a href="#benchmarksuiteprototyperunoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2056 "View in source") [Ⓣ][1]
+
+Runs the suite.
+
+#### Arguments
+1. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The suite instance.
+
+#### Example
+```js
+// basic usage
+suite.run();
+
+// or with options
+suite.run({ 'async': true, 'queued': true });
+```
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeshift"></a>`Benchmark.Suite.prototype.shift()`
+<a href="#benchmarksuiteprototypeshift">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L671 "View in source") [Ⓣ][1]
+
+Removes the first element of the host array and returns it.
+
+#### Returns
+*(Mixed)*: The first element of the array.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeslicestart-end"></a>`Benchmark.Suite.prototype.slice(start, end)`
+<a href="#benchmarksuiteprototypeslicestart-end">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L684 "View in source") [Ⓣ][1]
+
+Creates an array of the host array's elements from the start index up to, but not including, the end index.
+
+#### Arguments
+1. `start` *(Number)*: The starting index.
+2. `end` *(Number)*: The end index.
+
+#### Returns
+*(Array)*: The new array.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypesortcomparefnnull"></a>`Benchmark.Suite.prototype.sort([compareFn=null])`
+<a href="#benchmarksuiteprototypesortcomparefnnull">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3824 "View in source") [Ⓣ][1]
+
+Sorts the benchmarks of the suite.
+
+#### Arguments
+1. `[compareFn=null]` *(Function)*: A function that defines the sort order.
+
+#### Returns
+*(Object)*: The sorted suite.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypesplicestart-deletecount--val1-val2-"></a>`Benchmark.Suite.prototype.splice(start, deleteCount [, val1, val2, ...])`
+<a href="#benchmarksuiteprototypesplicestart-deletecount--val1-val2-">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L714 "View in source") [Ⓣ][1]
+
+Allows removing a range of elements and/or inserting elements into the host array.
+
+#### Arguments
+1. `start` *(Number)*: The start index.
+2. `deleteCount` *(Number)*: The number of elements to delete.
+3. `[val1, val2, ...]` *(Mixed)*: values to insert at the `start` index.
+
+#### Returns
+*(Array)*: An array of removed elements.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteprototypeunshift"></a>`Benchmark.Suite.prototype.unshift()`
+<a href="#benchmarksuiteprototypeunshift">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L749 "View in source") [Ⓣ][1]
+
+Appends arguments to the host array.
+
+#### Returns
+*(Number)*: The new length.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- div -->
+
+## `Benchmark.Suite.options`
+
+<!-- div -->
+
+### <a id="benchmarksuiteoptions"></a>`Benchmark.Suite.options`
+<a href="#benchmarksuiteoptions">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3706 "View in source") [Ⓣ][1]
+
+*(Object)*: The default options copied by suite instances.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- div -->
+
+### <a id="benchmarksuiteoptionsname"></a>`Benchmark.Suite.options.name`
+<a href="#benchmarksuiteoptionsname">#</a> [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3714 "View in source") [Ⓣ][1]
+
+*(String)*: The name of the suite.
+
+* * *
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+<!-- /div -->
+
+
+ [1]: #Benchmark "Jump back to the TOC."
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/parse.php b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/parse.php
new file mode 100644
index 0000000..565b78f
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/doc/parse.php
@@ -0,0 +1,35 @@
+<?php
+
+ // cleanup requested filepath
+ $file = isset($_GET['f']) ? $_GET['f'] : 'benchmark';
+ $file = preg_replace('#(\.*[\/])+#', '', $file);
+ $file .= preg_match('/\.[a-z]+$/', $file) ? '' : '.js';
+
+ // output filename
+ if (isset($_GET['o'])) {
+ $output = $_GET['o'];
+ } else if (isset($_SERVER['argv'][1])) {
+ $output = $_SERVER['argv'][1];
+ } else {
+ $output = basename($file);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ require('../vendor/docdown/docdown.php');
+
+ // generate Markdown
+ $markdown = docdown(array(
+ 'path' => '../' . $file,
+ 'title' => 'Benchmark.js <sup>v1.0.0</sup>',
+ 'url' => 'https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js'
+ ));
+
+ // save to a .md file
+ file_put_contents($output . '.md', $markdown);
+
+ // print
+ header('Content-Type: text/plain;charset=utf-8');
+ echo $markdown . PHP_EOL;
+
+?>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/index.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/index.html
new file mode 100644
index 0000000..040ddb6
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/index.html
@@ -0,0 +1,287 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="description" content="This is just a test document for Benchmark.js.">
+ <title>Benchmark.js test page | jsPerf</title>
+ <!-- http://jsperf.com/benchmark-js-test-page -->
+ <link rel="stylesheet" href="main.css">
+ <script>document.documentElement.className = 'js'</script>
+ <!--[if lt IE 9]><script src="https://raw.github.com/bestiejs/html5.js/master/html5.js"></script><![endif]-->
+ </head>
+ <body>
+ <article>
+ <hgroup>
+ <h1>Benchmark.js test page</h1>
+ <h2>JavaScript performance comparison</h2>
+ </hgroup>
+
+ <p class="meta">
+ Test case created by <a href="http://mathiasbynens.be/">Mathias</a>
+ <time datetime="2010-08-02T18:45:23+02:00" pubdate>40 seconds ago</time>
+ and last updated <time datetime="2010-08-02T18:45:51+02:00">12 seconds ago</time>
+ </p>
+
+ <section>
+ <h2>Info</h2>
+ <p>
+ This is just a test document for Benchmark.js.
+ </p>
+ </section>
+
+ <section id="prep-code">
+ <h2>Preparation code</h2>
+ <pre><code><span
+ class="sc2"><<span class="kw2">div</span>></span>Lorem ipsum<span
+ class="sc2"><<span class="sy0">/</span><span class="kw2">div</span>></span><br><span
+ class="sc2"><<span class="kw2">script</span>></span><br> <span
+ class="kw2">var</span> arr <span class="sy0">=</span> <span class="br0">[</span><span
+ class="nu0">1</span><span class="sy0">,</span> <span class="nu0">5</span><span
+ class="sy0">,</span> <span class="nu0">4</span><span class="sy0">,</span> <span
+ class="nu0">2</span><span class="sy0">,</span> <span class="nu0">3</span><span
+ class="br0">]</span><span class="sy0">;</span><br><br> <span
+ class="kw2">function</span> init<span class="br0">(</span><span
+ class="br0">)</span> <span class="br0">{</span><br> window.<span
+ class="me1">console</span> <span class="sy0">&&</span> console.<span
+ class="me1">log</span><span class="br0">(</span><span class="st0">'init called'</span><span
+ class="br0">)</span><span class="sy0">;</span><br> <span
+ class="br0">}</span><br><span class="sc2"><<span class="sy0">/</span><span
+ class="kw2">script</span>></span><br><span class="sc2"><<span
+ class="kw2">script</span>></span><br>Benchmark.<span class="me1">prototype</span>.<span
+ class="me1">setup</span> <span class="sy0">=</span> <span class="kw2">function</span><span
+ class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><br> window.<span
+ class="me1">foo</span> <span class="sy0">=</span> <span class="nu0">42</span><span
+ class="sy0">;</span><br> <span class="kw2">var</span> x <span
+ class="sy0">=</span> arr<span class="sy0">;</span><br><span class="br0">}</span><span
+ class="sy0">;</span><br><br>Benchmark.<span class="me1">prototype</span>.<span
+ class="me1">teardown</span> <span class="sy0">=</span> <span class="kw2">function</span><span
+ class="br0">(</span><span class="br0">) </span><span class="br0">{</span><br> window.<span
+ class="me1">foo</span> <span class="sy0">=</span> <span class="nu0">24</span><span
+ class="sy0">;</span><br><span class="br0">}</span><span class="sy0">;</span><br><span
+ class="sc2"><<span class="sy0">/</span><span class="kw2">script</span>></span></code></pre>
+ </section>
+
+ <section>
+ <h2>Preparation code output</h2>
+ <div class="user-output">
+ <div>Lorem ipsum</div>
+ </div>
+ </section>
+
+ <section id="runner">
+ <h2>Test runner</h2>
+ <p id="firebug">
+ <strong>Warning! For accurate results, please disable Firebug before running the tests. <a href="http://jsperf.com/faq#firebug-warning">(Why?)</a></strong>
+ </p>
+ <p id="java">
+ <strong>Java applet disabled.</strong>
+ </p>
+ <p id="status">
+ <noscript>
+ <strong>To run the tests, please <a href="http://enable-javascript.com/">enable JavaScript</a> and reload the page.</strong>
+ </noscript>
+ </p>
+ <div id="controls">
+ <button id="run" type="button"></button>
+ </div>
+ <table id="test-table">
+ <caption>Testing in <span id="user-agent"></span></caption>
+ <thead>
+ <tr>
+ <th colspan="2">Test</th>
+ <th title="Operations per second (higher is better)">Ops/sec</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th scope="row" id="title-1">
+ <div>Normal</div>
+ </th>
+ <td class="code">
+ <pre><code>x.<span class="me1">sort</span><span class="br0">(</span><span
+ class="kw2">function</span><span class="br0">(</span>a<span
+ class="sy0">,</span> b<span class="br0">)</span> <span
+ class="br0">{</span><br> <span class="kw1">return</span> a <span
+ class="sy0">-</span> b<span class="sy0">;</span><br><span
+ class="br0">}</span><span class="br0">)</span><span
+ class="sy0">;</span></code></pre>
+ </td>
+ <td id="results-1" class="results"></td>
+ </tr>
+ <tr>
+ <th scope="row" id="title-2">
+ <div>Exit Early</div>
+ </th>
+ <td class="code">
+ <pre><code>x.<span class="me1">sort</span><span class="br0">(</span><span
+ class="kw2">function</span><span class="br0">(</span>a<span
+ class="sy0">,</span> b<span class="br0">)</span> <span
+ class="br0">{</span><br> <span class="kw1">return</span> a <span
+ class="sy0">-</span> b<span class="sy0">;</span><br><span
+ class="br0">}</span><span class="br0">)</span><span
+ class="sy0">;</span><br><span class="kw1">return</span><span
+ class="sy0">;</span></code></pre>
+ </td>
+ <td id="results-2" class="results"></td>
+ </tr>
+ <tr>
+ <th scope="row" id="title-3">
+ <div>Async</div>
+ </th>
+ <td class="code">
+ <pre><code><strong class="co1">// async test</strong><br>setTimeout<span
+ class="br0">(</span><span class="kw2">function</span><span
+ class="br0">(</span><span class="br0">)</span> <span
+ class="br0">{</span><br> deferred.<span
+ class="me1">resolve</span><span class="br0">(</span><span
+ class="br0">)</span><span class="sy0">;</span><br><span
+ class="br0">}</span><span class="sy0">,</span> <span
+ class="nu0">10</span><span class="br0">)</span><span
+ class="sy0">;</span></code></pre>
+ </td>
+ <td id="results-3" class="results"></td>
+ </tr>
+ <tr>
+ <th scope="row" id="title-4">
+ <div>Error</div>
+ </th>
+ <td class="code">
+ <pre><code>x.<span class="me1">foo</span><span class="br0">(</span><span
+ class="br0">)</span><span class="sy0">;</span> <span
+ class="co1">// unknown method</span></code></pre>
+ </td>
+ <td id="results-4" class="results"></td>
+ </tr>
+ <tr>
+ <th scope="row" id="title-5">
+ <div>Comments</div>
+ </th>
+ <td class="code">
+ <pre><code><span class="co1">// comments at start</span><br>x.<span
+ class="me1">reverse</span><span class="br0">(</span><span
+ class="br0">)</span>.<span class="me1">sort</span><span
+ class="br0">(</span><span class="kw2">function</span><span
+ class="br0">(</span>a<span class="sy0">,</span> b<span
+ class="br0">)</span> <span class="br0">{</span><br> <span
+ class="kw1">return</span> a <span class="sy0">-</span> b<span
+ class="sy0">;</span><br><span class="br0">}</span><span
+ class="br0">)</span><span class="sy0">;</span><br><span
+ class="co1">// comments at end</span></code></pre>
+ </td>
+ <td id="results-5" class="results"></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <p>
+ You can <a href="#" rel="nofollow">edit these tests or add even more tests to this page</a>
+ by appending <code>/edit</code> to the URL.
+ </p>
+ </section>
+
+ <section>
+ <h2 id="results">Compare results of other browsers</h2>
+ <div id="bs-results"></div>
+ </section>
+
+ <section id="comments">
+ <h1>0 comments</h1>
+ <div id="comments-wrapper">
+ <form action="#comment-form" method="post" id="comment-form">
+ <fieldset>
+ <h2>Add a comment</h2>
+ <div>
+ <label for="author">Name <em title="This field is required">*</em></label>
+ <input type="text" name="author" id="author" required>
+ </div>
+ <div>
+ <label for="author-email">Email <em title="This field is required">*</em></label>
+ <label class="inline">
+ <input type="email" name="author-email" id="author-email" required> (only used for Gravatar)
+ </label>
+ </div>
+ <div>
+ <label for="author-url">URL </label>
+ <input type="url" name="author-url" id="author-url">
+ </div>
+ <div>
+ <label for="message">Message <em title="This field is required">*</em><span>Markdown syntax is allowed</span></label>
+ <textarea name="message" id="message" required></textarea>
+ </div>
+ <div class="question">
+ <label for="question">Are you a spammer? <span>(just answer the question)</span></label>
+ <input type="text" name="question" id="question">
+ </div>
+ <div class="buttons">
+ <input type="submit" class="submit" value="Add comment">
+ </div>
+ </fieldset>
+ </form>
+ </div>
+ </section>
+ </article>
+
+ <footer>
+ © 2011 <a href="http://jsperf.com/">jsPerf.com</a>
+ · <a href="http://jsperf.com/browse">Browse</a>
+ · <a href="http://jsperf.com/popular">Popular tests</a>
+ · <a href="http://jsperf.com/faq">FAQ</a>
+ · <a href="http://jsperf.com/faq#donate">Donate</a>
+ · <a href="http://twitter.com/jsprf" rel="nofollow">twitter: @jsprf</a>
+ · <a href="http://benchmarkjs.com/">Benchmark.js</a>
+ · by <a href="http://mathiasbynens.be/" title="Mathias Bynens, front-end web developer">@mathias</a>
+ </footer>
+
+ <script src="../../vendor/platform.js/platform.js"></script>
+ <script src="../../benchmark.js"></script>
+ <script src="ui.js"></script>
+ <script src="../../plugin/ui.browserscope.js"></script>
+ <script>
+ var arr = [1, 5, 4, 2, 3];
+
+ function init() {
+ window.console && console.log('init called');
+ }
+ </script>
+ <script>
+ ui.browserscope.key = 'agt1YS1wcm9maWxlcnINCxIEVGVzdBjR6NELDA';
+ ui.browserscope.selector = '#bs-results';
+
+ ui.add('Normal', '\
+ x.sort(function(a, b) {\n\
+ return a - b;\n\
+ });'
+ )
+ .add('Exit Early', '\
+ x.sort(function(a, b) {\n\
+ return a - b;\n\
+ });\n\
+ return;'
+ )
+ .add('Async', {
+ 'defer': true,
+ 'fn': '\
+ setTimeout(function() {\n\
+ deferred.resolve();\n\
+ }, 10);'
+ })
+ .add('Error', '\
+ x.foo(); // unknown method'
+ )
+ .add('Comments', '\
+ // comments at start\n\
+ x.reverse().sort(function(a, b) {\n\
+ return a - b;\n\
+ });\n\
+ // comments at end'
+ );
+
+ Benchmark.prototype.setup = '\
+ window.foo = 42;\n\
+ var x = arr;';
+
+ Benchmark.prototype.teardown = '\
+ window.foo = 24;';
+ </script>
+ </body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/main.css b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/main.css
new file mode 100644
index 0000000..52d42ef
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/main.css
@@ -0,0 +1,588 @@
+html, body, h1, h2, h3, fieldset, #faq, #faq dt, #faq dd {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+table, p, ul, h1, h2, h3, #error-info, form div, #faq, .bs-rt {
+ margin-bottom: 1em;
+}
+
+button, input, textarea, a, .bs-rt {
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+}
+
+html, input, textarea, button {
+ font: 1em/1.5 sans-serif;
+}
+
+html {
+ background: #c4c4c4;
+ height: 100%;
+}
+
+body {
+ background: #fff;
+ border: solid #aaa;
+ border-width: 0 1px;
+ width: 60em;
+ padding: 0 2.5em;
+ margin: 0 auto;
+ min-height: 100%;
+}
+
+a {
+ color: #357ab0;
+ padding: .2em;
+}
+
+a:hover, a:focus {
+ text-decoration: none;
+}
+
+blockquote {
+ margin: 0 0 1em;
+ border-left: 5px solid #b4b4b4;
+ padding-left: .5em;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+thead th, button:hover, button:focus, .submit:hover, .submit:focus, a:hover, a:focus, #comments .meta a:hover, #comments .meta a:focus, li.current a:hover, li.current a:focus, form h3, #comments .owner .meta {
+ background: #1a6ab9;
+ background-image: -moz-linear-gradient(top, #6ca5dd, #1a6ab9);
+ background-image: -o-linear-gradient(top, #6ca5dd, #1a6ab9);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#6ca5dd), to(#1a6ab9));
+ background-image: -webkit-linear-gradient(#6ca5dd, #1a6ab9);
+ background-image: linear-gradient(top, #6ca5dd, #1a6ab9);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#6ca5dd', EndColorStr='#1a6ab9');
+ color: #fff;
+}
+
+caption, #comments .meta {
+ background: #bcbcbc;
+ background-image: -moz-linear-gradient(top, #d0d0d0, #a7a7a7);
+ background-image: -o-linear-gradient(top, #d0d0d0, #a7a7a7);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#d0d0d0), to(#a7a7a7));
+ background-image: -webkit-linear-gradient(#d0d0d0, #a7a7a7);
+ background-image: linear-gradient(top, #d0d0d0, #a7a7a7);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#d0d0d0', EndColorStr='#a7a7a7');
+ color: #555;
+}
+
+thead th, caption {
+ font-weight: bold;
+}
+
+.js tbody th:hover, .js tbody th:focus {
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+tbody th, td {
+ border: solid #b4b4b4;
+ border-width: 0 1px 1px 0;
+}
+
+tbody th {
+ background: #dde4ea;
+ min-width: 100px;
+}
+
+tbody th div {
+ max-width: 200px;
+ word-wrap: break-word;
+ overflow: auto;
+}
+
+td.results {
+ text-align: center;
+ border-right: 0;
+}
+
+.results span, small {
+ display: block;
+ font-size: .8em;
+}
+
+td, th, caption {
+ padding: .2em .5em;
+}
+
+td.fastest {
+ background: #9cee82;
+}
+
+tr:last-child td, tr:last-child th {
+ border-bottom: 0;
+}
+
+td.slowest, td.error, .invalid {
+ background: pink;
+}
+
+/* needs its own rule because of buggy IE */
+:focus:invalid {
+ background: pink;
+}
+
+td.error {
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+button, .submit {
+ padding: .35em .5em;
+ cursor: pointer;
+ color: #000;
+ border: 1px solid #999;
+ background: #dadada;
+ background-image: -moz-linear-gradient(top, #ebebeb, #b8b8b8);
+ background-image: -o-linear-gradient(top, #ebebeb, #b8b8b8);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#b8b8b8));
+ background-image: -webkit-linear-gradient(top, #ebebeb, #b8b8b8);
+ background-image: linear-gradient(top, #ebebeb, #b8b8b8);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#ebebeb', EndColorStr='#b8b8b8');
+}
+
+a:hover span, a:focus span, #comments .owner .meta a {
+ color: #fff;
+}
+
+#controls, #run, .buttons {
+ float: right;
+}
+
+button:hover, button:focus, .submit:hover, .submit:focus {
+ border-color: #357ab0
+}
+
+#add-buttons button {
+ padding: .15em .4em;
+ font-size: 11px;
+ font-weight: bold;
+}
+
+form label {
+ float: left;
+ width: 14em;
+ cursor: pointer;
+ text-align: right;
+ margin-right: 1em;
+ padding: .4em;
+}
+
+label.inline {
+ float: none;
+ padding: 0;
+ margin: 0;
+}
+
+label[for="visible"], label[for$="][defer]"] {
+ position: relative;
+ top: -.37em;
+}
+
+label span {
+ display: block;
+ font-size: 90%;
+ color: #b4b4b4;
+}
+
+label em {
+ color: red;
+ font-style: normal;
+}
+
+.js #advanced {
+ display: none;
+}
+
+#show-advanced {
+ display: none;
+}
+
+.js #show-advanced {
+ display: block;
+}
+
+section {
+ display: block;
+ border-top: 1px solid #ccc;
+ padding-top: 2em;
+ margin: 2em 0;
+}
+
+textarea {
+ resize: vertical;
+ height: 15em;
+ width: 42.6em;
+ *width: 42.4em; /* IE < 8 */
+}
+
+input, textarea {
+ border: 1px solid #b4b4b4;
+ padding: .4em;
+}
+
+#visible, #calibrate { /* checkboxes, for IE */
+ border: 0;
+ padding: 0;
+}
+
+form h2, form h3, form h4, p.error, .preview, #add-libraries, #add-buttons {
+ padding-left: 250px;
+ display: block;
+}
+
+.js .question, hgroup h2, #controls, #firebug, #java {
+ display: none;
+}
+
+pre {
+ width: 100%;
+ overflow: auto;
+}
+
+table #results-1 {
+ width: 100px;
+}
+
+table pre {
+ *padding: 1.5em 0; /* IE < 8 */
+ *overflow-y: hidden; /* IE < 8 */
+}
+
+table pre, table td.code {
+ width: 650px;
+}
+
+mark {
+ background: #ff9;
+ padding: .2em .1em;
+}
+
+h1, h2, h3, h4 {
+ font-weight: bold;
+ font-size: 1em;
+}
+
+h1 {
+ padding-top: 1em;
+ font-size: 1.4em;
+}
+
+form h3 {
+ padding-top: .2em;
+ padding-bottom: .2em;
+}
+
+h1 em {
+ font-style: normal;
+}
+
+h1 strong {
+ font-style: italic;
+ font-family: Monaco, 'Lucida Console', monospace;
+}
+
+li.current a {
+ background: #90ee85;
+}
+
+#donate {
+ display: block;
+ background: #ffffdc;
+ border: 1px solid #faa700;
+ padding: 1em;
+}
+
+#donate h1 {
+ padding-top: 0;
+ font-size: 16px;
+}
+
+#paypal {
+ text-align: center;
+}
+
+footer {
+ display: block;
+ margin-top: 2em;
+ padding: .5em 0 1.5em;
+ border-top: 2px solid #c4c4c4;
+}
+
+#add-test {
+ margin-right: .3em;
+}
+
+#bs-chart {
+ overflow: auto;
+}
+
+#bs-logo {
+ margin: 0;
+}
+
+#bs-logo span, applet {
+ position: absolute;
+ left: -9999em;
+}
+
+#bs-logo a {
+ display: block;
+ width: 232px;
+ height: 39px;
+ filter: none;
+ background: url(//www.browserscope.org/static/img/logo.png) 0 0 no-repeat;
+}
+
+#bs-ua {
+ padding: .5em .5em 0;
+ color: #555;
+}
+
+#bs-results .bs-rt {
+ font-size: 10pt;
+ padding: .5em;
+ background: #ddd;
+}
+
+#bs-results td {
+ border: 1px solid #ddd;
+ padding: .4em;
+ white-space: nowrap;
+}
+
+#bs-results .rt-ua-cur {
+ font-style: italic;
+ font-weight: bold;
+}
+
+#bs-results .bs-rt-message {
+ padding: 3em;
+ text-align: center;
+ font-weight: bold;
+ color: #555;
+}
+
+#bs-results .google-visualization-table-tr-head td {
+ white-space: normal;
+}
+
+#controls {
+ margin-top: -3.35em;
+}
+
+#comments h1 {
+ padding: 0;
+}
+
+#comments .meta img {
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin: 0;
+}
+
+#comments .meta img {
+ top: 2px;
+ left: 2px;
+}
+
+#comments .meta {
+ padding-left: 35px;
+ margin-top: 0;
+ width: 923px;
+ line-height: 30px;
+}
+
+#comments .meta a {
+ font-weight: bold;
+ color: #555;
+}
+
+#comments article div {
+ padding: 0 1em 0;
+}
+
+#comments article {
+ display: block;
+ border: 1px solid #b4b4b4;
+ position: relative;
+ margin-bottom: 1em;
+}
+
+/* needs its own rule (cannot be grouped with `tbody th`) because of buggy IE */
+#comments article:target {
+ background: #dde4ea;
+}
+
+#error-info.show, .meta strong, #firebug strong, #java strong, #status strong {
+ background: pink;
+ border: 1px solid #b00b00;
+ padding: .4em;
+}
+
+#error-info.show {
+ padding: .5em 1em;
+}
+
+#error-info, code, samp, var, textarea, #slug {
+ font-family: Monaco, monospace;
+ font-size: .9em;
+}
+
+#java strong {
+ background: #ffffdc;
+ border: 1px solid #faa700;
+}
+
+#slug {
+ font-size: 1em;
+}
+
+#faq dt {
+ margin-top: 1em;
+ font-weight: bold;
+}
+
+#faq dt a {
+ display: none;
+}
+
+#faq dt:hover a {
+ display: inline;
+}
+
+#faq dt:target, #faq dt:target + dd {
+ background: #90ee85;
+ margin: 0 -.8em;
+ padding: 0 .8em;
+}
+
+#faq dt:target + dd {
+ padding-bottom: .5em;
+ margin-bottom: -.5em;
+}
+
+#faq dt:target {
+ margin-top: .5em;
+ padding-top: .5em;
+}
+
+#firebug, #java, #status {
+ margin: 0 0 1em;
+ padding: .3em 0;
+}
+
+#prep-code pre {
+ max-height: 500px;
+ overflow: auto;
+}
+
+#controls.show, #firebug.show, #java.show {
+ display: block;
+}
+
+.co1, .co2, .coMULTI {
+ font-style: italic;
+}
+
+.error {
+ color: #b00b00;
+}
+
+.imp {
+ color: red;
+}
+
+.kw1, .kw3 {
+ color: #006;
+}
+
+.kw2 {
+ color: #036;
+}
+
+.co1, .coMULTI {
+ color: #060;
+}
+
+.co2 {
+ color: #096;
+}
+
+.es0 {
+ color: #009;
+}
+
+.br0 {
+ color: #090;
+}
+
+.sy0 {
+ color: #393;
+}
+
+.st0 {
+ color: #36c;
+}
+
+.nu0 {
+ color: #c00;
+}
+
+.me1 {
+ color: #606;
+}
+
+/* < 1051px */
+@media (max-width: 1050px) {
+ table pre, table td.code {
+ width: 550px;
+ }
+}
+
+/* < 1041px */
+@media (max-width: 1040px) {
+ body {
+ margin: 0;
+ border: 0;
+ }
+
+ body, #comments .meta {
+ width: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+}
+
+/* < 801px */
+@media (max-width: 800px) {
+ table pre, table td.code {
+ width: 450px;
+ }
+}
+
+/* < 681px */
+@media (max-width: 680px) {
+ table pre, table td.code {
+ width: 350px;
+ }
+}
+
+/* < 651px */
+@media (max-width: 650px) {
+ table pre, table td.code {
+ width: 200px;
+ }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/ui.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/ui.js
new file mode 100644
index 0000000..8270cd66
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/example/jsperf/ui.js
@@ -0,0 +1,745 @@
+/*!
+ * ui.js
+ * Copyright Mathias Bynens <http://mths.be/>
+ * Modified by John-David Dalton <http://allyoucanleet.com/>
+ * Available under MIT license <http://mths.be/mit>
+ */
+(function(window, document) {
+
+ /** Java applet archive path */
+ var archive = '../../nano.jar';
+
+ /** Cache of error messages */
+ var errors = [];
+
+ /** Google Analytics account id */
+ var gaId = '';
+
+ /** Cache of event handlers */
+ var handlers = {};
+
+ /** A flag to indicate that the page has loaded */
+ var pageLoaded = false;
+
+ /** Benchmark results element id prefix (e.g. `results-1`) */
+ var prefix = 'results-';
+
+ /** The element responsible for scrolling the page (assumes ui.js is just before </body>) */
+ var scrollEl = document.body;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Namespace */
+ var ui = new Benchmark.Suite;
+
+ /** Object containing various CSS class names */
+ var classNames = {
+ // used for error styles
+ 'error': 'error',
+ // used to make content visible
+ 'show': 'show',
+ // used to reset result styles
+ 'results': 'results'
+ };
+
+ /** Used to flag environments/features */
+ var has = {
+ // used for pre-populating form fields
+ 'localStorage': !!function() {
+ try {
+ return !localStorage.getItem(+new Date);
+ } catch(e) { }
+ }(),
+ // used to distinguish between a regular test page and an embedded chart
+ 'runner': !!$('runner')
+ };
+
+ /** Object containing various text messages */
+ var texts = {
+ // inner text for the various run button states
+ 'run': {
+ 'again': 'Run again',
+ 'ready': 'Run tests',
+ 'running': 'Stop running'
+ },
+ // common status values
+ 'status': {
+ 'again': 'Done. Ready to run again.',
+ 'ready': 'Ready to run.'
+ }
+ };
+
+ /** The options object for Benchmark.Suite#run */
+ var runOptions = {
+ 'async': true,
+ 'queued': true
+ };
+
+ /** API shortcuts */
+ var each = Benchmark.each,
+ extend = Benchmark.extend,
+ filter = Benchmark.filter,
+ forOwn = Benchmark.forOwn,
+ formatNumber = Benchmark.formatNumber,
+ indexOf = Benchmark.indexOf,
+ invoke = Benchmark.invoke,
+ join = Benchmark.join;
+
+ /*--------------------------------------------------------------------------*/
+
+ handlers.benchmark = {
+
+ /**
+ * The onCycle callback, used for onStart as well, assigned to new benchmarks.
+ *
+ * @private
+ */
+ 'cycle': function() {
+ var bench = this,
+ size = bench.stats.sample.length;
+
+ if (!bench.aborted) {
+ setStatus(bench.name + ' × ' + formatNumber(bench.count) + ' (' +
+ size + ' sample' + (size == 1 ? '' : 's') + ')');
+ }
+ },
+
+ /**
+ * The onStart callback assigned to new benchmarks.
+ *
+ * @private
+ */
+ 'start': function() {
+ // call user provided init() function
+ if (isFunction(window.init)) {
+ init();
+ }
+ }
+ };
+
+ handlers.button = {
+
+ /**
+ * The "run" button click event handler used to run or abort the benchmarks.
+ *
+ * @private
+ */
+ 'run': function() {
+ var stopped = !ui.running;
+ ui.abort();
+ ui.length = 0;
+
+ if (stopped) {
+ logError({ 'clear': true });
+ ui.push.apply(ui, filter(ui.benchmarks, function(bench) {
+ return !bench.error && bench.reset();
+ }));
+ ui.run(runOptions);
+ }
+ }
+ };
+
+ handlers.title = {
+
+ /**
+ * The title table cell click event handler used to run the corresponding benchmark.
+ *
+ * @private
+ * @param {Object} event The event object.
+ */
+ 'click': function(event) {
+ event || (event = window.event);
+
+ var id,
+ index,
+ target = event.target || event.srcElement;
+
+ while (target && !(id = target.id)) {
+ target = target.parentNode;
+ }
+ index = id && --id.split('-')[1] || 0;
+ ui.push(ui.benchmarks[index].reset());
+ ui.running ? ui.render(index) : ui.run(runOptions);
+ },
+
+ /**
+ * The title cell keyup event handler used to simulate a mouse click when hitting the ENTER key.
+ *
+ * @private
+ * @param {Object} event The event object.
+ */
+ 'keyup': function(event) {
+ if (13 == (event || window.event).keyCode) {
+ handlers.title.click(event);
+ }
+ }
+ };
+
+ handlers.window = {
+
+ /**
+ * The window hashchange event handler supported by Chrome 5+, Firefox 3.6+, and IE8+.
+ *
+ * @private
+ */
+ 'hashchange': function() {
+ ui.parseHash();
+
+ var scrollTop,
+ params = ui.params,
+ chart = params.chart,
+ filterBy = params.filterby;
+
+ if (pageLoaded) {
+ // configure posting
+ ui.browserscope.postable = has.runner && !('nopost' in params);
+
+ // configure chart renderer
+ if (chart || filterBy) {
+ scrollTop = $('results').offsetTop;
+ ui.browserscope.render({ 'chart': chart, 'filterBy': filterBy });
+ }
+ if (has.runner) {
+ // call user provided init() function
+ if (isFunction(window.init)) {
+ init();
+ }
+ // auto-run
+ if ('run' in params) {
+ scrollTop = $('runner').offsetTop;
+ setTimeout(handlers.button.run, 1);
+ }
+ // scroll to the relevant section
+ if (scrollTop) {
+ scrollEl.scrollTop = scrollTop;
+ }
+ }
+ }
+ },
+
+ /**
+ * The window load event handler used to initialize the UI.
+ *
+ * @private
+ */
+ 'load': function() {
+ // only for pages with a comment form
+ if (has.runner) {
+ // init the ui
+ addClass('controls', classNames.show);
+ addListener('run', 'click', handlers.button.run);
+
+ setHTML('run', texts.run.ready);
+ setHTML('user-agent', Benchmark.platform);
+ setStatus(texts.status.ready);
+
+ // answer spammer question
+ $('question').value = 'no';
+
+ // prefill author details
+ if (has.localStorage) {
+ each([$('author'), $('author-email'), $('author-url')], function(element) {
+ element.value = localStorage[element.id] || '';
+ element.oninput = element.onkeydown = function(event) {
+ event && event.type < 'k' && (element.onkeydown = null);
+ localStorage[element.id] = element.value;
+ };
+ });
+ }
+ // show warning when Firebug is enabled (avoids showing for Firebug Lite)
+ try {
+ // Firebug 1.9 no longer has `console.firebug`
+ if (console.firebug || /firebug/i.test(console.table())) {
+ addClass('firebug', classNames.show);
+ }
+ } catch(e) { }
+ }
+ // evaluate hash values
+ // (delay in an attempt to ensure this is executed after all other window load handlers)
+ setTimeout(function() {
+ pageLoaded = true;
+ handlers.window.hashchange();
+ }, 1);
+ }
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Shortcut for document.getElementById().
+ *
+ * @private
+ * @param {Element|String} id The id of the element to retrieve.
+ * @returns {Element} The element, if found, or null.
+ */
+ function $(id) {
+ return typeof id == 'string' ? document.getElementById(id) : id;
+ }
+
+ /**
+ * Adds a CSS class name to an element's className property.
+ *
+ * @private
+ * @param {Element|String} element The element or id of the element.
+ * @param {String} className The class name.
+ * @returns {Element} The element.
+ */
+ function addClass(element, className) {
+ if ((element = $(element)) && !hasClass(element, className)) {
+ element.className += (element.className ? ' ' : '') + className;
+ }
+ return element;
+ }
+
+ /**
+ * Registers an event listener on an element.
+ *
+ * @private
+ * @param {Element|String} element The element or id of the element.
+ * @param {String} eventName The name of the event.
+ * @param {Function} handler The event handler.
+ * @returns {Element} The element.
+ */
+ function addListener(element, eventName, handler) {
+ if ((element = $(element))) {
+ if (typeof element.addEventListener != 'undefined') {
+ element.addEventListener(eventName, handler, false);
+ } else if (typeof element.attachEvent != 'undefined') {
+ element.attachEvent('on' + eventName, handler);
+ }
+ }
+ return element;
+ }
+
+ /**
+ * Appends to an element's innerHTML property.
+ *
+ * @private
+ * @param {Element|String} element The element or id of the element.
+ * @param {String} html The HTML to append.
+ * @returns {Element} The element.
+ */
+ function appendHTML(element, html) {
+ if ((element = $(element)) && html != null) {
+ element.innerHTML += html;
+ }
+ return element;
+ }
+
+ /**
+ * Shortcut for document.createElement().
+ *
+ * @private
+ * @param {String} tag The tag name of the element to create.
+ * @returns {Element} A new element of the given tag name.
+ */
+ function createElement(tagName) {
+ return document.createElement(tagName);
+ }
+
+ /**
+ * Checks if an element is assigned the given class name.
+ *
+ * @private
+ * @param {Element|String} element The element or id of the element.
+ * @param {String} className The class name.
+ * @returns {Boolean} If assigned the class name return true, else false.
+ */
+ function hasClass(element, className) {
+ return !!(element = $(element)) &&
+ (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
+ }
+
+ /**
+ * Set an element's innerHTML property.
+ *
+ * @private
+ * @param {Element|String} element The element or id of the element.
+ * @param {String} html The HTML to set.
+ * @returns {Element} The element.
+ */
+ function setHTML(element, html) {
+ if ((element = $(element))) {
+ element.innerHTML = html == null ? '' : html;
+ }
+ return element;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Gets the Hz, i.e. operations per second, of `bench` adjusted for the
+ * margin of error.
+ *
+ * @private
+ * @param {Object} bench The benchmark object.
+ * @returns {Number} Returns the adjusted Hz.
+ */
+ function getHz(bench) {
+ return 1 / (bench.stats.mean + bench.stats.moe);
+ }
+
+ /**
+ * Checks if a value has an internal [[Class]] of Function.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the value is a function, else `false`.
+ */
+ function isFunction(value) {
+ return toString.call(value) == '[object Function]';
+ }
+
+ /**
+ * Appends to or clears the error log.
+ *
+ * @private
+ * @param {String|Object} text The text to append or options object.
+ */
+ function logError(text) {
+ var table,
+ div = $('error-info'),
+ options = {};
+
+ // juggle arguments
+ if (typeof text == 'object' && text) {
+ options = text;
+ text = options.text;
+ }
+ else if (arguments.length) {
+ options.text = text;
+ }
+ if (!div) {
+ table = $('test-table');
+ div = createElement('div');
+ div.id = 'error-info';
+ table.parentNode.insertBefore(div, table.nextSibling);
+ }
+ if (options.clear) {
+ div.className = div.innerHTML = '';
+ errors.length = 0;
+ }
+ if ('text' in options && indexOf(errors, text) < 0) {
+ errors.push(text);
+ addClass(div, classNames.show);
+ appendHTML(div, text);
+ }
+ }
+
+ /**
+ * Sets the status text.
+ *
+ * @private
+ * @param {String} text The text to write to the status.
+ */
+ function setStatus(text) {
+ setHTML('status', text);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Parses the window.location.hash value into an object assigned to `ui.params`.
+ *
+ * @static
+ * @memberOf ui
+ * @returns {Object} The suite instance.
+ */
+ function parseHash() {
+ var me = this,
+ hashes = location.hash.slice(1).split('&'),
+ params = me.params || (me.params = {});
+
+ // remove old params
+ forOwn(params, function(value, key) {
+ delete params[key];
+ });
+
+ // add new params
+ each(hashes[0] && hashes, function(value) {
+ value = value.split('=');
+ params[value[0].toLowerCase()] = (value[1] || '').toLowerCase();
+ });
+ return me;
+ }
+
+ /**
+ * Renders the results table cell of the corresponding benchmark(s).
+ *
+ * @static
+ * @memberOf ui
+ * @param {Number} [index] The index of the benchmark to render.
+ * @returns {Object} The suite instance.
+ */
+ function render(index) {
+ each(index == null ? (index = 0, ui.benchmarks) : [ui.benchmarks[index]], function(bench) {
+ var parsed,
+ cell = $(prefix + (++index)),
+ error = bench.error,
+ hz = bench.hz;
+
+ // reset title and class
+ cell.title = '';
+ cell.className = classNames.results;
+
+ // status: error
+ if (error) {
+ setHTML(cell, 'Error');
+ addClass(cell, classNames.error);
+ parsed = join(error, '</li><li>');
+ logError('<p>' + error + '.</p>' + (parsed ? '<ul><li>' + parsed + '</li></ul>' : ''));
+ }
+ else {
+ // status: running
+ if (bench.running) {
+ setHTML(cell, 'running…');
+ }
+ // status: completed
+ else if (bench.cycles) {
+ // obscure details until the suite has completed
+ if (ui.running) {
+ setHTML(cell, 'completed');
+ }
+ else {
+ cell.title = 'Ran ' + formatNumber(bench.count) + ' times in ' +
+ bench.times.cycle.toFixed(3) + ' seconds.';
+ setHTML(cell, formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) +
+ ' <small>±' + bench.stats.rme.toFixed(2) + '%</small>');
+ }
+ }
+ else {
+ // status: pending
+ if (ui.running && ui.indexOf(bench) > -1) {
+ setHTML(cell, 'pending…');
+ }
+ // status: ready
+ else {
+ setHTML(cell, 'ready');
+ }
+ }
+ }
+ });
+ return ui;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ ui.on('add', function(event) {
+ var bench = event.target,
+ index = ui.benchmarks.length,
+ id = index + 1,
+ title = $('title-' + id);
+
+ delete ui[--ui.length];
+ ui.benchmarks.push(bench);
+
+ if (has.runner) {
+ title.tabIndex = 0;
+ title.title = 'Click to run this test again.';
+
+ addListener(title, 'click', handlers.title.click);
+ addListener(title, 'keyup', handlers.title.keyup);
+
+ bench.on('start', handlers.benchmark.start);
+ bench.on('start cycle', handlers.benchmark.cycle);
+ ui.render(index);
+ }
+ })
+ .on('start cycle', function() {
+ ui.render();
+ setHTML('run', texts.run.running);
+ })
+ .on('complete', function() {
+ var benches = filter(ui.benchmarks, 'successful'),
+ fastest = filter(benches, 'fastest'),
+ slowest = filter(benches, 'slowest');
+
+ ui.render();
+ setHTML('run', texts.run.again);
+ setStatus(texts.status.again);
+
+ // highlight result cells
+ each(benches, function(bench) {
+ var cell = $(prefix + (indexOf(ui.benchmarks, bench) + 1)),
+ fastestHz = getHz(fastest[0]),
+ hz = getHz(bench),
+ percent = (1 - (hz / fastestHz)) * 100,
+ span = cell.getElementsByTagName('span')[0],
+ text = 'fastest';
+
+ if (indexOf(fastest, bench) > -1) {
+ // mark fastest
+ addClass(cell, text);
+ }
+ else {
+ text = isFinite(hz)
+ ? formatNumber(percent < 1 ? percent.toFixed(2) : Math.round(percent)) + '% slower'
+ : '';
+
+ // mark slowest
+ if (indexOf(slowest, bench) > -1) {
+ addClass(cell, 'slowest');
+ }
+ }
+ // write ranking
+ if (span) {
+ setHTML(span, text);
+ } else {
+ appendHTML(cell, '<span>' + text + '</span>');
+ }
+ });
+
+ ui.browserscope.post();
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * An array of benchmarks created from test cases.
+ *
+ * @memberOf ui
+ * @type Array
+ */
+ ui.benchmarks = [];
+
+ /**
+ * The parsed query parameters of the pages url hash.
+ *
+ * @memberOf ui
+ * @type Object
+ */
+ ui.params = {};
+
+ // parse query params into ui.params hash
+ ui.parseHash = parseHash;
+
+ // (re)render the results of one or more benchmarks
+ ui.render = render;
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose
+ window.ui = ui;
+
+ // don't let users alert, confirm, prompt, or open new windows
+ window.alert = window.confirm = window.prompt = window.open = function() { };
+
+ // parse hash query params when it changes
+ addListener(window, 'hashchange', handlers.window.hashchange);
+
+ // bootstrap onload
+ addListener(window, 'load', handlers.window.load);
+
+ // parse location hash string
+ ui.parseHash();
+
+ // provide a simple UI for toggling between chart types and filtering results
+ // (assumes ui.js is just before </body>)
+ (function() {
+ var sibling = $('bs-results'),
+ p = createElement('p');
+
+ p.innerHTML =
+ '<span id=charts><strong>Chart type:</strong> <a href=#>bar</a>, ' +
+ '<a href=#>column</a>, <a href=#>line</a>, <a href=#>pie</a>, ' +
+ '<a href=#>table</a></span><br>' +
+ '<span id=filters><strong>Filter:</strong> <a href=#>popular</a>, ' +
+ '<a href=#>all</a>, <a href=#>desktop</a>, <a href=#>family</a>, ' +
+ '<a href=#>major</a>, <a href=#>minor</a>, <a href=#>mobile</a>, ' +
+ '<a href=#>prerelease</a></span>';
+
+ sibling.parentNode.insertBefore(p, sibling);
+
+ // use DOM0 event handler to simplify canceling the default action
+ $('charts').onclick =
+ $('filters').onclick = function(event) {
+ event || (event = window.event);
+ var target = event.target || event.srcElement;
+ if (target.href || (target = target.parentNode).href) {
+ ui.browserscope.render(
+ target.parentNode.id == 'charts'
+ ? { 'chart': target.innerHTML }
+ : { 'filterBy': target.innerHTML }
+ );
+ }
+ // cancel the default action
+ return false;
+ };
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ // fork for runner or embedded chart
+ if (has.runner) {
+ // detect the scroll element
+ (function() {
+ var scrollTop,
+ div = document.createElement('div'),
+ body = document.body,
+ bodyStyle = body.style,
+ bodyHeight = bodyStyle.height,
+ html = document.documentElement,
+ htmlStyle = html.style,
+ htmlHeight = htmlStyle.height;
+
+ bodyStyle.height = htmlStyle.height = 'auto';
+ div.style.cssText = 'display:block;height:9001px;';
+ body.insertBefore(div, body.firstChild);
+ scrollTop = html.scrollTop;
+
+ // set `scrollEl` that's used in `handlers.window.hashchange()`
+ if (html.clientWidth !== 0 && ++html.scrollTop && html.scrollTop == scrollTop + 1) {
+ scrollEl = html;
+ }
+ body.removeChild(div);
+ bodyStyle.height = bodyHeight;
+ htmlStyle.height = htmlHeight;
+ html.scrollTop = scrollTop;
+ }());
+
+ // catch and display errors from the "preparation code"
+ window.onerror = function(message, fileName, lineNumber) {
+ logError('<p>' + message + '.</p><ul><li>' + join({
+ 'message': message,
+ 'fileName': fileName,
+ 'lineNumber': lineNumber
+ }, '</li><li>') + '</li></ul>');
+ scrollEl.scrollTop = $('error-info').offsetTop;
+ };
+ // inject nano applet
+ // (assumes ui.js is just before </body>)
+ if ('nojava' in ui.params) {
+ addClass('java', classNames.show);
+ } else {
+ // using innerHTML avoids an alert in some versions of IE6
+ document.body.insertBefore(setHTML(createElement('div'),
+ '<applet code=nano archive=' + archive + '>').lastChild, document.body.firstChild);
+ }
+ }
+ else {
+ // short circuit unusable methods
+ ui.render = function() { };
+ ui.off('start cycle complete');
+ setTimeout(function() {
+ ui.off();
+ ui.browserscope.post = function() { };
+ invoke(ui.benchmarks, 'off');
+ }, 1);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // optimized asynchronous Google Analytics snippet based on
+ // http://mathiasbynens.be/notes/async-analytics-snippet
+ if (gaId) {
+ (function() {
+ var script = createElement('script'),
+ sibling = document.getElementsByTagName('script')[0];
+
+ window._gaq = [['_setAccount', gaId], ['_trackPageview']];
+ script.src = '//www.google-analytics.com/ga.js';
+ sibling.parentNode.insertBefore(script, sibling);
+ }());
+ }
+}(this, document));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.jar b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.jar
new file mode 100644
index 0000000..b577840
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.jar
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.java b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.java
new file mode 100644
index 0000000..96fc224
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/nano.java
@@ -0,0 +1,23 @@
+/**
+ * Simple class to expose nanoTime() to JavaScript.
+ *
+ * Compile using
+ * javac -g:none -target 1.5 nano.java
+ * jar cfM nano.jar nano.class
+ * java -jar proguard.jar @options.txt
+ *
+ * ProGuard (http://proguard.sourceforge.net)
+ * options.txt
+ * -injars nano.jar
+ * -outjars nano_s.jar
+ * -libraryjars <java.home>/jre/lib/rt.jar
+ * -keep public class nano {
+ * public long nanoTime();
+ * }
+ */
+import java.applet.Applet;
+public class nano extends Applet {
+ public long nanoTime() {
+ return System.nanoTime();
+ }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/package.json b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/package.json
new file mode 100644
index 0000000..2be940c
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "benchmark",
+ "version": "1.0.0",
+ "description": "A benchmarking library that works on nearly all JavaScript platforms, supports high-resolution timers, and returns statistically significant results.",
+ "homepage": "http://benchmarkjs.com/",
+ "main": "benchmark",
+ "keywords": [
+ "benchmark",
+ "narwhal",
+ "node",
+ "performance",
+ "ringo",
+ "speed"
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://mths.be/mit"
+ }
+ ],
+ "author": {
+ "name": "Mathias Bynens",
+ "email": "mathias@benchmarkjs.com",
+ "web": "http://mathiasbynens.be/"
+ },
+ "maintainers": [
+ {
+ "name": "John-David Dalton",
+ "email": "john.david.dalton@gmail.com",
+ "web": "http://allyoucanleet.com/"
+ },
+ {
+ "name": "Mathias Bynens",
+ "email": "mathias@benchmarkjs.com",
+ "web": "http://mathiasbynens.be/"
+ }
+ ],
+ "bugs": {
+ "email": "bugs@benchmarkjs.com",
+ "url": "https://github.com/bestiejs/benchmark.js/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/bestiejs/benchmark.js.git"
+ },
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "directories": {
+ "doc": "./doc",
+ "test": "./test"
+ }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/plugin/ui.browserscope.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/plugin/ui.browserscope.js
new file mode 100644
index 0000000..01905bd
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/plugin/ui.browserscope.js
@@ -0,0 +1,1052 @@
+(function(window, document) {
+
+ /** Cache used by various methods */
+ var cache = {
+ 'counter': 0,
+ 'lastAction': 'load',
+ 'lastChart': 'bar',
+ 'lastFilterBy': 'all',
+ 'responses': { /* 'all': null, 'desktop': null, 'major': null, ... */ },
+ 'timers': { /* 'cleanup': null, 'load': null, 'post': null, ... */ },
+ 'trash': createElement('div')
+ };
+
+ /**
+ * Used to filter Browserscope results by browser category.
+ *
+ * @see http://www.browserscope.org/user/tests/howto#urlparams
+ */
+ var filterMap = {
+ 'all': 3,
+ 'desktop': 'top-d',
+ 'family': 0,
+ 'major': 1,
+ 'minor': 2,
+ 'mobile': 'top-m',
+ 'popular': 'top',
+ 'prerelease': 'top-d-e'
+ };
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /**
+ * The `uaToken` is prepended to the value of the data cell of the Google
+ * visualization data table object that matches the user's browser name. After
+ * the chart is rendered the element containing the `uaToken` is assigned the
+ * `ui.browserscope.uaClass` class name to allow for the creation of a visual
+ * indicator to help the user more easily find their browser's results.
+ */
+ var uaToken = '\u2028';
+
+ /** Math shortcuts */
+ var floor = Math.floor,
+ max = Math.max,
+ min = Math.min;
+
+ /** Utility shortcuts */
+ var each = Benchmark.each,
+ extend = Benchmark.extend,
+ filter = Benchmark.filter,
+ forOwn = Benchmark.forOwn,
+ formatNumber = Benchmark.formatNumber,
+ hasKey = Benchmark.hasKey,
+ indexOf = Benchmark.indexOf,
+ interpolate = Benchmark.interpolate,
+ invoke = Benchmark.invoke,
+ map = Benchmark.map,
+ reduce = Benchmark.reduce;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Registers an event listener.
+ *
+ * @private
+ * @param {Element} element The element.
+ * @param {String} eventName The name of the event to listen to.
+ * @param {Function} handler The event handler.
+ * @returns {Element} The element.
+ */
+ function addListener(element, eventName, handler) {
+ if ((element = typeof element == 'string' ? query(element)[0] : element)) {
+ if (typeof element.addEventListener != 'undefined') {
+ element.addEventListener(eventName, handler, false);
+ } else if (typeof element.attachEvent != 'undefined') {
+ element.attachEvent('on' + eventName, handler);
+ }
+ }
+ return element;
+ }
+
+ /**
+ * Shortcut for `document.createElement()`.
+ *
+ * @private
+ * @param {String} tagName The tag name of the element to create.
+ * @param {String} name A name to assign to the element.
+ * @param {Document|Element} context The document object used to create the element.
+ * @returns {Element} Returns a new element.
+ */
+ function createElement(tagName, name, context) {
+ var result;
+ name && name.nodeType && (context = name, name = 0);
+ context = context ? context.ownerDocument || context : document;
+ name || (name = '');
+
+ try {
+ // set name attribute for IE6/7
+ result = context.createElement('<' + tagName + ' name="' + name + '">');
+ } catch(e) {
+ (result = context.createElement(tagName)).name = name;
+ }
+ return result;
+ }
+
+ /**
+ * Creates a new style element.
+ *
+ * @private
+ * @param {String} cssText The css text of the style element.
+ * @param {Document|Element} context The document object used to create the element.
+ * @returns {Element} Returns the new style element.
+ */
+ function createStyleSheet(cssText, context) {
+ // use a text node, "x", to work around innerHTML issues with style elements
+ // http://msdn.microsoft.com/en-us/library/ms533897(v=vs.85).aspx#1
+ var div = createElement('div', context);
+ div.innerHTML = 'x<style>' + cssText + '</style>';
+ return div.lastChild;
+ }
+
+ /**
+ * Gets the text content of an element.
+ *
+ * @private
+ * @param {Element} element The element.
+ * @returns {String} The text content of the element.
+ */
+ function getText(element) {
+ element = query(element)[0];
+ return element && (element.textContent || element.innerText) || '';
+ }
+
+ /**
+ * Injects a script into the document.
+ *
+ * @private
+ * @param {String} src The external script source.
+ * @param {Object} sibling The element to inject the script after.
+ * @param {Document} context The document object used to create the script element.
+ * @returns {Object} The new script element.
+ */
+ function loadScript(src, sibling, context) {
+ context = sibling ? sibling.ownerDocument || [sibling, sibling = 0][0] : context;
+ var script = createElement('script', context),
+ nextSibling = sibling ? sibling.nextSibling : query('script', context).pop();
+
+ script.src = src;
+ return (sibling || nextSibling).parentNode.insertBefore(script, nextSibling);
+ }
+
+ /**
+ * Queries the document for elements by id or tagName.
+ *
+ * @private
+ * @param {String} selector The css selector to match.
+ * @param {Document|Element} context The element whose descendants are queried.
+ * @returns {Array} The array of results.
+ */
+ function query(selector, context) {
+ var result = [];
+ selector || (selector = '');
+ context = typeof context == 'string' ? query(context)[0] : context || document;
+
+ if (selector.nodeType) {
+ result = [selector];
+ }
+ else if (context) {
+ each(selector.split(','), function(selector) {
+ each(/^#/.test(selector)
+ ? [context.getElementById(selector.slice(1))]
+ : context.getElementsByTagName(selector), function(node) {
+ result.push(node);
+ });
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Set an element's innerHTML property.
+ *
+ * @private
+ * @param {Element} element The element.
+ * @param {String} html The HTML to set.
+ * @param {Object} object The template object used to modify the html.
+ * @returns {Element} The element.
+ */
+ function setHTML(element, html, object) {
+ if ((element = query(element)[0])) {
+ element.innerHTML = interpolate(html, object);
+ }
+ return element;
+ }
+
+ /**
+ * Displays a message in the "results" element.
+ *
+ * @private
+ * @param {String} text The text to display.
+ * @param {Object} object The template object used to modify the text.
+ */
+ function setMessage(text, object) {
+ var me = ui.browserscope,
+ cont = me.container;
+
+ if (cont) {
+ cont.className = 'bs-rt-message';
+ setHTML(cont, text, object);
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Adds a style sheet to the current chart and assigns the `ui.browserscope.uaClass`
+ * class name to the chart element containing the user's browser name.
+ *
+ * @private
+ * @returns {Boolean} Returns `true` if the operation succeeded, else `false`.
+ */
+ function addChartStyle() {
+ var me = ui.browserscope,
+ cssText = [],
+ context = frames[query('iframe', me.container)[0].name].document,
+ chartNodes = query('text,textpath', context),
+ uaClass = me.uaClass,
+ result = false;
+
+ if (chartNodes.length) {
+ // extract CSS rules for `uaClass`
+ each(query('link,style'), function(node) {
+ // avoid access denied errors on external style sheets
+ // outside the same origin policy
+ try {
+ var sheet = node.sheet || node.styleSheet;
+ each(sheet.cssRules || sheet.rules, function(rule) {
+ if ((rule.selectorText || rule.cssText).indexOf('.' + uaClass) > -1) {
+ cssText.push(rule.style && rule.style.cssText || /[^{}]*(?=})/.exec(rule.cssText) || '');
+ }
+ });
+ } catch(e) { }
+ });
+
+ // insert custom style sheet
+ query('head', context)[0].appendChild(
+ createStyleSheet('.' + uaClass + '{' + cssText.join(';') + '}', context));
+
+ // scan chart elements for a match
+ each(chartNodes, function(node) {
+ var nextSibling;
+ if ((node.string || getText(node)).charAt(0) == uaToken) {
+ // for VML
+ if (node.string) {
+ // IE requires reinserting the element to render correctly
+ node.className = uaClass;
+ nextSibling = node.nextSibling;
+ node.parentNode.insertBefore(node.removeNode(), nextSibling);
+ }
+ // for SVG
+ else {
+ node.setAttribute('class', uaClass);
+ }
+ result = true;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Periodically executed callback that removes injected script and iframe elements.
+ *
+ * @private
+ */
+ function cleanup() {
+ var me = ui.browserscope,
+ timings = me.timings,
+ timers = cache.timers,
+ trash = cache.trash,
+ delay = timings.cleanup * 1e3;
+
+ // remove injected scripts and old iframes when benchmarks aren't running
+ if (timers.cleanup && !ui.running) {
+ // if expired, destroy the element to prevent pseudo memory leaks.
+ // http://dl.dropbox.com/u/513327/removechild_ie_leak.html
+ each(query('iframe,script'), function(element) {
+ var expire = +(/^browserscope-\d+-(\d+)$/.exec(element.name) || 0)[1] + max(delay, timings.timeout * 1e3);
+ if (new Date > expire || /browserscope\.org|google\.com/.test(element.src)) {
+ trash.appendChild(element);
+ trash.innerHTML = '';
+ }
+ });
+ }
+ // schedule another round
+ timers.cleanup = setTimeout(cleanup, delay);
+ }
+
+ /**
+ * A simple data object cloning utility.
+ *
+ * @private
+ * @param {Mixed} data The data object to clone.
+ * @returns {Mixed} The cloned data object.
+ */
+ function cloneData(data) {
+ var fn,
+ ctor,
+ result = data;
+
+ if (isArray(data)) {
+ result = map(data, cloneData);
+ }
+ else if (data === Object(data)) {
+ ctor = data.constructor;
+ result = ctor == Object ? {} : (fn = function(){}, fn.prototype = ctor.prototype, new fn);
+ forOwn(data, function(value, key) {
+ result[key] = cloneData(value);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates a Browserscope results object.
+ *
+ * @private
+ * @returns {Object|Null} Browserscope results object or null.
+ */
+ function createSnapshot() {
+ // clone benches, exclude those that are errored, unrun, or have hz of Infinity
+ var benches = invoke(filter(ui.benchmarks, 'successful'), 'clone'),
+ fastest = filter(benches, 'fastest'),
+ slowest = filter(benches, 'slowest'),
+ neither = filter(benches, function(bench) {
+ return indexOf(fastest, bench) + indexOf(slowest, bench) == -2;
+ });
+
+ function merge(destination, source) {
+ destination.count = source.count;
+ destination.cycles = source.cycles;
+ destination.hz = source.hz;
+ destination.stats = extend({}, source.stats);
+ }
+
+ // normalize results on slowest in each category
+ each(fastest.concat(slowest), function(bench) {
+ merge(bench, indexOf(fastest, bench) > -1 ? fastest[fastest.length - 1] : slowest[0]);
+ });
+
+ // sort slowest to fastest
+ // (a larger `mean` indicates a slower benchmark)
+ neither.sort(function(a, b) {
+ a = a.stats; b = b.stats;
+ return (a.mean + a.moe > b.mean + b.moe) ? -1 : 1;
+ });
+
+ // normalize the leftover benchmarks
+ reduce(neither, function(prev, bench) {
+ // if the previous slower benchmark is indistinguishable from
+ // the current then use the previous benchmark's values
+ if (prev.compare(bench) == 0) {
+ merge(bench, prev);
+ }
+ return bench;
+ });
+
+ // append benchmark ids for duplicate names or names with no alphanumeric/space characters
+ // and use the upper limit of the confidence interval to compute a lower hz
+ // to avoid recording inflated results caused by a high margin or error
+ return reduce(benches, function(result, bench, key) {
+ var stats = bench.stats;
+ result || (result = {});
+ key = toLabel(bench.name);
+ result[key && !hasKey(result, key) ? key : key + bench.id ] = floor(1 / (stats.mean + stats.moe));
+ return result;
+ }, null);
+ }
+
+ /**
+ * Retrieves the "cells" array from a given Google visualization data row object.
+ *
+ * @private
+ * @param {Object} object The data row object.
+ * @returns {Array} An array of cell objects.
+ */
+ function getDataCells(object) {
+ // resolve cells by duck typing because of munged property names
+ var result = [];
+ forOwn(object, function(value) {
+ return !(isArray(value) && (result = value));
+ });
+ // remove empty entries which occur when not all the tests are recorded
+ return filter(result, Boolean);
+ }
+
+ /**
+ * Retrieves the "labels" array from a given Google visualization data table object.
+ *
+ * @private
+ * @param {Object} object The data table object.
+ * @returns {Array} An array of label objects.
+ */
+ function getDataLabels(object) {
+ var result = [],
+ labelMap = {};
+
+ // resolve labels by duck typing because of munged property names
+ forOwn(object, function(value) {
+ return !(isArray(value) && 0 in value && 'type' in value[0] && (result = value));
+ });
+ // create a data map of labels to names
+ each(ui.benchmarks, function(bench) {
+ var key = toLabel(bench.name);
+ labelMap[key && !hasKey(labelMap, key) ? key : key + bench.id ] = bench.name;
+ });
+ // replace Browserscope's basic labels with benchmark names
+ return each(result, function(cell) {
+ var name = labelMap[cell.label];
+ name && (cell.label = name);
+ });
+ }
+
+ /**
+ * Retrieves the "rows" array from a given Google visualization data table object.
+ *
+ * @private
+ * @param {Object} object The data table object.
+ * @returns {Array} An array of row objects.
+ */
+ function getDataRows(object) {
+ var name,
+ filterBy = cache.lastFilterBy,
+ browserName = toBrowserName(getText(query('strong', '#bs-ua')[0]), filterBy),
+ uaClass = ui.browserscope.uaClass,
+ result = [];
+
+ // resolve rows by duck typing because of munged property names
+ forOwn(object, function(value, key) {
+ return !(isArray(value) && 0 in value && !('type' in value[0]) && (name = key, result = value));
+ });
+ // remove empty rows and set the `p.className` on the browser
+ // name cell that matches the user's browser name
+ if (result.length) {
+ result = object[name] = filter(result, function(value) {
+ var cells = getDataCells(value),
+ first = cells[0],
+ second = cells[1];
+
+ // cells[0] is the browser name cell so instead we check cells[1]
+ // for the presence of ops/sec data to determine if a row is empty or not
+ if (first && second && second.f) {
+ delete first.p.className;
+ if (browserName == toBrowserName(first.f, filterBy)) {
+ first.p.className = uaClass;
+ }
+ return true;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a value has an internal [[Class]] of Array.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the value has an internal [[Class]] of
+ * Array, else `false`.
+ */
+ function isArray(value) {
+ return toString.call(value) == '[object Array]';
+ }
+
+ /**
+ * Executes a callback at a given delay interval until it returns `false`.
+ *
+ * @private
+ * @param {Function} callback The function called every poll interval.
+ * @param {Number} delay The delay between callback calls (secs).
+ */
+ function poll(callback, delay) {
+ function poller(init) {
+ if (init || callback() !== false) {
+ setTimeout(poller, delay * 1e3);
+ }
+ }
+ poller(true);
+ }
+
+ /**
+ * Cleans up the last action and sets the current action.
+ *
+ * @private
+ * @param {String} action The current action.
+ */
+ function setAction(action) {
+ clearTimeout(cache.timers[cache.lastAction]);
+ cache.lastAction = action;
+ }
+
+ /**
+ * Converts the browser name version number to the format allowed by the
+ * specified filter.
+ *
+ * @private
+ * @param {String} name The full browser name .
+ * @param {String} filterBy The filter formating rules to apply.
+ * @returns {String} The converted browser name.
+ */
+ function toBrowserName(name, filterBy) {
+ name || (name = '');
+ if (filterBy == 'all') {
+ // truncate something like 1.0.0 to 1
+ name = name.replace(/(\d+)[.0]+$/, '$1');
+ }
+ else if (filterBy == 'family') {
+ // truncate something like XYZ 1.2 to XYZ
+ name = name.replace(/[.\d\s]+$/, '');
+ }
+ else if (/minor|popular/.test(filterBy) && /\d+(?:\.[1-9])+$/.test(name)) {
+ // truncate something like 1.2.3 to 1.2
+ name = name.replace(/(\d+\.[1-9])(\.[.\d]+$)/, '$1');
+ }
+ else {
+ // truncate something like 1.0 to 1 or 1.2.3 to 1 but leave something like 1.2 alone
+ name = name.replace(/(\d+)(?:(\.[1-9]$)|(\.[.\d]+$))/, '$1$2');
+ }
+ return name;
+ }
+
+ /**
+ * Replaces non-alphanumeric characters with spaces because Browserscope labels
+ * can only contain alphanumeric characters and spaces.
+ *
+ * @private
+ * @param {String} text The text to be converted.
+ * @returns {String} The Browserscope safe label text.
+ * @see http://code.google.com/p/browserscope/issues/detail?id=271
+ */
+ function toLabel(text) {
+ return (text || '').replace(/[^a-z0-9]+/gi, ' ');
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Loads Browserscope's cumulative results table.
+ *
+ * @static
+ * @memberOf ui.browserscope
+ * @param {Object} options The options object.
+ */
+ function load(options) {
+ options || (options = {});
+
+ var fired,
+ me = ui.browserscope,
+ cont = me.container,
+ filterBy = cache.lastFilterBy = options.filterBy || cache.lastFilterBy,
+ responses = cache.responses,
+ response = cache.responses[filterBy],
+ visualization = window.google && google.visualization;
+
+ function onComplete(response) {
+ var lastResponse = responses[filterBy];
+ if (!fired) {
+ // set the fired flag to avoid Google's own timeout
+ fired = true;
+ // render if the filter is still the same, else cache the result
+ if (filterBy == cache.lastFilterBy) {
+ me.render({ 'force': true, 'response': lastResponse || response });
+ } else if(!lastResponse && response && !response.isError()) {
+ responses[filterBy] = response;
+ }
+ }
+ }
+
+ // set last action in case the load fails and a retry is needed
+ setAction('load');
+
+ // exit early if there is no container element or the response is cached
+ // and retry if the visualization library hasn't loaded yet
+ if (!cont || !visualization || !visualization.Query || response) {
+ cont && onComplete(response);
+ }
+ else if (!ui.running) {
+ // set our own load timeout to display an error message and retry loading
+ cache.timers.load = setTimeout(onComplete, me.timings.timeout * 1e3);
+ // set "loading" message and attempt to load Browserscope data
+ setMessage(me.texts.loading);
+ // request Browserscope pass chart data to `google.visualization.Query.setResponse()`
+ (new visualization.Query(
+ '//www.browserscope.org/gviz_table_data?category=usertest_' + me.key + '&v=' + filterMap[filterBy],
+ { 'sendMethod': 'scriptInjection' }
+ ))
+ .send(onComplete);
+ }
+ }
+
+ /**
+ * Creates a Browserscope beacon and posts the benchmark results.
+ *
+ * @static
+ * @memberOf ui.browserscope
+ */
+ function post() {
+ var idoc,
+ iframe,
+ body = document.body,
+ me = ui.browserscope,
+ key = me.key,
+ timings = me.timings,
+ name = 'browserscope-' + (cache.counter++) + '-' + (+new Date),
+ snapshot = createSnapshot();
+
+ // set last action in case the post fails and a retry is needed
+ setAction('post');
+
+ if (key && snapshot && me.postable && !ui.running && !/Simulator/i.test(Benchmark.platform)) {
+ // create new beacon
+ // (the name contains a timestamp so `cleanup()` can determine when to remove it)
+ iframe = createElement('iframe', name);
+ body.insertBefore(iframe, body.firstChild);
+ idoc = frames[name].document;
+ iframe.style.display = 'none';
+
+ // expose results snapshot
+ me.snapshot = snapshot;
+ // set "posting" message and attempt to post the results snapshot
+ setMessage(me.texts.post);
+ // Note: We originally created an iframe to avoid Browerscope's old limit
+ // of one beacon per page load. It's currently used to implement custom
+ // request timeout and retry routines.
+ idoc.write(interpolate(
+ // the doctype is required so Browserscope detects the correct IE compat mode
+ '#{doctype}<title></title><body><script>' +
+ 'with(parent.ui.browserscope){' +
+ 'var _bTestResults=snapshot,' +
+ '_bC=function(){clearTimeout(_bT);parent.setTimeout(function(){purge();load()},#{refresh}*1e3)},' +
+ '_bT=setTimeout(function(){_bC=function(){};render()},#{timeout}*1e3)' +
+ '}<\/script>' +
+ '<script src=//www.browserscope.org/user/beacon/#{key}?callback=_bC><\/script>',
+ {
+ 'doctype': /css/i.test(document.compatMode) ? '<!doctype html>' : '',
+ 'key': key,
+ 'refresh': timings.refresh,
+ 'timeout': timings.timeout
+ }
+ ));
+ // avoid the IE spinner of doom
+ // http://www.google.com/search?q=IE+throbber+of+doom
+ idoc.close();
+ }
+ else {
+ me.load();
+ }
+ }
+
+ /**
+ * Purges the Browserscope response cache.
+ *
+ * @static
+ * @memberOf ui.browserscope
+ * @param {String} key The key of a single cache entry to clear.
+ */
+ function purge(key) {
+ // we don't pave the cache object with a new one to preserve existing references
+ var responses = cache.responses;
+ if (key) {
+ delete responses[key];
+ } else {
+ forOwn(responses, function(value, key) {
+ delete responses[key];
+ });
+ }
+ }
+
+ /**
+ * Renders the cumulative results table.
+ * (tweak the dimensions and styles to best fit your environment)
+ *
+ * @static
+ * @memberOf ui.browserscope
+ * @param {Object} options The options object.
+ */
+ function render(options) {
+ options || (options = {});
+
+ // coordinates, dimensions, and sizes are in px
+ var areaHeight,
+ cellWidth,
+ data,
+ labels,
+ rowCount,
+ rows,
+ me = ui.browserscope,
+ cont = me.container,
+ responses = cache.responses,
+ visualization = window.google && google.visualization,
+ lastChart = cache.lastChart,
+ chart = cache.lastChart = options.chart || lastChart,
+ lastFilterBy = cache.lastFilterBy,
+ filterBy = cache.lastFilterBy = options.filterBy || lastFilterBy,
+ lastResponse = responses[filterBy],
+ response = responses[filterBy] = 'response' in options ? (response = options.response) && !response.isError() && response : lastResponse,
+ areaWidth = '100%',
+ cellHeight = 80,
+ fontSize = 13,
+ height = 'auto',
+ hTitle = 'operations per second (higher is better)',
+ hTitleHeight = 48,
+ left = 240,
+ legend = 'top',
+ maxChars = 0,
+ maxCharsLimit = 20,
+ maxOps = 0,
+ minHeight = 480,
+ minWidth = cont && cont.offsetWidth || 948,
+ title = '',
+ top = 50,
+ vTitle = '',
+ vTitleWidth = 48,
+ width = minWidth;
+
+ function retry(force) {
+ var action = cache.lastAction;
+ if (force || ui.running) {
+ cache.timers[action] = setTimeout(retry, me.timings.retry * 1e3);
+ } else {
+ me[action].apply(me, action == 'render' ? [options] : []);
+ }
+ }
+
+ // set action to clear any timeouts and prep for retries
+ setAction(response ? 'render' : cache.lastAction);
+
+ // exit early if there is no container element, the data filter has changed or nothing has changed
+ if (!cont || visualization && (filterBy != lastFilterBy ||
+ (!options.force && chart == lastChart && response == lastResponse))) {
+ cont && filterBy != lastFilterBy && load(options);
+ }
+ // retry if response data is empty/errored or the visualization library hasn't loaded yet
+ else if (!response || !visualization) {
+ // set error message for empty/errored response
+ !response && visualization && setMessage(me.texts.error);
+ retry(true);
+ }
+ // visualization chart gallary
+ // http://code.google.com/apis/chart/interactive/docs/gallery.html
+ else if (!ui.running) {
+ cont.className = '';
+ data = cloneData(response.getDataTable());
+ labels = getDataLabels(data);
+ rows = getDataRows(data);
+ rowCount = rows.length;
+ chart = chart.charAt(0).toUpperCase() + chart.slice(1).toLowerCase();
+
+ // adjust data for non-tabular displays
+ if (chart != 'Table') {
+ // remove "# Tests" run count label (without label data the row will be ignored)
+ labels.pop();
+
+ // modify row data
+ each(rows, function(row) {
+ each(getDataCells(row), function(cell, index, cells) {
+ var lastIndex = cells.length - 1;
+
+ // cells[1] through cells[lastIndex - 1] are ops/sec cells
+ if (/^[\d.,]+$/.test(cell.f)) {
+ // assign ops/sec as cell value
+ cell.v = +cell.f.replace(/,/g, '');
+ // add rate to the text
+ cell.f += ' ops/sec';
+ // capture highest ops value to use when computing the left coordinate
+ maxOps = max(maxOps, cell.v);
+ }
+ // cells[0] is the browser name cell
+ // cells[lastIndex] is the run count cell and has no `f` property
+ else if (cell.f) {
+ // add test run count to browser name
+ cell.f += chart == 'Pie' ? '' : ' (' + (cells[lastIndex].v || 1) + ')';
+ // capture longest char count to use when computing left coordinate/cell width
+ maxChars = min(maxCharsLimit, max(maxChars, cell.f.length));
+ }
+ // compute sum of all ops/sec for pie charts
+ if (chart == 'Pie') {
+ if (index == lastIndex) {
+ cells[1].f = formatNumber(cells[1].v) + ' total ops/sec';
+ } else if (index > 1 && typeof cell.v == 'number') {
+ cells[1].v += cell.v;
+ }
+ }
+ // if the browser name matches the user's browser then style it
+ if (cell.p && cell.p.className) {
+ // prefix the browser name with a line separator (\u2028) because it's not rendered
+ // (IE may render a negligible space in the tooltip of browser names truncated with ellipsis)
+ cell.f = uaToken + cell.f;
+ // poll until the chart elements exist and are styled
+ poll(function() { return !addChartStyle(); }, 0.01);
+ }
+ });
+ });
+
+ // adjust captions and chart dimensions
+ if (chart == 'Bar') {
+ // use minHeight to avoid sizing issues when there is only 1 bar
+ height = max(minHeight, top + (rowCount * cellHeight));
+ // compute left by adding the longest approximate vAxis text width and
+ // a right pad of 10px
+ left = (maxChars * (fontSize / 1.6)) + 10;
+ // get percentage of width left after subtracting the chart's left
+ // coordinate and room for the ops/sec number
+ areaWidth = (100 - (((left + 50) / width) * 100)) + '%';
+ }
+ else {
+ // swap captions (the browser list caption is blank to conserve space)
+ vTitle = [hTitle, hTitle = vTitle][0];
+ height = minHeight;
+
+ if (chart == 'Pie') {
+ legend = 'right';
+ title = 'Total operations per second by browser (higher is better)';
+ }
+ else {
+ hTitleHeight = 28;
+ // compute left by getting the sum of the horizontal space wanted
+ // for the vAxis title's width, the approximate vAxis text width, and
+ // the 13px gap between the chart and the right side of the vAxis text
+ left = vTitleWidth + (formatNumber(maxOps).length * (fontSize / 1.6)) + 13;
+ // compute cell width by adding the longest approximate hAxis text
+ // width and wiggle room of 26px
+ cellWidth = (maxChars * (fontSize / 2)) + 26;
+ // use minWidth to avoid clipping the key
+ width = max(minWidth, left + (rowCount * cellWidth));
+ }
+ }
+ // get percentage of height left after subtracting the vertical space wanted
+ // for the hAxis title's height, text size, the chart's top coordinate,
+ // and the 8px gap between the chart and the top of the hAxis text
+ areaHeight = (100 - (((hTitleHeight + fontSize + top + 8) / height) * 100)) + '%';
+ // make chart type recognizable
+ chart += 'Chart';
+ }
+
+ if (rowCount && visualization[chart]) {
+ new visualization[chart](cont).draw(data, {
+ 'colors': ui.browserscope.colors,
+ 'fontSize': fontSize,
+ 'is3D': true,
+ 'legend': legend,
+ 'height': height,
+ 'title': title,
+ 'width': width,
+ 'chartArea': { 'height': areaHeight, 'left': left, 'top': top, 'width': areaWidth },
+ 'hAxis': { 'baseline': 0, 'title': hTitle },
+ 'vAxis': { 'baseline': 0, 'title': vTitle }
+ });
+ } else {
+ setMessage(me.texts.empty);
+ }
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose
+ ui.browserscope = {
+
+ /**
+ * Your Browserscope API key.
+ *
+ * @memberOf ui.browserscope
+ * @type String
+ */
+ 'key': '',
+
+ /**
+ * A flag to indicate if posting is enabled or disabled.
+ *
+ * @memberOf ui.browserscope
+ * @type Boolean
+ */
+ 'postable': true,
+
+ /**
+ * The selector of the element to contain the entire Browserscope UI.
+ *
+ * @memberOf ui.browserscope
+ * @type String
+ */
+ 'selector': '',
+
+ /**
+ * The class name used to style the user's browser name when it appears
+ * in charts.
+ *
+ * @memberOf ui.browserscope
+ * @type String
+ */
+ 'uaClass': 'rt-ua-cur',
+
+ /**
+ * Object containing various timings settings.
+ *
+ * @memberOf ui.browserscope
+ * @type Object
+ */
+ 'timings': {
+
+ /**
+ * The delay between removing abandoned script and iframe elements (secs).
+ *
+ * @memberOf ui.browserscope.timings
+ * @type Number
+ */
+ 'cleanup': 10,
+
+ /**
+ * The delay before refreshing the cumulative results after posting (secs).
+ *
+ * @memberOf ui.browserscope.timings
+ * @type Number
+ */
+ 'refresh': 3,
+
+ /**
+ * The delay between load attempts (secs).
+ *
+ * @memberOf ui.browserscope.timings
+ * @type Number
+ */
+ 'retry': 5,
+
+ /**
+ * The time to wait for a request to finish (secs).
+ *
+ * @memberOf ui.browserscope.timings
+ * @type Number
+ */
+ 'timeout': 10
+ },
+
+ /**
+ * Object containing various text messages.
+ *
+ * @memberOf ui.browserscope
+ * @type Object
+ */
+ 'texts': {
+
+ /**
+ * The text shown when their is no recorded data available to report.
+ *
+ * @memberOf ui.browserscope.texts
+ * @type String
+ */
+ 'empty': 'No data available',
+
+ /**
+ * The text shown when the cumulative results data cannot be retrieved.
+ *
+ * @memberOf ui.browserscope.texts
+ * @type String
+ */
+ 'error': 'The get/post request has failed :(',
+
+ /**
+ * The text shown while waiting for the cumulative results data to load.
+ *
+ * @memberOf ui.browserscope.texts
+ * @type String
+ */
+ 'loading': 'Loading cumulative results data…',
+
+ /**
+ * The text shown while posting the results snapshot to Browserscope.
+ *
+ * @memberOf ui.browserscope.texts
+ * @type String
+ */
+ 'post': 'Posting results snapshot…',
+
+ /**
+ * The text shown while benchmarks are running.
+ *
+ * @memberOf ui.browserscope.texts
+ * @type String
+ */
+ 'wait': 'Benchmarks running. Please wait…'
+ },
+
+ // loads cumulative results table
+ 'load': load,
+
+ // posts benchmark snapshot to Browserscope
+ 'post': post,
+
+ // purges the Browserscope response cache
+ 'purge': purge,
+
+ // renders cumulative results table
+ 'render': render
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ addListener(window, 'load', function() {
+ var me = ui.browserscope,
+ key = me.key,
+ placeholder = key && query(me.selector)[0];
+
+ // create results html
+ if (placeholder) {
+ setHTML(placeholder,
+ '<h1 id=bs-logo><a href=//www.browserscope.org/user/tests/table/#{key}>' +
+ '<span>Browserscope</span></a></h1>' +
+ '<div class=bs-rt><div id=bs-chart></div></div>',
+ { 'key': key });
+
+ // the element the charts are inserted into
+ me.container = query('#bs-chart')[0];
+
+ // Browserscope's UA div is inserted before an element with the id of "bs-ua-script"
+ loadScript('//www.browserscope.org/ua?o=js', me.container).id = 'bs-ua-script';
+
+ // the "autoload" string can be created with
+ // http://code.google.com/apis/loader/autoloader-wizard.html
+ loadScript('//www.google.com/jsapi?autoload=' + encodeURIComponent('{' +
+ 'modules:[{' +
+ 'name:"visualization",' +
+ 'version:1,' +
+ 'packages:["corechart","table"],' +
+ 'callback:ui.browserscope.load' +
+ '}]' +
+ '}'));
+
+ // init garbage collector
+ cleanup();
+ }
+ });
+
+ // hide the chart while benchmarks are running
+ ui.on('start', function() {
+ setMessage(ui.browserscope.texts.wait);
+ })
+ .on('abort', function() {
+ ui.browserscope.render({ 'force': true });
+ });
+
+}(this, document));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/README.md
new file mode 100644
index 0000000..a304b6f
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/README.md
@@ -0,0 +1,6 @@
+# Instructions
+
+Before importing the Flash Builder project please perform the following steps:
+
+ 1. Copy/paste `benchmark.js` into the `src` folder.
+ 2. Copy/paste `AIRIntrospector.js` from some place like `C:\Program Files\Adobe\Adobe Flash Builder 4.5\sdks\4.5.0\frameworks\libs\air\AIRIntrospector.js` into the `src` folder.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/bin-debug/air.swf b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/bin-debug/air.swf
new file mode 100644
index 0000000..5339954
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/bin-debug/air.swf
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air-app.xml b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air-app.xml
new file mode 100644
index 0000000..0ef9707
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air-app.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<application xmlns="http://ns.adobe.com/air/application/2.6">
+ <id>air</id>
+ <filename>air</filename>
+ <name>air</name>
+ <versionNumber>0.0.0</versionNumber>
+ <initialWindow>
+ <content>index.html</content>
+ <resizable>true</resizable>
+ <width>320</width>
+ <height>240</height>
+ <autoOrients>false</autoOrients>
+ <fullScreen>false</fullScreen>
+ <visible>true</visible>
+ </initialWindow>
+</application>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air.mxml b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air.mxml
new file mode 100644
index 0000000..c2c4660
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/air.mxml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
+ xmlns:s="library://ns.adobe.com/flex/spark"
+ xmlns:mx="library://ns.adobe.com/flex/mx">
+ <fx:Declarations>
+ </fx:Declarations>
+</s:WindowedApplication>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/index.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/index.html
new file mode 100644
index 0000000..1a1eb19
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/benchmark.air/src/index.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Benchmark.js Adobe AIR Test Suite</title>
+ <script src="AIRIntrospector.js"></script>
+ <script>
+ var console = air.Introspector.Console;
+ </script>
+ <script src="benchmark.js"></script>
+ </head>
+ <body>
+ <script>
+ (function() {
+ var suite = new Benchmark.Suite;
+
+ // add tests
+ suite.add('RegExp#test', function() {
+ /o/.test('Hello World!');
+ })
+ .add('String#indexOf', function() {
+ 'Hello World!'.indexOf('o') > -1;
+ })
+ // add listeners
+ .on('cycle', function(event) {
+ console.log(event.target);
+ })
+ .on('complete', function() {
+ console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+ })
+ // don't run async to avoid JavaScript security errors
+ // http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7f0e.html
+ .run({ 'async': false });
+ }());
+ </script>
+ </body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/index.html b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/index.html
new file mode 100644
index 0000000..9762fb6
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/index.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Benchmark.js Test Suite</title>
+ <link rel="stylesheet" href="../vendor/qunit/qunit/qunit.css">
+ <style>
+ applet {
+ position: absolute;
+ left: -9999em;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="qunit"></div>
+ <script src="../benchmark.js"></script>
+ <script src="../vendor/platform.js/platform.js"></script>
+ <script>
+ (function() {
+ var hasOwnProperty = function hasOwnProperty(key) {
+ var parent = (this.constructor || Object).prototype;
+ return key in this && !(key in parent && this[key] === parent[key]);
+ };
+ if (typeof {}.hasOwnProperty != 'function') {
+ // redefine for Safari 2, else use the less accurate fallback for others
+ if ({}.__proto__ == Object.prototype) {
+ hasOwnProperty = function hasOwnProperty(key) {
+ var result;
+ this.__proto__ = [this.__proto__, this.__proto__ = null, result = key in this][0];
+ return result;
+ };
+ }
+ Object.prototype.hasOwnProperty = hasOwnProperty;
+ }
+ }());
+
+ // load the nanosecond timer
+ if (!/[?&]nojava=true(?:&|$)/.test(location.search)) {
+ document.write('<applet code="nano" archive="../nano.jar"></applet>');
+ }
+
+ // avoid syntax errors for `QUnit.throws` in older Firefoxes
+ document.write(platform.name == 'Firefox' && /^1\b/.test(platform.version)
+ ? '<script src="../vendor/qunit/qunit/qunit-1.8.0.js"><\/script>'
+ : '<script src="../vendor/qunit/qunit/qunit.js"><\/script>'
+ );
+ </script>
+ <script>
+ // load test.js if not using require.js
+ document.write(/[?&]norequire=true(?:&|$)/.test(location.search)
+ ? '<script src="test.js"><\/script>'
+ : '<script src="../vendor/requirejs/require.js"><\/script>'
+ );
+ </script>
+ <script>
+ // load Benchmark as a module
+ var Benchmark2;
+
+ window.require && require({
+ 'baseUrl': '../vendor/requirejs/',
+ 'urlArgs': 't=' + (+new Date),
+ 'paths': {
+ 'benchmark': '../../benchmark',
+ 'platform': '../platform.js/platform'
+ }
+ },
+ ['benchmark', 'platform'], function(Benchmark, platform) {
+ Benchmark2 = Benchmark;
+ Benchmark2.platform = platform;
+ require(['test.js']);
+ });
+
+ // set a more readable browser name
+ window.onload = function() {
+ var timeoutId = setInterval(function() {
+ var ua = document.getElementById('qunit-userAgent');
+ if (ua) {
+ ua.innerHTML = platform;
+ clearInterval(timeoutId);
+ }
+ }, 15);
+ };
+ </script>
+ </body>
+</html>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/run-test.sh b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/run-test.sh
new file mode 100755
index 0000000..43424e4
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/run-test.sh
@@ -0,0 +1,9 @@
+cd "$(dirname "$0")"
+for cmd in rhino ringo narwhal node; do
+ echo ""
+ echo "Testing in $cmd..."
+ $cmd test.js
+done
+echo ""
+echo "Testing in a browser..."
+open index.html
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/test.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/test.js
new file mode 100644
index 0000000..d694494
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/test/test.js
@@ -0,0 +1,2074 @@
+;(function(window, undefined) {
+ 'use strict';
+
+ /** Use a single load function */
+ var load = typeof require == 'function' ? require : window.load;
+
+ /** The `platform` object to check */
+ var platform =
+ window.platform ||
+ load('../vendor/platform.js/platform.js') ||
+ window.platform;
+
+ /** The unit testing framework */
+ var QUnit =
+ window.QUnit || (
+ window.setTimeout || (window.addEventListener = window.setTimeout = / /),
+ window.QUnit = load('../vendor/qunit/qunit/qunit' + (platform.name == 'Narwhal' ? '-1.8.0' : '') + '.js') || window.QUnit,
+ load('../vendor/qunit-clib/qunit-clib.js'),
+ (window.addEventListener || 0).test && delete window.addEventListener,
+ window.QUnit
+ );
+
+ /** The `Benchmark` constructor to test */
+ var Benchmark =
+ window.Benchmark || (
+ Benchmark = load('../benchmark.js') || window.Benchmark,
+ Benchmark.Benchmark || Benchmark
+ );
+
+ /** API shortcut */
+ var forOwn = Benchmark.forOwn;
+
+ /** Used to get property descriptors */
+ var getDescriptor = Object.getOwnPropertyDescriptor;
+
+ /** Used to set property descriptors */
+ var setDescriptor = Object.defineProperty;
+
+ /** Shortcut used to convert array-like objects to arrays */
+ var slice = [].slice;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */
+ var shadowed = {
+ 'constructor': 1,
+ 'hasOwnProperty': 2,
+ 'isPrototypeOf': 3,
+ 'propertyIsEnumerable': 4,
+ 'toLocaleString': 5,
+ 'toString': 6,
+ 'valueOf': 7
+ };
+
+ /** Used to flag environments/features */
+ var support = {
+ 'descriptors': !!function() {
+ try {
+ var o = {};
+ return (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
+ } catch(e) { }
+ }()
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Skips a given number of tests with a passing result.
+ *
+ * @private
+ * @param {Number} [count=1] The number of tests to skip.
+ */
+ function skipTest(count) {
+ count || (count = 1);
+ while (count--) {
+ ok(true, 'test skipped');
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // init Benchmark.options.minTime
+ Benchmark(function() { throw 0; }).run();
+
+ // set a shorter max time
+ Benchmark.options.maxTime = Benchmark.options.minTime * 5;
+
+ // explicitly call `QUnit.module()` instead of `module()`
+ // in case we are in a CLI environment
+ QUnit.module('Benchmark');
+
+ (function() {
+ test('has the default `Benchmark.platform` value', function() {
+ if (window.document) {
+ equal(String(Benchmark.platform), navigator.userAgent);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('supports loading Benchmark.js as a module', function() {
+ if (window.document && window.require) {
+ equal((Benchmark2 || {}).version, Benchmark.version);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('supports loading Platform.js as a module', function() {
+ if (window.document && window.require) {
+ var platform = (Benchmark2 || {}).platform || {};
+ equal(typeof platform.name, 'string');
+ } else {
+ skipTest(1)
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark constructor');
+
+ (function() {
+ test('creates a new instance when called without the `new` operator', function() {
+ ok(Benchmark() instanceof Benchmark);
+ });
+
+ test('supports passing an options object', function() {
+ var bench = Benchmark({ 'name': 'foo', 'fn': function() { } });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" and "fn" argument', function() {
+ var bench = Benchmark('foo', function() { });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" argument and an options object', function() {
+ var bench = Benchmark('foo', { 'fn': function() { } });
+ ok(bench.fn && bench.name == 'foo');
+ });
+
+ test('supports passing a "name" argument and an options object', function() {
+ var bench = Benchmark('foo', function() { }, { 'id': 'bar' });
+ ok(bench.fn && bench.name == 'foo' && bench.id == 'bar');
+ });
+
+ test('supports passing an empy string for the "fn" options property', function() {
+ var bench = Benchmark({ 'fn': '' }).run();
+ ok(!bench.error);
+ });
+
+ test('detects dead code', function() {
+ var bench = Benchmark(function() { }).run();
+ ok(/setup\(\)/.test(bench.compiled) ? !bench.error : bench.error);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark compilation');
+
+ (function() {
+ test('compiles using the default `Function#toString`', function() {
+ var bench = Benchmark({
+ 'setup': function() { var a = 1; },
+ 'fn': function() { throw a; },
+ 'teardown': function() { a = 2; }
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+
+ test('compiles using a custom "toString" method', function() {
+ var bench = Benchmark({
+ 'setup': function() { },
+ 'fn': function() { },
+ 'teardown': function() { }
+ });
+
+ bench.setup.toString = function() { return 'var a = 1;' };
+ bench.fn.toString = function() { return 'throw a;' };
+ bench.teardown.toString = function() { return 'a = 2;' };
+ bench.run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+
+ test('compiles using a string value', function() {
+ var bench = Benchmark({
+ 'setup': 'var a = 1;',
+ 'fn': 'throw a;',
+ 'teardown': 'a = 2;'
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest();
+ }
+ else {
+ ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark test binding');
+
+ (function() {
+ var count = 0;
+
+ var tests = {
+ 'inlined "setup", "fn", and "teardown"': (
+ 'if(/ops/.test(this))this._fn=true;'
+ ),
+ 'called "fn" and inlined "setup"/"teardown" reached by error': function() {
+ count++;
+ if (/ops/.test(this)) {
+ this._fn = true;
+ }
+ },
+ 'called "fn" and inlined "setup"/"teardown" reached by `return` statement': function() {
+ if (/ops/.test(this)) {
+ this._fn = true;
+ }
+ return;
+ }
+ };
+
+ forOwn(tests, function(fn, title) {
+ test('has correct binding for ' + title, function() {
+ var bench = Benchmark({
+ 'setup': 'if(/ops/.test(this))this._setup=true;',
+ 'fn': fn,
+ 'teardown': 'if(/ops/.test(this))this._teardown=true;',
+ 'onCycle': function() { this.abort(); }
+ }).run();
+
+ var compiled = bench.compiled;
+ if (/setup\(\)/.test(compiled)) {
+ skipTest(3);
+ }
+ else {
+ ok(bench._setup, 'correct binding for "setup"');
+ ok(bench._fn, 'correct binding for "fn"');
+ ok(bench._teardown, 'correct binding for "teardown"');
+ }
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.deepClone');
+
+ (function() {
+ function createCircularObject() {
+ var result = {
+ 'foo': { 'b': { 'foo': { 'c': { } } } },
+ 'bar': { }
+ };
+
+ result.foo.b.foo.c.foo = result;
+ result.bar.b = result.foo.b;
+ return result;
+ }
+
+ function Klass() {
+ this.a = 1;
+ }
+
+ Klass.prototype = { 'b': 1 };
+
+ var notCloneable = {
+ 'an arguments object': arguments,
+ 'an element': window.document && document.body,
+ 'a function': Klass,
+ 'a Klass instance': new Klass
+ };
+
+ var objects = {
+ 'an array': ['a', 'b', 'c', ''],
+ 'an array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
+ 'boolean': false,
+ 'boolean object': Object(false),
+ 'an object': { 'a': 0, 'b': 1, 'c': 3 },
+ 'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
+ 'null': null,
+ 'a number': 3,
+ 'a number object': Object(3),
+ 'a regexp': /x/gim,
+ 'a string': 'x',
+ 'a string object': Object('x'),
+ 'undefined': undefined
+ };
+
+ objects['an array'].length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('clones ' + key + ' correctly', function() {
+ var kind = toString.call(object),
+ clone = Benchmark.deepClone(object);
+
+ if (object == null) {
+ equal(clone, object);
+ } else {
+ deepEqual(clone.valueOf(), object.valueOf());
+ }
+ if (object === Object(object)) {
+ ok(clone !== object);
+ } else {
+ skipTest();
+ }
+ });
+ });
+
+ forOwn(notCloneable, function(object, key) {
+ test('does not clone ' + key, function() {
+ ok(Benchmark.deepClone(object) === object);
+ });
+ });
+
+ test('clones using Klass#deepClone', function() {
+ var object = new Klass;
+ Klass.prototype.deepClone = function() { return new Klass; };
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone !== object && clone instanceof Klass);
+
+ delete Klass.prototype.clone;
+ });
+
+ test('clones problem JScript properties', function() {
+ var clone = Benchmark.deepClone(shadowed);
+ deepEqual(clone, shadowed);
+ });
+
+ test('clones string object with custom property', function() {
+ var object = new String('x');
+ object.x = 1;
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone == 'x' && typeof clone == 'object' && clone.x === 1 && toString.call(clone) == '[object String]');
+ });
+
+ test('clones objects with circular references', function() {
+ var object = createCircularObject(),
+ clone = Benchmark.deepClone(object);
+
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ });
+
+ test('clones non-extensible objects with circular references', function() {
+ if (Object.preventExtensions) {
+ var object = Object.preventExtensions(createCircularObject());
+ Object.preventExtensions(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones sealed objects with circular references', function() {
+ if (Object.seal) {
+ var object = Object.seal(createCircularObject());
+ Object.seal(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones frozen objects with circular references', function() {
+ if (Object.freeze) {
+ var object = Object.freeze(createCircularObject());
+ Object.freeze(object.bar.b);
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+ } else {
+ skipTest(1)
+ }
+ });
+
+ test('clones objects with custom descriptors and circular references', function() {
+ var accessor,
+ descriptor;
+
+ if (support.descriptors) {
+ var object = setDescriptor({}, 'foo', {
+ 'configurable': true,
+ 'value': setDescriptor({}, 'b', {
+ 'writable': true,
+ 'value': setDescriptor({}, 'foo', {
+ 'get': function() { return accessor; },
+ 'set': function(value) { accessor = value; }
+ })
+ })
+ });
+
+ setDescriptor(object, 'bar', { 'value': {} });
+ object.foo.b.foo = { 'c': object };
+ object.bar.b = object.foo.b;
+
+ var clone = Benchmark.deepClone(object);
+ ok(clone !== object &&
+ clone.bar.b === clone.foo.b &&
+ clone !== clone.foo.b.foo.c.foo &&
+ (descriptor = getDescriptor(clone, 'foo')) &&
+ descriptor.configurable && !(descriptor.enumerable && descriptor.writable) &&
+ (descriptor = getDescriptor(clone.foo, 'b')) &&
+ descriptor.writable && !(descriptor.configurable && descriptor.enumerable) &&
+ (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+ descriptor.get && descriptor.set &&
+ (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+ !(descriptor.configurable && descriptor.enumerable && descriptor.writable) &&
+ (descriptor = getDescriptor(clone, 'bar')) &&
+ !(descriptor.configurable && descriptor.enumerable && descriptor.writable));
+ }
+ else {
+ skipTest(1)
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.each');
+
+ (function() {
+ var xpathResult;
+
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
+ 'xpath snapshot': null
+ };
+
+ if (window.document && document.evaluate) {
+ xpathResult = [document.documentElement, document.getElementsByTagName('head')[0], document.body];
+ objects['xpath snapshot'] = document.evaluate('//*[self::html or self::head or self::body]', document, null, 7, null);
+ }
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ if (object) {
+ var args
+ Benchmark.each(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ if (key == 'xpath snapshot') {
+ ok(args[0] === xpathResult[0]);
+ } else {
+ equal(args[0], 'a');
+ }
+ equal(args[1], 0);
+ ok(args[2] === object);
+ }
+ else {
+ skipTest(3);
+ }
+ });
+
+ test('returns the passed object when passing an ' + key, function() {
+ if (object) {
+ var actual = Benchmark.each(object, function() { });
+ ok(actual === object);
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('iterates over all indexes when passing an ' + key, function() {
+ if (object) {
+ var values = [];
+ Benchmark.each(object, function(value) {
+ values.push(value);
+ });
+
+ deepEqual(values, key == 'xpath snapshot' ? xpathResult : ['a', 'b', 'c', '']);
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('exits early when returning `false` when passing an ' + key, function() {
+ if (object) {
+ var values = [];
+ Benchmark.each(object, function(value) {
+ values.push(value);
+ return values.length < 2;
+ });
+
+ deepEqual(values, key == 'xpath snapshot' ? xpathResult.slice(0, 2) : ['a', 'b']);
+ }
+ else {
+ skipTest();
+ }
+ });
+ });
+
+ test('passes the third callback argument as an object', function() {
+ var thirdArg;
+ Benchmark.each('hello', function(value, index, object) {
+ thirdArg = object;
+ });
+
+ ok(thirdArg && typeof thirdArg == 'object');
+ });
+
+ test('iterates over strings by index', function() {
+ var values = [];
+ Benchmark.each('hello', function(value) {
+ values.push(value)
+ });
+
+ deepEqual(values, ['h', 'e', 'l', 'l', 'o']);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.extend');
+
+ (function() {
+ test('allows no source argument', function() {
+ var object = {};
+ equal(Benchmark.extend(object), object);
+ });
+
+ test('allows a single source argument', function() {
+ var source = { 'x': 1, 'y': 1 },
+ actual = Benchmark.extend({}, source);
+
+ deepEqual(Benchmark.extend({}, source), { 'x': 1, 'y': 1 });
+ });
+
+ test('allows multiple source arguments', function() {
+ var source1 = { 'x': 1, 'y': 1 },
+ source2 = { 'y': 2, 'z': 2 },
+ actual = Benchmark.extend({}, source1, source2);
+
+ deepEqual(actual, { 'x': 1, 'y': 2, 'z': 2 });
+ });
+
+ test('will add inherited source properties', function() {
+ function Source() { }
+ Source.prototype.x = 1;
+ deepEqual(Benchmark.extend({}, new Source), { 'x': 1 });
+ });
+
+ test('will add problem JScript properties', function() {
+ deepEqual(Benchmark.extend({}, shadowed), shadowed);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.filter');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.filter(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['a', 0, object]);
+ });
+
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.filter(object, function(value, index) {
+ return index > 0;
+ });
+
+ deepEqual(actual, ['b', 'c', '']);
+ });
+
+ test('iterates over sparse ' + key + 's correctly', function() {
+ var actual = Benchmark.filter(object, function(value) {
+ return value === undefined;
+ });
+
+ deepEqual(actual, []);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.forOwn');
+
+ (function() {
+ function fn() {
+ // no-op
+ }
+
+ function KlassA() {
+ this.a = 1;
+ this.b = 2;
+ this.c = 3;
+ }
+
+ function KlassB() {
+ this.a = 1;
+ this.constructor = 2;
+ this.hasOwnProperty = 3;
+ this.isPrototypeOf = 4;
+ this.propertyIsEnumerable = 5;
+ this.toLocaleString = 6;
+ this.toString = 7;
+ this.valueOf = 8;
+ }
+
+ function KlassC() {
+ // no-op
+ }
+
+ fn.a = 1;
+ fn.b = 2;
+ fn.c = 3;
+
+ KlassC.prototype.a = 1;
+ KlassC.prototype.b = 2;
+ KlassC.prototype.c = 3;
+
+ var objects = {
+ 'an arguments object': arguments,
+ 'a function': fn,
+ 'an object': new KlassA,
+ 'an object shadowing properties on Object.prototype': new KlassB,
+ 'a prototype object': KlassC.prototype,
+ 'a string': 'abc'
+ };
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing ' + key, function() {
+ var args;
+ Benchmark.forOwn(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ equal(typeof args[0], key == 'a string' ? 'string' : 'number');
+ equal(typeof args[1], 'string');
+ equal(args[2] && typeof args[2], key == 'a function' ? 'function' : 'object');
+ });
+
+ test('returns the passed object when passing ' + key, function() {
+ var actual = Benchmark.forOwn(object, function() { });
+ deepEqual(actual, object);
+ });
+
+ test('iterates over own properties when passing ' + key, function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ });
+
+ if (object instanceof KlassB) {
+ deepEqual(values.sort(), [1, 2, 3, 4, 5, 6, 7, 8]);
+ } else if (key == 'a string') {
+ deepEqual(values, ['a', 'b', 'c']);
+ } else {
+ deepEqual(values.sort(), [1, 2, 3]);
+ }
+ });
+
+ test('exits early when returning `false` when passing ' + key, function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ return false;
+ });
+
+ equal(values.length, 1);
+ });
+
+ if (object instanceof KlassB) {
+ test('exits correctly when transitioning to the JScript [[DontEnum]] fix', function() {
+ var values = [];
+ Benchmark.forOwn(object, function(value) {
+ values.push(value);
+ return values.length < 2;
+ });
+
+ equal(values.length, 2);
+ });
+ }
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.formatNumber');
+
+ (function() {
+ test('formats a million correctly', function() {
+ equal(Benchmark.formatNumber(1e6), '1,000,000');
+ });
+
+ test('formats less than 100 correctly', function() {
+ equal(Benchmark.formatNumber(23), '23');
+ });
+
+ test('formats numbers with decimal values correctly', function() {
+ equal(Benchmark.formatNumber(1234.56), '1,234.56');
+ });
+
+ test('formats negative numbers correctly', function() {
+ equal(Benchmark.formatNumber(-1234.56), '-1,234.56');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.hasKey');
+
+ (function() {
+ test('returns `true` for own properties', function() {
+ var object = { 'x': 1 };
+ equal(Benchmark.hasKey(object, 'x'), true);
+ });
+
+ test('returns `false` for inherited properties', function() {
+ equal(Benchmark.hasKey({}, 'toString'), false);
+ });
+
+ test('doesn\'t use an object\'s `hasOwnProperty` method', function() {
+ var object = { 'hasOwnProperty': function() { return true; } };
+ equal(Benchmark.hasKey(object, 'x'), false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.indexOf');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ equal(Benchmark.indexOf(object, 'b'), 1);
+ });
+
+ test('matches values by strict equality when passing an ' + key, function() {
+ equal(Benchmark.indexOf(object, new String('b')), -1);
+ });
+
+ test('iterates over sparse ' + key + 's correctly', function() {
+ equal(Benchmark.indexOf(object, undefined), -1);
+ });
+ });
+
+ test('searches from the given `fromIndex`', function() {
+ var array = ['a', 'b', 'c', 'a'];
+ equal(Benchmark.indexOf(array, 'a', 1), 3);
+ });
+
+ test('handles extreme negative `fromIndex` values correctly', function() {
+ var array = ['a'];
+ array['-1'] = 'z';
+ equal(Benchmark.indexOf(array, 'z', -2), -1);
+ });
+
+ test('handles extreme positive `fromIndex` values correctly', function() {
+ var object = { '0': 'a', '1': 'b', '2': 'c', 'length': 2 };
+ equal(Benchmark.indexOf(object, 'c', 2), -1);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.interpolate');
+
+ (function() {
+ test('replaces tokens correctly', function() {
+ var actual = Benchmark.interpolate('#{greeting} #{location}.', {
+ 'greeting': 'Hello',
+ 'location': 'world'
+ });
+
+ equal(actual, 'Hello world.');
+ });
+
+ test('ignores inherited object properties', function() {
+ var actual = Benchmark.interpolate('x#{toString}', {});
+ equal(actual, 'x#{toString}');
+ });
+
+ test('allows for no template object', function() {
+ var actual = Benchmark.interpolate('x');
+ equal(actual, 'x');
+ });
+
+ test('replaces duplicate tokens', function() {
+ var actual = Benchmark.interpolate('#{x}#{x}#{x}', { 'x': 'a' });
+ equal(actual, 'aaa');
+ });
+
+ test('handles keys containing RegExp special characters', function() {
+ var actual = Benchmark.interpolate('#{.*+?^=!:${}()|[]\\/}', { '.*+?^=!:${}()|[]\\/': 'x' });
+ equal(actual, 'x');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.invoke');
+
+ (function() {
+ var objects = {
+ 'array': ['a', ['b'], 'c', null],
+ 'array-like-object': { '0': 'a', '1': ['b'], '2': 'c', '3': null, 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.invoke(object, 'concat');
+ deepEqual(actual, ['a', ['b'], 'c', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('passes the correct arguments to the invoked method when passing an ' + key, function() {
+ var actual = Benchmark.invoke(object, 'concat', 'x', 'y', 'z');
+ deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('handles options object with callbacks correctly when passing an ' + key, function() {
+ function callback() {
+ callbacks.push(slice.call(arguments));
+ }
+
+ var callbacks = [];
+ var actual = Benchmark.invoke(object, {
+ 'name': 'concat',
+ 'args': ['x', 'y', 'z'],
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback
+ });
+
+ deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+ equal('4' in actual, false);
+
+ equal(callbacks[0].length, 1);
+ equal(callbacks[0][0].target, 'a');
+ deepEqual(callbacks[0][0].currentTarget, object);
+ equal(callbacks[0][0].type, 'start');
+ equal(callbacks[1][0].type, 'cycle');
+ equal(callbacks[5][0].type, 'complete');
+ });
+
+ test('supports queuing when passing an ' + key, function() {
+ var lengths = [];
+ var actual = Benchmark.invoke(object, {
+ 'name': 'concat',
+ 'queued': true,
+ 'args': 'x',
+ 'onCycle': function() {
+ lengths.push(object.length);
+ }
+ });
+
+ deepEqual(lengths, [5, 4, 3, 2]);
+ deepEqual(actual, ['ax', ['b', 'x'], 'cx', undefined, undefined]);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.join');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': '', 'length': 4 },
+ 'object': { 'a': '0', 'b': '1', '': '2' }
+ };
+
+ objects.array.length = 4;
+
+ forOwn(objects, function(object, key) {
+ test('joins correctly using the default separator when passing an ' + key, function() {
+ equal(Benchmark.join(object), key == 'object' ? 'a: 0,b: 1,: 2' : 'a,b,');
+ });
+
+ test('joins correctly using a custom separator when passing an ' + key, function() {
+ equal(Benchmark.join(object, '+', '@'), key == 'object' ? 'a@0+b@1+@2' : 'a+b+');
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.map');
+
+ (function() {
+ var objects = {
+ 'array': ['a', 'b', 'c', ''],
+ 'array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.map(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['a', 0, object]);
+ });
+
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.map(object, function(value, index) {
+ return value + index;
+ });
+
+ deepEqual(actual, ['a0', 'b1', 'c2', '3', undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('produces an array of the correct length for sparse ' + key + 's', function() {
+ equal(Benchmark.map(object, function() { }).length, 5);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.pluck');
+
+ (function() {
+ var objects = {
+ 'array': [{ '_': 'a' }, { '_': 'b' }, { '_': 'c' }, null],
+ 'array-like-object': { '0': { '_': 'a' }, '1': { '_': 'b' }, '2': { '_': 'c' }, '3': null, 'length': 5 }
+ };
+
+ objects.array.length = 5;
+
+ forOwn(objects, function(object, key) {
+ test('produces the correct result when passing an ' + key, function() {
+ var actual = Benchmark.pluck(object, '_');
+ deepEqual(actual, ['a', 'b', 'c', undefined, undefined]);
+ equal('4' in actual, false);
+ });
+
+ test('produces the correct result for non-existent keys when passing an ' + key, function() {
+ var actual = Benchmark.pluck(object, 'non-existent');
+ deepEqual(actual, [undefined, undefined, undefined, undefined, undefined]);
+ equal('4' in actual, false);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.reduce');
+
+ (function() {
+ var objects = {
+ 'array': ['b', 'c', ''],
+ 'array-like-object': { '0': 'b', '1': 'c', '2': '', 'length': 4 }
+ };
+
+ objects.array.length = 4;
+
+ forOwn(objects, function(object, key) {
+ test('passes the correct arguments when passing an ' + key, function() {
+ var args;
+ Benchmark.reduce(object, function() {
+ args || (args = slice.call(arguments));
+ }, 'a');
+
+ deepEqual(args, ['a', 'b', 0, object]);
+ });
+
+ test('accumulates correctly when passing an ' + key, function() {
+ var actual = Benchmark.reduce(object, function(string, value) {
+ return string + value;
+ }, 'a');
+
+ equal(actual, 'abc');
+ });
+
+ test('handles arguments with no initial value correctly when passing an ' + key, function() {
+ var args;
+ Benchmark.reduce(object, function() {
+ args || (args = slice.call(arguments));
+ });
+
+ deepEqual(args, ['b', 'c', 1, object]);
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark#clone');
+
+ (function() {
+ var bench = Benchmark(function() { this.count += 0; }).run();
+
+ test('produces the correct result passing no arguments', function() {
+ var clone = bench.clone();
+ deepEqual(clone, bench);
+ ok(clone.stats != bench.stats && clone.times != bench.times && clone.options != bench.options);
+ });
+
+ test('produces the correct result passing a data object', function() {
+ var clone = bench.clone({ 'fn': '', 'name': 'foo' });
+ ok(clone.fn === '' && clone.options.fn === '');
+ ok(clone.name == 'foo' && clone.options.name == 'foo');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark#run');
+
+ (function() {
+ var data = { 'onComplete': 0, 'onCycle': 0, 'onStart': 0 };
+
+ var bench = Benchmark({
+ 'fn': function() {
+ this.count += 0;
+ },
+ 'onStart': function() {
+ data.onStart++;
+ },
+ 'onComplete': function() {
+ data.onComplete++;
+ }
+ })
+ .run();
+
+ test('onXYZ callbacks should not be triggered by internal benchmark clones', function() {
+ equal(data.onStart, 1);
+ equal(data.onComplete, 1);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ forOwn({
+ 'Benchmark': Benchmark,
+ 'Benchmark.Suite': Benchmark.Suite
+ },
+ function(Constructor, namespace) {
+
+ QUnit.module(namespace + '#emit');
+
+ (function() {
+ test('emits passed arguments', function() {
+ var args,
+ object = Constructor();
+
+ object.on('args', function() { args = slice.call(arguments, 1); });
+ object.emit('args', 'a', 'b', 'c');
+ deepEqual(args, ['a', 'b', 'c']);
+ });
+
+ test('emits with no listeners', function() {
+ var event = Benchmark.Event('empty'),
+ object = Constructor();
+
+ object.emit(event);
+ equal(event.cancelled, false);
+ });
+
+ test('emits with an event type of "toString"', function() {
+ var event = Benchmark.Event('toString'),
+ object = Constructor();
+
+ object.emit(event);
+ equal(event.cancelled, false);
+ });
+
+ test('returns the last listeners returned value', function() {
+ var event = Benchmark.Event('result'),
+ object = Constructor();
+
+ object.on('result', function() { return 'x'; });
+ object.on('result', function() { return 'y'; });
+ equal(object.emit(event), 'y');
+ });
+
+ test('aborts the emitters listener iteration when `event.aborted` is `true`', function() {
+ var event = Benchmark.Event('aborted'),
+ object = Constructor();
+
+ object.on('aborted', function(event) {
+ event.aborted = true;
+ return false;
+ });
+
+ object.on('aborted', function(event) {
+ // should not get here
+ event.aborted = false;
+ return true;
+ });
+
+ equal(object.emit(event), false);
+ equal(event.aborted, true);
+ });
+
+ test('cancels the event if a listener explicitly returns `false`', function() {
+ var event = Benchmark.Event('cancel'),
+ object = Constructor();
+
+ object.on('cancel', function() { return false; });
+ object.on('cancel', function() { return true; });
+ object.emit(event);
+ equal(event.cancelled, true);
+ });
+
+ test('uses a shallow clone of the listeners when emitting', function() {
+ var event,
+ listener2 = function(eventObject) { eventObject.listener2 = true },
+ object = Constructor();
+
+ object.on('shallowclone', function(eventObject) {
+ event = eventObject;
+ object.off(event.type, listener2);
+ })
+ .on('shallowclone', listener2)
+ .emit('shallowclone');
+
+ ok(event.listener2);
+ });
+
+ test('emits a custom event object', function() {
+ var event = Benchmark.Event('custom'),
+ object = Constructor();
+
+ object.on('custom', function(eventObject) { eventObject.touched = true; });
+ object.emit(event);
+ ok(event.touched);
+ });
+
+ test('sets `event.result` correctly', function() {
+ var event = Benchmark.Event('result'),
+ object = Constructor();
+
+ object.on('result', function() { return 'x'; });
+ object.emit(event);
+ equal(event.result, 'x');
+ });
+
+ test('sets `event.type` correctly', function() {
+ var event,
+ object = Constructor();
+
+ object.on('type', function(eventObj) {
+ event = eventObj;
+ });
+
+ object.emit('type');
+ equal(event.type, 'type');
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#listeners');
+
+ (function() {
+ test('returns the correct listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ deepEqual(object.listeners('x'), [listener]);
+ });
+
+ test('returns an array and initializes previously uninitialized listeners', function() {
+ var object = Constructor();
+ deepEqual(object.listeners('x'), []);
+ deepEqual(object.events, { 'x': [] });
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#off');
+
+ (function() {
+ test('returns the benchmark', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ equal(object.off('x', listener), object);
+ });
+
+ test('will ignore inherited properties of the event cache', function() {
+ var Dummy = function() { },
+ listener = function() { },
+ object = Constructor();
+
+ Dummy.prototype.x = [listener];
+ object.events = new Dummy;
+
+ object.off('x', listener);
+ deepEqual(object.events.x, [listener]);
+ });
+
+ test('handles an event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.off('x', listener);
+ deepEqual(object.events.x, []);
+ });
+
+ test('handles unregistering duplicate listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('x', listener);
+
+ var events = object.events;
+ object.off('x', listener);
+ deepEqual(events.x, [listener]);
+
+ object.off('x', listener);
+ deepEqual(events.x, []);
+ });
+
+ test('handles a non-registered listener', function() {
+ var object = Constructor();
+ object.off('x', function() { });
+ equal(object.events, undefined);
+ });
+
+ test('handles space separated event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('y', listener);
+
+ var events = object.events;
+ object.off('x y', listener);
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ });
+
+ test('handles space separated event type and no listener', function() {
+ var listener1 = function() { },
+ listener2 = function() { },
+ object = Constructor();
+
+ object.on('x', listener1);
+ object.on('y', listener2);
+
+ var events = object.events;
+ object.off('x y');
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ });
+
+ test('handles no arguments', function() {
+ var listener1 = function() { },
+ listener2 = function() { },
+ listener3 = function() { },
+ object = Constructor();
+
+ object.on('x', listener1);
+ object.on('y', listener2);
+ object.on('z', listener3);
+
+ var events = object.events;
+ object.off();
+ deepEqual(events.x, []);
+ deepEqual(events.y, []);
+ deepEqual(events.z, []);
+ });
+ }());
+
+ /*------------------------------------------------------------------------*/
+
+ QUnit.module(namespace + '#on');
+
+ (function() {
+ test('returns the benchmark', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ equal(object.on('x', listener), object);
+ });
+
+ test('will ignore inherited properties of the event cache', function() {
+ var Dummy = function() { },
+ listener1 = function() { },
+ listener2 = function() { },
+ object = Constructor();
+
+ Dummy.prototype.x = [listener1];
+ object.events = new Dummy;
+
+ object.on('x', listener2);
+ deepEqual(object.events.x, [listener2]);
+ });
+
+ test('handles an event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ deepEqual(object.events.x, [listener]);
+ });
+
+ test('handles registering duplicate listeners', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x', listener);
+ object.on('x', listener);
+ deepEqual(object.events.x, [listener, listener]);
+ });
+
+ test('handles space separated event type and listener', function() {
+ var listener = function() { },
+ object = Constructor();
+
+ object.on('x y', listener);
+
+ var events = object.events;
+ deepEqual(events.x, [listener]);
+ deepEqual(events.y, [listener]);
+ });
+ }());
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#abort');
+
+ (function() {
+ test('igores abort calls when the suite isn\'t running', function() {
+ var fired = false;
+ var suite = Benchmark.Suite('suite', {
+ 'onAbort': function() { fired = true; }
+ });
+
+ suite.add('foo', function() { });
+ suite.abort();
+ equal(fired, false);
+ });
+
+ test('ignores abort calls from `Benchmark.Suite#reset` when the suite isn\'t running', function() {
+ var fired = false;
+ var suite = Benchmark.Suite('suite', {
+ 'onAbort': function() { fired = true; }
+ });
+
+ suite.add('foo', function() { });
+ suite.reset();
+ equal(fired, false);
+ });
+
+ asyncTest('emits an abort event when running', function() {
+ var fired = false;
+
+ Benchmark.Suite({
+ 'onAbort': function() { fired = true; }
+ })
+ .on('start', function() {
+ this.abort();
+ })
+ .on('complete', function() {
+ ok(fired);
+ QUnit.start();
+ })
+ .add(function(){ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('emits an abort event after calling `Benchmark.Suite#reset`', function() {
+ var fired = false;
+
+ Benchmark.Suite({
+ 'onAbort': function() { fired = true; }
+ })
+ .on('start', function() {
+ this.reset();
+ })
+ .on('complete', function() {
+ ok(fired);
+ QUnit.start();
+ })
+ .add(function(){ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should abort deferred benchmark', function() {
+ var fired = false,
+ suite = Benchmark.Suite();
+
+ suite.on('complete', function() {
+ equal(fired, false);
+ QUnit.start();
+ })
+ .add('a', {
+ 'defer': true,
+ 'fn': function(deferred) {
+ // avoid test inlining
+ suite.name;
+ // delay resolve
+ setTimeout(function() {
+ deferred.resolve();
+ suite.abort();
+ }, 10);
+ }
+ })
+ .add('b', {
+ 'defer': true,
+ 'fn': function(deferred) {
+ // avoid test inlining
+ suite.name;
+ // delay resolve
+ setTimeout(function() {
+ deferred.resolve();
+ fired = true;
+ }, 10);
+ }
+ })
+ .run();
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#concat');
+
+ (function() {
+ var args = arguments;
+
+ test('doesn\'t treat an arguments object like an array', function() {
+ var suite = Benchmark.Suite();
+ deepEqual(suite.concat(args), [args]);
+ });
+
+ test('flattens array arguments', function() {
+ var suite = Benchmark.Suite();
+ deepEqual(suite.concat([1, 2], 3, [4, 5]), [1, 2, 3, 4, 5]);
+ });
+
+ test('supports concating sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[2] = 2;
+ suite.length = 3;
+
+ var actual = suite.concat(3);
+ deepEqual(actual, [0, undefined, 2, 3]);
+ equal('1' in actual, false);
+ });
+
+ test('supports sparse arrays as arguments', function() {
+ var suite = Benchmark.Suite(),
+ sparse = [];
+
+ sparse[0] = 0;
+ sparse[2] = 2;
+ sparse.length = 3;
+
+ var actual = suite.concat(sparse);
+ deepEqual(actual, [0, undefined, 2]);
+ equal('1' in actual, false);
+ });
+
+ test('creates a new array', function() {
+ var suite = Benchmark.Suite();
+ ok(suite.concat(1) !== suite);
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#reverse');
+
+ (function() {
+ test('reverses the element order', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.reverse();
+ equal(actual, suite);
+ deepEqual(slice.call(actual), [1, 0]);
+ });
+
+ test('supports reversing sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[2] = 2;
+ suite.length = 3;
+
+ var actual = suite.reverse();
+ equal(actual, suite);
+ deepEqual(slice.call(actual), [2, undefined, 0]);
+ equal('1' in actual, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#shift');
+
+ (function() {
+ test('removes the first element', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.shift();
+ equal(actual, 0);
+ deepEqual(slice.call(suite), [1]);
+ });
+
+ test('shifts an object with no elements', function() {
+ var suite = Benchmark.Suite(),
+ actual = suite.shift();
+
+ equal(actual, undefined);
+ deepEqual(slice.call(suite), []);
+ });
+
+ test('should have no elements when length is 0 after shift', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1;
+ suite.shift();
+
+ // ensure element is removed
+ equal('0' in suite, false);
+ equal(suite.length, 0);
+ });
+
+ test('supports shifting sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 1;
+ suite[3] = 3;
+ suite.length = 4;
+
+ var actual = suite.shift();
+ equal(actual, undefined);
+ deepEqual(slice.call(suite), [1, undefined, 3]);
+ equal('1' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#slice');
+
+ (function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite[2] = 2;
+ suite[3] = 3;
+ suite.length = 4;
+
+ test('works with no arguments', function() {
+ var actual = suite.slice();
+ deepEqual(actual, [0, 1, 2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with positive `start` argument', function() {
+ var actual = suite.slice(2);
+ deepEqual(actual, [2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with positive `start` and `end` arguments', function() {
+ var actual = suite.slice(1, 3);
+ deepEqual(actual, [1, 2]);
+ ok(suite !== actual);
+ });
+
+ test('works with `end` values exceeding length', function() {
+ var actual = suite.slice(1, 10);
+ deepEqual(actual, [1, 2, 3]);
+ ok(suite !== actual);
+ });
+
+ test('works with negative `start` and `end` arguments', function() {
+ var actual = suite.slice(-3, -1);
+ deepEqual(actual, [1, 2]);
+ ok(suite !== actual);
+ });
+
+ test('works with an extreme negative `end` value', function() {
+ var actual = suite.slice(1, -10);
+ deepEqual(actual, []);
+ equal('-1' in actual, false);
+ ok(suite !== actual);
+ });
+
+ test('supports slicing sparse arrays', function() {
+ var sparse = Benchmark.Suite();
+ sparse[1] = 1;
+ sparse[3] = 3;
+ sparse.length = 4;
+
+ var actual = sparse.slice(0, 2);
+ deepEqual(actual, [undefined, 1]);
+ equal('0' in actual, false);
+
+ actual = sparse.slice(1);
+ deepEqual(actual, [1, undefined, 3]);
+ equal('1' in actual, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#splice');
+
+ (function() {
+ test('works with no arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1;
+
+ var actual = suite.splice();
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0]);
+ });
+
+ test('works with only the `start` argument', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 1;
+ suite.length = 2;
+
+ var actual = suite.splice(1);
+ deepEqual(actual, [1]);
+ deepEqual(slice.call(suite), [0]);
+ });
+
+ test('should have no elements when length is 0 after splice', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite.length = 1
+ suite.splice(0, 1);
+
+ // ensure element is removed
+ equal('0' in suite, false);
+ equal(suite.length, 0);
+ });
+
+ test('works with positive `start` argument', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 0, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('works with positive `start` and `deleteCount` arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 1, 1, 2);
+ deepEqual(actual, [3]);
+ deepEqual(slice.call(suite), [0, 1, 2]);
+ });
+
+ test('works with `deleteCount` values exceeding length', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(1, 10, 1, 2);
+ deepEqual(actual, [3]);
+ deepEqual(slice.call(suite), [0, 1, 2]);
+ });
+
+ test('works with negative `start` and `deleteCount` arguments', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(-1, -1, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('works with an extreme negative `deleteCount` value', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 0;
+ suite[1] = 3;
+ suite.length = 2;
+
+ var actual = suite.splice(0, -10, 1, 2);
+ deepEqual(actual, []);
+ deepEqual(slice.call(suite), [1, 2, 0, 3]);
+ });
+
+ test('supports splicing sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 1;
+ suite[3] = 3;
+ suite.length = 4;
+
+ var actual = suite.splice(1, 2, 1, 2);
+ deepEqual(actual, [1, undefined]);
+ equal(actual.length, 2);
+ deepEqual(slice.call(suite), [undefined, 1, 2, 3]);
+ equal('0' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite#unshift');
+
+ (function() {
+ test('adds a first element', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 1;
+ suite.length = 1;
+
+ var actual = suite.unshift(0);
+ equal(actual, 2);
+ deepEqual(slice.call(suite), [0, 1]);
+ });
+
+ test('adds multiple elements to the front', function() {
+ var suite = Benchmark.Suite();
+ suite[0] = 3;
+ suite.length = 1;
+
+ var actual = suite.unshift(0, 1, 2);
+ equal(actual, 4);
+ deepEqual(slice.call(suite), [0, 1, 2, 3]);
+ });
+
+ test('supports unshifting sparse arrays', function() {
+ var suite = Benchmark.Suite();
+ suite[1] = 2;
+ suite.length = 2;
+
+ var actual = suite.unshift(0);
+ equal(actual, 3);
+ deepEqual(slice.call(suite), [0, undefined, 2]);
+ equal('1' in suite, false);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite filtered results onComplete');
+
+ (function() {
+ var count = 0,
+ suite = Benchmark.Suite();
+
+ suite.add('a', function() {
+ count++;
+ })
+ .add('b', function() {
+ for (var i = 0; i < 1e6; i++) {
+ count++;
+ }
+ })
+ .add('c', function() {
+ throw new TypeError;
+ });
+
+ asyncTest('should filter by fastest', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('fastest').pluck('name'), ['a']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should filter by slowest', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('slowest').pluck('name'), ['b']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+
+ asyncTest('should filter by successful', function() {
+ suite.on('complete', function() {
+ suite.off();
+ deepEqual(this.filter('successful').pluck('name'), ['a', 'b']);
+ QUnit.start();
+ })
+ .run({ 'async': true });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.Suite event flow');
+
+ (function() {
+ var events = [],
+ callback = function(event) { events.push(event); };
+
+ var suite = Benchmark.Suite('suite', {
+ 'onAdd': callback,
+ 'onAbort': callback,
+ 'onClone': callback,
+ 'onError': callback,
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback,
+ 'onReset': callback
+ })
+ .add('bench', function() {
+ throw null;
+ }, {
+ 'onAbort': callback,
+ 'onClone': callback,
+ 'onError': callback,
+ 'onStart': callback,
+ 'onCycle': callback,
+ 'onComplete': callback,
+ 'onReset': callback
+ })
+ .run({ 'async': false });
+
+ // first Suite#onAdd
+ test('should emit the suite "add" event first', function() {
+ var event = events[0];
+ ok(event.type == 'add' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // next we start the Suite because no reset was needed
+ test('should emit the suite "start" event', function() {
+ var event = events[1];
+ ok(event.type == 'start' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // and so start the first benchmark
+ test('should emit the benchmark "start" event', function() {
+ var event = events[2];
+ ok(event.type == 'start' && event.currentTarget.name == 'bench');
+ });
+
+ // oh no! we abort because of an error
+ test('should emit the benchmark "error" event', function() {
+ var event = events[3];
+ ok(event.type == 'error' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark error triggered
+ test('should emit the benchmark "abort" event', function() {
+ var event = events[4];
+ ok(event.type == 'abort' && event.currentTarget.name == 'bench');
+ });
+
+ // we reset the benchmark as part of the abort
+ test('should emit the benchmark "reset" event', function() {
+ var event = events[5];
+ ok(event.type == 'reset' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark is cycle is finished
+ test('should emit the benchmark "cycle" event', function() {
+ var event = events[6];
+ ok(event.type == 'cycle' && event.currentTarget.name == 'bench');
+ });
+
+ // benchmark is complete
+ test('should emit the benchmark "complete" event', function() {
+ var event = events[7];
+ ok(event.type == 'complete' && event.currentTarget.name == 'bench');
+ });
+
+ // the benchmark error triggers a Suite error
+ test('should emit the suite "error" event', function() {
+ var event = events[8];
+ ok(event.type == 'error' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // the Suite cycle finishes
+ test('should emit the suite "cycle" event', function() {
+ var event = events[9];
+ ok(event.type == 'cycle' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ // the Suite completes
+ test('finally it should emit the suite "complete" event', function() {
+ var event = events[10];
+ ok(event.type == 'complete' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+ });
+
+ test('emitted all expected events', function() {
+ ok(events.length == 11);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Deferred benchmarks');
+
+ (function() {
+ asyncTest('should run a deferred benchmark correctly', function() {
+ Benchmark(function(deferred) {
+ setTimeout(function() { deferred.resolve(); }, 1e3);
+ }, {
+ 'defer': true,
+ 'onComplete': function() {
+ equal(this.hz.toFixed(0), 1);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should run with string values for "fn", "setup", and "teardown"', function() {
+ Benchmark({
+ 'defer': true,
+ 'setup': 'var x = [3, 2, 1];',
+ 'fn': 'setTimeout(function() { x.sort(); deferred.resolve(); }, 10);',
+ 'teardown': 'x.length = 0;',
+ 'onComplete': function() {
+ ok(true);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should run recursively', function() {
+ Benchmark({
+ 'defer': true,
+ 'setup': 'var x = [3, 2, 1];',
+ 'fn': 'for (var i = 0; i < 100; i++) x[ i % 2 ? "sort" : "reverse" ](); deferred.resolve();',
+ 'teardown': 'x.length = 0;',
+ 'onComplete': function() {
+ ok(true);
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+
+ asyncTest('should execute "setup", "fn", and "teardown" in correct order', function() {
+ var fired = [];
+
+ Benchmark({
+ 'defer': true,
+ 'setup': function() {
+ fired.push('setup');
+ },
+ 'fn': function(deferred) {
+ fired.push('fn');
+ setTimeout(function() { deferred.resolve(); }, 10);
+ },
+ 'teardown': function() {
+ fired.push('teardown');
+ },
+ 'onComplete': function() {
+ var actual = fired.join().replace(/(fn,)+/g, '$1').replace(/(setup,fn,teardown(?:,|$))+/, '$1');
+ equal(actual, 'setup,fn,teardown');
+ QUnit.start();
+ }
+ })
+ .run();
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('Benchmark.deepClone');
+
+ (function() {
+ asyncTest('avoids call stack limits', function() {
+ var result,
+ count = 0,
+ object = {},
+ recurse = function() { count++; recurse(); };
+
+ setTimeout(function() {
+ ok(result, 'avoids call stack limits (stack limit is ' + (count - 1) + ')');
+ QUnit.start();
+ }, 15);
+
+ if (toString.call(window.java) == '[object JavaPackage]') {
+ // Java throws uncatchable errors on call stack overflows, so to avoid
+ // them I chose a number higher than Rhino's call stack limit without
+ // dynamically testing for the actual limit
+ count = 3e3;
+ } else {
+ try { recurse(); } catch(e) { }
+ }
+
+ // exceed limit
+ count++;
+ for (var i = 0, sub = object; i <= count; i++) {
+ sub = sub[i] = {};
+ }
+
+ try {
+ for (var i = 0, sub = Benchmark.deepClone(object); sub = sub[i]; i++) { }
+ result = --i == count;
+ } catch(e) { }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ // explicitly call `QUnit.start()` for Narwhal, Rhino, and RingoJS
+ if (!window.document) {
+ QUnit.start();
+ }
+}(typeof global == 'object' && global || this));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/LICENSE.txt b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/LICENSE.txt
new file mode 100644
index 0000000..dadad22
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/README.md
new file mode 100644
index 0000000..7e2665e
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/README.md
@@ -0,0 +1,33 @@
+# Docdown <sup>v1.0.0</sup>
+
+A simple JSDoc to Markdown documentation generator.
+
+## Documentation
+
+The documentation for Docdown can be viewed here: [/doc/README.md](https://github.com/jdalton/docdown/blob/master/doc/README.md#readme)
+
+For a list of upcoming features, check out our [roadmap](https://github.com/jdalton/docdown/wiki/Roadmap).
+
+## Installation and usage
+
+Usage example:
+
+```php
+require("docdown.php");
+
+// generate Markdown
+$markdown = docdown(array(
+ "path" => $filepath,
+ "url" => "https://github.com/username/project/blob/master/my.js"
+));
+```
+
+## Author
+
+* [John-David Dalton](http://allyoucanleet.com/)
+ [](https://twitter.com/jdalton "Follow @jdalton on Twitter")
+
+## Contributors
+
+* [Mathias Bynens](http://mathiasbynens.be/)
+ [](https://twitter.com/mathias "Follow @mathias on Twitter")
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/docdown.php b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/docdown.php
new file mode 100644
index 0000000..6a960d4
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/docdown.php
@@ -0,0 +1,38 @@
+<?php
+/*!
+ * Docdown v1.0.0-pre
+ * Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
+ * Available under MIT license <http://mths.be/mit>
+ */
+require(dirname(__FILE__) . '/src/DocDown/Generator.php');
+
+/**
+ * Generates Markdown from JSDoc entries in a given file.
+ *
+ * @param {Array} [$options=array()] The options array.
+ * @returns {String} The generated Markdown.
+ * @example
+ *
+ * // specify a file path
+ * $markdown = docdown(array(
+ * // path to js file
+ * 'path' => $filepath,
+ * // url used to reference line numbers in code
+ * 'url' => 'https://github.com/username/project/blob/master/my.js'
+ * ));
+ *
+ * // or pass raw js
+ * $markdown = docdown(array(
+ * // raw JavaScript source
+ * 'source' => $rawJS,
+ * // documentation title
+ * 'title' => 'My API Documentation',
+ * // url used to reference line numbers in code
+ * 'url' => 'https://github.com/username/project/blob/master/my.js'
+ * ));
+ */
+function docdown( $options = array() ) {
+ $gen = new Generator($options);
+ return $gen->generate();
+}
+?>
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Entry.php b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Entry.php
new file mode 100644
index 0000000..e7924c0
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Entry.php
@@ -0,0 +1,304 @@
+<?php
+
+/**
+ * A class to simplify parsing a single JSDoc entry.
+ */
+class Entry {
+
+ /**
+ * The documentation entry.
+ *
+ * @memberOf Entry
+ * @type String
+ */
+ public $entry = '';
+
+ /**
+ * The language highlighter used for code examples.
+ *
+ * @memberOf Entry
+ * @type String
+ */
+ public $lang = '';
+
+ /**
+ * The source code.
+ *
+ * @memberOf Entry
+ * @type String
+ */
+ public $source = '';
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The Entry constructor.
+ *
+ * @constructor
+ * @param {String} $entry The documentation entry to analyse.
+ * @param {String} $source The source code.
+ * @param {String} $lang The language highlighter used for code examples.
+ */
+ public function __construct( $entry, $source, $lang = 'js' ) {
+ $this->entry = $entry;
+ $this->lang = $lang;
+ $this->source = str_replace(PHP_EOL, "\n", $source);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Extracts the documentation entries from source code.
+ *
+ * @static
+ * @memberOf Entry
+ * @param {String} $source The source code.
+ * @returns {Array} The array of entries.
+ */
+ public static function getEntries( $source ) {
+ preg_match_all('#/\*\*(?![-!])[\s\S]*?\*/\s*[^\n]+#', $source, $result);
+ return array_pop($result);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if the entry is a function reference.
+ *
+ * @private
+ * @memberOf Entry
+ * @returns {Boolean} Returns `true` if the entry is a function reference, else `false`.
+ */
+ private function isFunction() {
+ return !!(
+ $this->isCtor() ||
+ count($this->getParams()) ||
+ count($this->getReturns()) ||
+ preg_match('/\*\s*@function\b/', $this->entry)
+ );
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Extracts the function call from the entry.
+ *
+ * @memberOf Entry
+ * @returns {String} The function call.
+ */
+ public function getCall() {
+ preg_match('#\*/\s*(?:function ([^(]*)|(.*?)(?=[:=,]|return\b))#', $this->entry, $result);
+ if ($result = array_pop($result)) {
+ $result = array_pop(explode('var ', trim(trim(array_pop(explode('.', $result))), "'")));
+ }
+ // resolve name
+ // avoid $this->getName() because it calls $this->getCall()
+ preg_match('#\*\s*@name\s+([^\n]+)#', $this->entry, $name);
+ if (count($name)) {
+ $name = trim($name[1]);
+ } else {
+ $name = $result;
+ }
+ // compile function call syntax
+ if ($this->isFunction()) {
+ // compose parts
+ $result = array($result);
+ $params = $this->getParams();
+ foreach ($params as $param) {
+ $result[] = $param[1];
+ }
+ // format
+ $result = $name .'('. implode(array_slice($result, 1), ', ') .')';
+ $result = str_replace(', [', ' [, ', str_replace('], [', ', ', $result));
+ }
+ return $result ? $result : $name;
+ }
+
+ /**
+ * Extracts the entry description.
+ *
+ * @memberOf Entry
+ * @returns {String} The entry description.
+ */
+ public function getDesc() {
+ preg_match('#/\*\*(?:\s*\*)?([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result);
+ if (count($result)) {
+ $type = $this->getType();
+ $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1]));
+ $result = ($type == 'Function' ? '' : '(' . str_replace('|', ', ', trim($type, '{}')) . '): ') . $result;
+ }
+ return $result;
+ }
+
+ /**
+ * Extracts the entry `example` data.
+ *
+ * @memberOf Entry
+ * @returns {String} The entry `example` data.
+ */
+ public function getExample() {
+ preg_match('#\*\s*@example\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result);
+ if (count($result)) {
+ $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', "\n", $result[1]));
+ $result = '```' . $this->lang . "\n" . $result . "\n```";
+ }
+ return $result;
+ }
+
+ /**
+ * Resolves the line number of the entry.
+ *
+ * @memberOf Entry
+ * @returns {Number} The line number.
+ */
+ public function getLineNumber() {
+ preg_match_all('/\n/', substr($this->source, 0, strrpos($this->source, $this->entry) + strlen($this->entry)), $lines);
+ return count(array_pop($lines)) + 1;
+ }
+
+ /**
+ * Extracts the entry `member` data.
+ *
+ * @memberOf Entry
+ * @param {Number} $index The index of the array value to return.
+ * @returns {Array|String} The entry `member` data.
+ */
+ public function getMembers( $index = null ) {
+ preg_match('#\*\s*@member(?:Of)?\s+([^\n]+)#', $this->entry, $result);
+ if (count($result)) {
+ $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1]));
+ $result = preg_split('/,\s*/', $result);
+ }
+ return $index !== null ? @$result[$index] : $result;
+ }
+
+ /**
+ * Extracts the entry `name` data.
+ *
+ * @memberOf Entry
+ * @returns {String} The entry `name` data.
+ */
+ public function getName() {
+ preg_match('#\*\s*@name\s+([^\n]+)#', $this->entry, $result);
+ if (count($result)) {
+ $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1]));
+ } else {
+ $result = array_shift(explode('(', $this->getCall()));
+ }
+ return $result;
+ }
+
+ /**
+ * Extracts the entry `param` data.
+ *
+ * @memberOf Entry
+ * @param {Number} $index The index of the array value to return.
+ * @returns {Array} The entry `param` data.
+ */
+ public function getParams( $index = null ) {
+ preg_match_all('#\*\s*@param\s+\{([^}]+)\}\s+(\[.+\]|[$\w]+)\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#i', $this->entry, $result);
+ if (count($result = array_filter(array_slice($result, 1)))) {
+ // repurpose array
+ foreach ($result as $param) {
+ foreach ($param as $key => $value) {
+ if (!is_array($result[0][$key])) {
+ $result[0][$key] = array();
+ }
+ $result[0][$key][] = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $value));
+ }
+ }
+ $result = $result[0];
+ }
+ return $index !== null ? @$result[$index] : $result;
+ }
+
+ /**
+ * Extracts the entry `returns` data.
+ *
+ * @memberOf Entry
+ * @returns {String} The entry `returns` data.
+ */
+ public function getReturns() {
+ preg_match('#\*\s*@returns\s+\{([^}]+)\}\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result);
+ if (count($result)) {
+ $result = array_map('trim', array_slice($result, 1));
+ $result[0] = str_replace('|', ', ', $result[0]);
+ $result[1] = preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1]);
+ }
+ return $result;
+ }
+
+ /**
+ * Extracts the entry `type` data.
+ *
+ * @memberOf Entry
+ * @returns {String} The entry `type` data.
+ */
+ public function getType() {
+ preg_match('#\*\s*@type\s+([^\n]+)#', $this->entry, $result);
+ if (count($result)) {
+ $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1]));
+ } else {
+ $result = $this->isFunction() ? 'Function' : 'Unknown';
+ }
+ return $result;
+ }
+
+ /**
+ * Checks if an entry is a constructor.
+ *
+ * @memberOf Entry
+ * @returns {Boolean} Returns true if a constructor, else false.
+ */
+ public function isCtor() {
+ return !!preg_match('/\*\s*@constructor\b/', $this->entry);
+ }
+
+ /**
+ * Checks if an entry *is* assigned to a prototype.
+ *
+ * @memberOf Entry
+ * @returns {Boolean} Returns true if assigned to a prototype, else false.
+ */
+ public function isPlugin() {
+ return !$this->isCtor() && !$this->isPrivate() && !$this->isStatic();
+ }
+
+ /**
+ * Checks if an entry is private.
+ *
+ * @memberOf Entry
+ * @returns {Boolean} Returns true if private, else false.
+ */
+ public function isPrivate() {
+ return !!preg_match('/\*\s*@private\b/', $this->entry) || strrpos($this->entry, '@') === false;
+ }
+
+ /**
+ * Checks if an entry is *not* assigned to a prototype.
+ *
+ * @memberOf Entry
+ * @returns {Boolean} Returns true if not assigned to a prototype, else false.
+ */
+ public function isStatic() {
+ $public = !$this->isPrivate();
+ $result = $public && !!preg_match('/\*\s*@static\b/', $this->entry);
+
+ // set in cases where it isn't explicitly stated
+ if ($public && !$result) {
+ if ($parent = array_pop(preg_split('/[#.]/', $this->getMembers(0)))) {
+ foreach (Entry::getEntries($this->source) as $entry) {
+ $entry = new Entry($entry, $this->source);
+ if ($entry->getName() == $parent) {
+ $result = !$entry->isCtor();
+ break;
+ }
+ }
+ } else {
+ $result = true;
+ }
+ }
+ return $result;
+ }
+}
+?>
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Generator.php b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Generator.php
new file mode 100644
index 0000000..6c1873d
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/docdown/src/DocDown/Generator.php
@@ -0,0 +1,416 @@
+<?php
+
+require(dirname(__FILE__) . "/Entry.php");
+
+/**
+ * Generates Markdown from JSDoc entries.
+ */
+class Generator {
+
+ /**
+ * An array of JSDoc entries.
+ *
+ * @memberOf Generator
+ * @type Array
+ */
+ public $entries = array();
+
+ /**
+ * An options array used to configure the generator.
+ *
+ * @memberOf Generator
+ * @type Array
+ */
+ public $options = array();
+
+ /**
+ * The entire file's source code.
+ *
+ * @memberOf Generator
+ * @type String
+ */
+ public $source = '';
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The Generator constructor.
+ *
+ * @constructor
+ * @param {String} $source The source code to parse.
+ * @param {Array} $options The options array.
+ */
+ public function __construct( $source, $options = array() ) {
+ // juggle arguments
+ if (is_array($source)) {
+ $options = $source;
+ } else {
+ $options['source'] = $source;
+ }
+ if (isset($options['source']) && realpath($options['source'])) {
+ $options['path'] = $options['source'];
+ }
+ if (isset($options['path'])) {
+ preg_match('/(?<=\.)[a-z]+$/', $options['path'], $ext);
+ $options['source'] = file_get_contents($options['path']);
+ $ext = array_pop($ext);
+
+ if (!isset($options['lang']) && $ext) {
+ $options['lang'] = $ext;
+ }
+ if (!isset($options['title'])) {
+ $options['title'] = ucfirst(basename($options['path'])) . ' API documentation';
+ }
+ }
+ if (!isset($options['lang'])) {
+ $options['lang'] = 'js';
+ }
+
+ $this->options = $options;
+ $this->source = str_replace(PHP_EOL, "\n", $options['source']);
+ $this->entries = Entry::getEntries($this->source);
+
+ foreach ($this->entries as $index => $value) {
+ $this->entries[$index] = new Entry($value, $this->source, $options['lang']);
+ }
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Performs common string formatting operations.
+ *
+ * @private
+ * @static
+ * @memberOf Generator
+ * @param {String} $string The string to format.
+ * @returns {String} The formatted string.
+ */
+ private static function format($string) {
+ $counter = 0;
+
+ // tokenize inline code snippets
+ preg_match_all('/`[^`]+`/', $string, $tokenized);
+ $tokenized = $tokenized[0];
+ foreach ($tokenized as $snippet) {
+ $string = str_replace($snippet, '__token' . ($counter++) .'__', $string);
+ }
+
+ // italicize parentheses
+ $string = preg_replace('/(^|\s)(\([^)]+\))/', '$1*$2*', $string);
+
+ // mark numbers as inline code
+ $string = preg_replace('/ (-?\d+(?:.\d+)?)(?!\.[^\n])/', ' `$1`', $string);
+
+ // detokenize inline code snippets
+ $counter = 0;
+ foreach ($tokenized as $snippet) {
+ $string = str_replace('__token' . ($counter++) . '__', $snippet, $string);
+ }
+
+ return trim($string);
+ }
+
+ /**
+ * Modify a string by replacing named tokens with matching assoc. array values.
+ *
+ * @private
+ * @static
+ * @memberOf Generator
+ * @param {String} $string The string to modify.
+ * @param {Array|Object} $object The template object.
+ * @returns {String} The modified string.
+ */
+ private static function interpolate($string, $object) {
+ preg_match_all('/#\{([^}]+)\}/', $string, $tokens);
+ $tokens = array_unique(array_pop($tokens));
+
+ foreach ($tokens as $token) {
+ $pattern = '/#\{' . $token . '\}/';
+ $replacement = '';
+
+ if (is_object($object)) {
+ preg_match('/\(([^)]+?)\)$/', $token, $args);
+ $args = preg_split('/,\s*/', array_pop($args));
+ $method = 'get' . ucfirst(str_replace('/\([^)]+?\)$/', '', $token));
+
+ if (method_exists($object, $method)) {
+ $replacement = (string) call_user_func_array(array($object, $method), $args);
+ } else if (isset($object->{$token})) {
+ $replacement = (string) $object->{$token};
+ }
+ } else if (isset($object[$token])) {
+ $replacement = (string) $object[$token];
+ }
+ $string = preg_replace($pattern, trim($replacement), $string);
+ }
+ return Generator::format($string);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Resolves the entry's hash used to navigate the documentation.
+ *
+ * @private
+ * @memberOf Generator
+ * @param {Number|Object} $entry The entry object.
+ * @param {String} $member The name of the member.
+ * @returns {String} The url hash.
+ */
+ private function getHash( $entry, $member = '' ) {
+ $entry = is_numeric($entry) ? $this->entries[$entry] : $entry;
+ $member = !$member ? $entry->getMembers(0) : $member;
+ $result = ($member ? $member . ($entry->isPlugin() ? 'prototype' : '') : '') . $entry->getCall();
+ $result = preg_replace('/\(\[|\[\]/', '', $result);
+ $result = preg_replace('/[ =\'"{}.()\]]/', '', $result);
+ $result = preg_replace('/[[#,]/', '-', $result);
+ return strtolower($result);
+ }
+
+ /**
+ * Resolves the entry's url for the specific line number.
+ *
+ * @private
+ * @memberOf Generator
+ * @param {Number|Object} $entry The entry object.
+ * @returns {String} The url.
+ */
+ private function getLineUrl( $entry ) {
+ $entry = is_numeric($entry) ? $this->entries($entry) : $entry;
+ return $this->options['url'] . '#L' . $entry->getLineNumber();
+ }
+
+ /**
+ * Extracts the character used to separate the entry's name from its member.
+ *
+ * @private
+ * @memberOf Generator
+ * @param {Number|Object} $entry The entry object.
+ * @returns {String} The separator.
+ */
+ private function getSeparator( $entry ) {
+ $entry = is_numeric($entry) ? $this->entries($entry) : $entry;
+ return $entry->isPlugin() ? '.prototype.' : '.';
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Generates Markdown from JSDoc entries.
+ *
+ * @memberOf Generator
+ * @returns {String} The rendered Markdown.
+ */
+ public function generate() {
+ $api = array();
+ $compiling = false;
+ $openTag = "\n<!-- div -->\n";
+ $closeTag = "\n<!-- /div -->\n";
+ $result = array('# ' . $this->options['title']);
+ $toc = 'toc';
+
+ // initialize $api array
+ foreach ($this->entries as $entry) {
+
+ if (!$entry->isPrivate()) {
+ $name = $entry->getName();
+ $members = $entry->getMembers();
+ $members = count($members) ? $members : array('');
+
+ foreach ($members as $member) {
+ // create api category arrays
+ if (!isset($api[$member]) && $member) {
+ $api[$member] = new Entry('', '', $entry->lang);
+ $api[$member]->static = array();
+ $api[$member]->plugin = array();
+ }
+ // append entry to api category
+ if (!$member || $entry->isCtor() || ($entry->getType() == 'Object' &&
+ !preg_match('/[=:]\s*null\s*[,;]?$/', $entry->entry))) {
+ $member = ($member ? $member . ($entry->isPlugin() ? '#' : '.') : '') . $name;
+ $entry->static = @$api[$member] ? $api[$member]->static : array();
+ $entry->plugin = @$api[$member] ? $api[$member]->plugin : array();
+ $api[$member] = $entry;
+ }
+ else if ($entry->isStatic()) {
+ $api[$member]->static[] = $entry;
+ } else if (!$entry->isCtor()) {
+ $api[$member]->plugin[] = $entry;
+ }
+ }
+ }
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // custom sort for root level entries
+ // TODO: see how well it handles deeper namespace traversal
+ function sortCompare($a, $b) {
+ $score = array( 'a' => 0, 'b' => 0);
+ foreach (array( 'a' => $a, 'b' => $b) as $key => $value) {
+ // capitalized keys that represent constructor properties are last
+ if (preg_match('/[#.][A-Z]/', $value)) {
+ $score[$key] = 0;
+ }
+ // lowercase keys with prototype properties are next to last
+ else if (preg_match('/#[a-z]/', $value)) {
+ $score[$key] = 1;
+ }
+ // lowercase keys with static properties next to first
+ else if (preg_match('/\.[a-z]/', $value)) {
+ $score[$key] = 2;
+ }
+ // lowercase keys with no properties are first
+ else if (preg_match('/^[^#.]+$/', $value)) {
+ $score[$key] = 3;
+ }
+ }
+ $score = $score['b'] - $score['a'];
+ return $score ? $score : strcasecmp($a, $b);
+ }
+
+ uksort($api, 'sortCompare');
+
+ // sort static and plugin sub-entries
+ foreach ($api as $entry) {
+ foreach (array('static', 'plugin') as $kind) {
+ $sortBy = array( 'a' => array(), 'b' => array(), 'c' => array() );
+ foreach ($entry->{$kind} as $subentry) {
+ $name = $subentry->getName();
+ // functions w/o ALL-CAPs names are last
+ $sortBy['a'][] = $subentry->getType() == 'Function' && !preg_match('/^[A-Z_]+$/', $name);
+ // ALL-CAPs properties first
+ $sortBy['b'][] = preg_match('/^[A-Z_]+$/', $name);
+ // lowercase alphanumeric sort
+ $sortBy['c'][] = strtolower($name);
+ }
+ array_multisort($sortBy['a'], SORT_ASC, $sortBy['b'], SORT_DESC, $sortBy['c'], SORT_ASC, $entry->{$kind});
+ }
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // compile TOC
+ $result[] = $openTag;
+
+ foreach ($api as $key => $entry) {
+ $entry->hash = $this->getHash($entry);
+ $entry->href = $this->getLineUrl($entry);
+
+ $member = $entry->getMembers(0);
+ $member = ($member ? $member . ($entry->isPlugin() ? '.prototype.' : '.') : '') . $entry->getName();
+
+ $entry->member = preg_replace('/' . $entry->getName() . '$/', '', $member);
+
+ $compiling = $compiling ? ($result[] = $closeTag) : true;
+
+ // assign TOC hash
+ if (count($result) == 2) {
+ $toc = $member;
+ }
+
+ // add root entry
+ array_push(
+ $result,
+ $openTag, '## ' . (count($result) == 2 ? '<a id="' . $toc . '"></a>' : '') . '`' . $member . '`',
+ Generator::interpolate('* [`' . $member . '`](##{hash})', $entry)
+ );
+
+ // add static and plugin sub-entries
+ foreach (array('static', 'plugin') as $kind) {
+ if ($kind == 'plugin' && count($entry->plugin)) {
+ array_push(
+ $result,
+ $closeTag,
+ $openTag,
+ '## `' . $member . ($entry->isCtor() ? '.prototype`' : '`')
+ );
+ }
+ foreach ($entry->{$kind} as $subentry) {
+ $subentry->hash = $this->getHash($subentry);
+ $subentry->href = $this->getLineUrl($subentry);
+ $subentry->member = $member;
+ $subentry->separator = $this->getSeparator($subentry);
+ $result[] = Generator::interpolate('* [`#{member}#{separator}#{name}`](##{hash})', $subentry);
+ }
+ }
+ }
+
+ array_push($result, $closeTag, $closeTag);
+
+ /*------------------------------------------------------------------------*/
+
+ // compile content
+ $compiling = false;
+ $result[] = $openTag;
+
+ foreach ($api as $entry) {
+ // add root entry
+ $member = $entry->member . $entry->getName();
+ $compiling = $compiling ? ($result[] = $closeTag) : true;
+
+ array_push($result, $openTag, '## `' . $member . '`');
+
+ foreach (array($entry, 'static', 'plugin') as $kind) {
+ $subentries = is_string($kind) ? $entry->{$kind} : array($kind);
+
+ // title
+ if ($kind != 'static' && $entry->getType() != 'Object' &&
+ count($subentries) && $subentries[0] != $kind) {
+ if ($kind == 'plugin') {
+ $result[] = $closeTag;
+ }
+ array_push(
+ $result,
+ $openTag,
+ '## `' . $member . ($kind == 'plugin' ? '.prototype`' : '`')
+ );
+ }
+
+ // body
+ foreach ($subentries as $subentry) {
+ // description
+ array_push(
+ $result,
+ $openTag,
+ Generator::interpolate("### <a id=\"#{hash}\"></a>`#{member}#{separator}#{call}`\n<a href=\"##{hash}\">#</a> [Ⓢ](#{href} \"View in source\") [Ⓣ][1]\n\n#{desc}", $subentry)
+ );
+
+ // @param
+ if (count($params = $subentry->getParams())) {
+ array_push($result, '', '#### Arguments');
+ foreach ($params as $index => $param) {
+ $result[] = Generator::interpolate('#{num}. `#{name}` (#{type}): #{desc}', array(
+ 'desc' => $param[2],
+ 'name' => $param[1],
+ 'num' => $index + 1,
+ 'type' => $param[0]
+ ));
+ }
+ }
+ // @returns
+ if (count($returns = $subentry->getReturns())) {
+ array_push(
+ $result, '',
+ '#### Returns',
+ Generator::interpolate('(#{type}): #{desc}', array('desc' => $returns[1], 'type' => $returns[0]))
+ );
+ }
+ // @example
+ if ($example = $subentry->getExample()) {
+ array_push($result, '', '#### Example', $example);
+ }
+ array_push($result, "\n* * *", $closeTag);
+ }
+ }
+ }
+
+ // close tags add TOC link reference
+ array_push($result, $closeTag, $closeTag, '', ' [1]: #' . $toc . ' "Jump back to the TOC."');
+
+ // cleanup whitespace
+ return trim(preg_replace('/ +\n/', "\n", join($result, "\n")));
+ }
+}
+?>
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/LICENSE.txt b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/LICENSE.txt
new file mode 100644
index 0000000..dadad22
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/README.md
new file mode 100644
index 0000000..b913311
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/README.md
@@ -0,0 +1,98 @@
+# Platform.js <sup>v1.0.0</sup>
+
+A platform detection library that works on nearly all JavaScript platforms<sup><a name="fnref1" href="#fn1">1</a></sup>.
+
+## Disclaimer
+
+Platform.js is for informational purposes only and **not** intended as a substitution for [feature detection/inference](http://allyoucanleet.com/post/18087210413/feature-testing-costs#screencast2) checks.
+
+## BestieJS
+
+Platform.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+
+## Documentation
+
+The documentation for Platform.js can be viewed here: [/doc/README.md](https://github.com/bestiejs/platform.js/blob/master/doc/README.md#readme)
+
+For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/platform.js/wiki/Roadmap).
+
+## Support
+
+Platform.js has been tested in at least Adobe AIR 3.1, Chrome 5-21, Firefox 1.5-13, IE 6-9, Opera 9.25-12.01, Safari 3-6, Node.js 0.8.6, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
+
+## Installation and usage
+
+In a browser or Adobe AIR:
+
+```html
+<script src="platform.js"></script>
+```
+
+Via [npm](http://npmjs.org/):
+
+```bash
+npm install platform
+```
+
+In [Node.js](http://nodejs.org/) and [RingoJS](http://ringojs.org/):
+
+```js
+var platform = require('platform');
+```
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+```js
+load('platform.js');
+```
+
+In an AMD loader like [RequireJS](http://requirejs.org/):
+
+```js
+require({
+ 'paths': {
+ 'platform': 'path/to/platform'
+ }
+},
+['platform'], function(platform) {
+ console.log(platform.name);
+});
+```
+
+Usage example:
+
+```js
+// on IE10 x86 platform preview running in IE7 compatibility mode on Windows 7 64 bit edition
+platform.name; // 'IE'
+platform.version; // '10.0'
+platform.layout; // 'Trident'
+platform.os; // 'Windows Server 2008 R2 / 7 x64'
+platform.description; // 'IE 10.0 x86 (platform preview; running in IE 7 mode) on Windows Server 2008 R2 / 7 x64'
+
+// or on an iPad
+platform.name; // 'Safari'
+platform.version; // '5.1'
+platform.product; // 'iPad'
+platform.manufacturer; // 'Apple'
+platform.layout; // 'WebKit'
+platform.os; // 'iOS 5.0'
+platform.description; // 'Safari 5.1 on Apple iPad (iOS 5.0)'
+
+// or parsing a given UA string
+var info = platform.parse('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7.2; en; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 11.52');
+info.name; // 'Opera'
+info.version; // '11.52'
+info.layout; // 'Presto'
+info.os; // 'Mac OS X 10.7.2'
+info.description; // 'Opera 11.52 (identifying as Firefox 4.0) on Mac OS X 10.7.2'
+```
+
+## Author
+
+* [John-David Dalton](http://allyoucanleet.com/)
+ [](https://twitter.com/jdalton "Follow @jdalton on Twitter")
+
+## Contributors
+
+* [Mathias Bynens](http://mathiasbynens.be/)
+ [](https://twitter.com/mathias "Follow @mathias on Twitter")
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/platform.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/platform.js
new file mode 100644
index 0000000..a8a5482
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/platform.js/platform.js
@@ -0,0 +1,996 @@
+/*!
+ * Platform.js v1.0.0 <http://mths.be/platform>
+ * Copyright 2010-2012 John-David Dalton <http://allyoucanleet.com/>
+ * Available under MIT license <http://mths.be/mit>
+ */
+;(function(window) {
+ 'use strict';
+
+ /** Backup possible window/global object */
+ var oldWin = window;
+
+ /** Detect free variable `exports` */
+ var freeExports = typeof exports == 'object' && exports;
+
+ /** Detect free variable `global` */
+ var freeGlobal = typeof global == 'object' && global &&
+ (global == global.global ? (window = global) : global);
+
+ /** Opera regexp */
+ var reOpera = /Opera/;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Detect Java environment */
+ var java = /Java/.test(getClassOf(window.java)) && window.java;
+
+ /** A character to represent alpha */
+ var alpha = java ? 'a' : '\u03b1';
+
+ /** A character to represent beta */
+ var beta = java ? 'b' : '\u03b2';
+
+ /** Browser document object */
+ var doc = window.document || {};
+
+ /** Used to check for own properties of an object */
+ var hasOwnProperty = {}.hasOwnProperty;
+
+ /** Browser navigator object */
+ var nav = window.navigator || {};
+
+ /**
+ * Detect Opera browser
+ * http://www.howtocreate.co.uk/operaStuff/operaObject.html
+ * http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini
+ */
+ var opera = window.operamini || window.opera;
+
+ /** Opera [[Class]] */
+ var operaClass = reOpera.test(operaClass = getClassOf(opera)) ? operaClass : (opera = null);
+
+ /** Possible global object */
+ var thisBinding = this;
+
+ /** Browser user agent string */
+ var userAgent = nav.userAgent || '';
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Capitalizes a string value.
+ *
+ * @private
+ * @param {String} string The string to capitalize.
+ * @returns {String} The capitalized string.
+ */
+ function capitalize(string) {
+ string = String(string);
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ }
+
+ /**
+ * An iteration utility for arrays and objects.
+ *
+ * @private
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} callback The function called per iteration.
+ */
+ function each(object, callback) {
+ var index = -1,
+ length = object.length;
+
+ if (length == length >>> 0) {
+ while (++index < length) {
+ callback(object[index], index, object);
+ }
+ } else {
+ forOwn(object, callback);
+ }
+ }
+
+ /**
+ * Trim and conditionally capitalize string values.
+ *
+ * @private
+ * @param {String} string The string to format.
+ * @returns {String} The formatted string.
+ */
+ function format(string) {
+ string = trim(string);
+ return /^(?:webOS|i(?:OS|P))/.test(string)
+ ? string
+ : capitalize(string);
+ }
+
+ /**
+ * Iterates over an object's own properties, executing the `callback` for each.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function executed per own property.
+ */
+ function forOwn(object, callback) {
+ for (var key in object) {
+ hasKey(object, key) && callback(object[key], key, object);
+ }
+ }
+
+ /**
+ * Gets the internal [[Class]] of a value.
+ *
+ * @private
+ * @param {Mixed} value The value.
+ * @returns {String} The [[Class]].
+ */
+ function getClassOf(value) {
+ return value == null
+ ? capitalize(value)
+ : toString.call(value).slice(8, -1);
+ }
+
+ /**
+ * Checks if an object has the specified key as a direct property.
+ *
+ * @private
+ * @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() {
+ // lazy define for others (not as accurate)
+ hasKey = function(object, key) {
+ var parent = object != null && (object.constructor || Object).prototype;
+ return !!parent && key in Object(object) && !(key in parent && object[key] === parent[key]);
+ };
+ // for modern browsers
+ if (getClassOf(hasOwnProperty) == 'Function') {
+ hasKey = function(object, key) {
+ return object != null && hasOwnProperty.call(object, key);
+ };
+ }
+ // for Safari 2
+ else if ({}.__proto__ == Object.prototype) {
+ hasKey = function(object, key) {
+ var result = false;
+ if (object != null) {
+ object = Object(object);
+ object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
+ }
+ return result;
+ };
+ }
+ return hasKey.apply(this, arguments);
+ }
+
+ /**
+ * 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.
+ *
+ * @private
+ * @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) {
+ var type = object != null ? typeof object[property] : 'number';
+ return !/^(?:boolean|number|string|undefined)$/.test(type) &&
+ (type == 'object' ? !!object[property] : true);
+ }
+
+ /**
+ * Prepares a string for use in a RegExp constructor by making hyphens and
+ * spaces optional.
+ *
+ * @private
+ * @param {String} string The string to qualify.
+ * @returns {String} The qualified string.
+ */
+ function qualify(string) {
+ return String(string).replace(/([ -])(?!$)/g, '$1?');
+ }
+
+ /**
+ * A bare-bones` Array#reduce` like utility function.
+ *
+ * @private
+ * @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) {
+ var accumulator = null;
+ each(array, function(value, index) {
+ accumulator = callback(accumulator, value, index, array);
+ });
+ return accumulator;
+ }
+
+ /**
+ * Removes leading and trailing whitespace from a string.
+ *
+ * @private
+ * @param {String} string The string to trim.
+ * @returns {String} The trimmed string.
+ */
+ function trim(string) {
+ return String(string).replace(/^ +| +$/g, '');
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a new platform object.
+ *
+ * @memberOf platform
+ * @param {String} [ua = navigator.userAgent] The user agent string.
+ * @returns {Object} A platform object.
+ */
+ function parse(ua) {
+
+ ua || (ua = userAgent);
+
+ /** Temporary variable used over the script's lifetime */
+ var data;
+
+ /** The CPU architecture */
+ var arch = ua;
+
+ /** Platform description array */
+ var description = [];
+
+ /** Platform alpha/beta indicator */
+ var prerelease = null;
+
+ /** A flag to indicate that environment features should be used to resolve the platform */
+ var useFeatures = ua == userAgent;
+
+ /** The browser/environment version */
+ var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();
+
+ /* Detectable layout engines (order is important) */
+ var layout = getLayout([
+ { 'label': 'WebKit', 'pattern': 'AppleWebKit' },
+ 'iCab',
+ 'Presto',
+ 'NetFront',
+ 'Tasman',
+ 'Trident',
+ 'KHTML',
+ 'Gecko'
+ ]);
+
+ /* Detectable browser names (order is important) */
+ var name = getName([
+ 'Adobe AIR',
+ 'Arora',
+ 'Avant Browser',
+ 'Camino',
+ 'Epiphany',
+ 'Fennec',
+ 'Flock',
+ 'Galeon',
+ 'GreenBrowser',
+ 'iCab',
+ 'Iceweasel',
+ 'Iron',
+ 'K-Meleon',
+ 'Konqueror',
+ 'Lunascape',
+ 'Maxthon',
+ 'Midori',
+ 'Nook Browser',
+ 'PhantomJS',
+ 'Raven',
+ 'Rekonq',
+ 'RockMelt',
+ 'SeaMonkey',
+ { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+ 'Sleipnir',
+ 'SlimBrowser',
+ 'Sunrise',
+ 'Swiftfox',
+ 'WebPositive',
+ 'Opera Mini',
+ 'Opera',
+ 'Chrome',
+ { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' },
+ { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },
+ { 'label': 'IE', 'pattern': 'MSIE' },
+ 'Safari'
+ ]);
+
+ /* Detectable products (order is important) */
+ var product = getProduct([
+ 'BlackBerry',
+ { 'label': 'Galaxy S', 'pattern': 'GT-I9000' },
+ { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },
+ 'Google TV',
+ 'iPad',
+ 'iPod',
+ 'iPhone',
+ 'Kindle',
+ { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+ 'Nook',
+ 'PlayBook',
+ 'PlayStation Vita',
+ 'TouchPad',
+ 'Transformer',
+ 'Xoom'
+ ]);
+
+ /* Detectable manufacturers */
+ var manufacturer = getManufacturer({
+ 'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },
+ 'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },
+ 'Asus': { 'Transformer': 1 },
+ 'Barnes & Noble': { 'Nook': 1 },
+ 'BlackBerry': { 'PlayBook': 1 },
+ 'Google': { 'Google TV': 1 },
+ 'HP': { 'TouchPad': 1 },
+ 'LG': { },
+ 'Motorola': { 'Xoom': 1 },
+ 'Nokia': { },
+ 'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1 },
+ 'Sony': { 'PlayStation Vita': 1 }
+ });
+
+ /* Detectable OSes (order is important) */
+ var os = getOS([
+ 'Android',
+ 'CentOS',
+ 'Debian',
+ 'Fedora',
+ 'FreeBSD',
+ 'Gentoo',
+ 'Haiku',
+ 'Kubuntu',
+ 'Linux Mint',
+ 'Red Hat',
+ 'SuSE',
+ 'Ubuntu',
+ 'Xubuntu',
+ 'Cygwin',
+ 'Symbian OS',
+ 'hpwOS',
+ 'webOS ',
+ 'webOS',
+ 'Tablet OS',
+ 'Linux',
+ 'Mac OS X',
+ 'Macintosh',
+ 'Mac',
+ 'Windows 98;',
+ 'Windows '
+ ]);
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Picks the layout engine from an array of guesses.
+ *
+ * @private
+ * @param {Array} guesses An array of guesses.
+ * @returns {String|Null} The detected layout engine.
+ */
+ function getLayout(guesses) {
+ return reduce(guesses, function(result, guess) {
+ return result || RegExp('\\b' + (
+ guess.pattern || qualify(guess)
+ ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+ });
+ }
+
+ /**
+ * Picks the manufacturer from an array of guesses.
+ *
+ * @private
+ * @param {Array} guesses An array of guesses.
+ * @returns {String|Null} The detected manufacturer.
+ */
+ function getManufacturer(guesses) {
+ return reduce(guesses, function(result, value, key) {
+ // lookup the manufacturer by product or scan the UA for the manufacturer
+ return result || (
+ value[product] ||
+ value[0/*Opera 9.25 fix*/, /^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] ||
+ RegExp('\\b' + (key.pattern || qualify(key)) + '(?:\\b|\\w*\\d)', 'i').exec(ua)
+ ) && (key.label || key);
+ });
+ }
+
+ /**
+ * Picks the browser name from an array of guesses.
+ *
+ * @private
+ * @param {Array} guesses An array of guesses.
+ * @returns {String|Null} The detected browser name.
+ */
+ function getName(guesses) {
+ return reduce(guesses, function(result, guess) {
+ return result || RegExp('\\b' + (
+ guess.pattern || qualify(guess)
+ ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+ });
+ }
+
+ /**
+ * Picks the OS name from an array of guesses.
+ *
+ * @private
+ * @param {Array} guesses An array of guesses.
+ * @returns {String|Null} The detected OS name.
+ */
+ function getOS(guesses) {
+ return reduce(guesses, function(result, guess) {
+ var pattern = guess.pattern || qualify(guess);
+ if (!result && (result =
+ RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua))) {
+ // platform tokens defined at
+ // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+ // http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+ data = {
+ '6.2': '8',
+ '6.1': 'Server 2008 R2 / 7',
+ '6.0': 'Server 2008 / Vista',
+ '5.2': 'Server 2003 / XP 64-bit',
+ '5.1': 'XP',
+ '5.01': '2000 SP1',
+ '5.0': '2000',
+ '4.0': 'NT',
+ '4.90': 'ME'
+ };
+ // detect Windows version from platform tokens
+ if (/^Win/i.test(result) &&
+ (data = data[0/*Opera 9.25 fix*/, /[\d.]+$/.exec(result)])) {
+ result = 'Windows ' + data;
+ }
+ // correct character case and cleanup
+ result = format(String(result)
+ .replace(RegExp(pattern, 'i'), guess.label || guess)
+ .replace(/ ce$/i, ' CE')
+ .replace(/hpw/i, 'web')
+ .replace(/Macintosh/, 'Mac OS')
+ .replace(/_PowerPC/i, ' OS')
+ .replace(/(OS X) [^ \d]+/i, '$1')
+ .replace(/\/(\d)/, ' $1')
+ .replace(/_/g, '.')
+ .replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
+ .replace(/x86\.64/gi, 'x86_64')
+ .split(' on ')[0]);
+ }
+ return result;
+ });
+ }
+
+ /**
+ * Picks the product name from an array of guesses.
+ *
+ * @private
+ * @param {Array} guesses An array of guesses.
+ * @returns {String|Null} The detected product name.
+ */
+ function getProduct(guesses) {
+ return reduce(guesses, function(result, guess) {
+ var pattern = guess.pattern || qualify(guess);
+ if (!result && (result =
+ RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) ||
+ RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua)
+ )) {
+ // split by forward slash and append product version if needed
+ if ((result = String(guess.label || result).split('/'))[1] && !/[\d.]+/.test(result[0])) {
+ result[0] += ' ' + result[1];
+ }
+ // correct character case and cleanup
+ guess = guess.label || guess;
+ result = format(result[0]
+ .replace(RegExp(pattern, 'i'), guess)
+ .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')
+ .replace(RegExp('(' + guess + ')(\\w)', 'i'), '$1 $2'));
+ }
+ return result;
+ });
+ }
+
+ /**
+ * Resolves the version using an array of UA patterns.
+ *
+ * @private
+ * @param {Array} patterns An array of UA patterns.
+ * @returns {String|Null} The detected version.
+ */
+ function getVersion(patterns) {
+ return reduce(patterns, function(result, pattern) {
+ return result || (RegExp(pattern +
+ '(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;
+ });
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Returns `platform.description` when the platform object is coerced to a string.
+ *
+ * @name toString
+ * @memberOf platform
+ * @returns {String} Returns `platform.description` if available, else an empty string.
+ */
+ function toStringPlatform() {
+ return this.description || '';
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // convert layout to an array so we can add extra details
+ layout && (layout = [layout]);
+
+ // detect product names that contain their manufacturer's name
+ if (manufacturer && !product) {
+ product = getProduct([manufacturer]);
+ }
+ // clean up Google TV
+ if ((data = /Google TV/.exec(product))) {
+ product = data[0];
+ }
+ // detect simulators
+ if (/\bSimulator\b/i.test(ua)) {
+ product = (product ? product + ' ' : '') + 'Simulator';
+ }
+ // detect iOS
+ if (/^iP/.test(product)) {
+ name || (name = 'Safari');
+ os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua))
+ ? ' ' + data[1].replace(/_/g, '.')
+ : '');
+ }
+ // detect Kubuntu
+ else if (name == 'Konqueror' && !/buntu/i.test(os)) {
+ os = 'Kubuntu';
+ }
+ // detect Android browsers
+ else if (manufacturer && manufacturer != 'Google' &&
+ /Chrome|Vita/.test(name + ';' + product)) {
+ name = 'Android Browser';
+ os = /Android/.test(os) ? os : 'Android';
+ }
+ // detect false positives for Firefox/Safari
+ else if (!name || (data = !/\bMinefield\b/i.test(ua) && /Firefox|Safari/.exec(name))) {
+ // escape the `/` for Firefox 1
+ if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
+ // clear name of false positives
+ name = null;
+ }
+ // reassign a generic name
+ if ((data = product || manufacturer || os) &&
+ (product || manufacturer || /Android|Symbian OS|Tablet OS|webOS/.test(os))) {
+ name = /[a-z]+(?: Hat)?/i.exec(/Android/.test(os) ? os : data) + ' Browser';
+ }
+ }
+ // detect non-Opera versions (order is important)
+ if (!version) {
+ version = getVersion([
+ '(?:Cloud9|CriOS|CrMo|Opera ?Mini|Raven|Silk(?!/[\\d.]+$))',
+ 'Version',
+ qualify(name),
+ '(?:Firefox|Minefield|NetFront)'
+ ]);
+ }
+ // detect stubborn layout engines
+ if (layout == 'iCab' && parseFloat(version) > 3) {
+ layout = ['WebKit'];
+ } else if (data =
+ /Opera/.test(name) && 'Presto' ||
+ /\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
+ !layout && /\bMSIE\b/i.test(ua) && (/^Mac/.test(os) ? 'Tasman' : 'Trident')) {
+ layout = [data];
+ }
+ // leverage environment features
+ if (useFeatures) {
+ // detect server-side environments
+ // Rhino has a global function while others have a global object
+ if (isHostType(window, 'global')) {
+ if (java) {
+ data = java.lang.System;
+ arch = data.getProperty('os.arch');
+ os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
+ }
+ if (typeof exports == 'object' && exports) {
+ // if `thisBinding` is the [ModuleScope]
+ if (thisBinding == oldWin && typeof system == 'object' && (data = [system])[0]) {
+ os || (os = data[0].os || null);
+ try {
+ data[1] = require('ringo/engine').version;
+ version = data[1].join('.');
+ name = 'RingoJS';
+ } catch(e) {
+ if (data[0].global == freeGlobal) {
+ name = 'Narwhal';
+ }
+ }
+ } else if (typeof process == 'object' && (data = process)) {
+ name = 'Node.js';
+ arch = data.arch;
+ os = data.platform;
+ version = /[\d.]+/.exec(data.version)[0];
+ }
+ } else if (getClassOf(window.environment) == 'Environment') {
+ name = 'Rhino';
+ }
+ }
+ // detect Adobe AIR
+ else if (getClassOf(data = window.runtime) == 'ScriptBridgingProxyObject') {
+ name = 'Adobe AIR';
+ os = data.flash.system.Capabilities.os;
+ }
+ // detect PhantomJS
+ else if (getClassOf(data = window.phantom) == 'RuntimeObject') {
+ name = 'PhantomJS';
+ version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
+ }
+ // detect IE compatibility modes
+ else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) {
+ // we're in compatibility mode when the Trident version + 4 doesn't
+ // equal the document mode
+ version = [version, doc.documentMode];
+ if ((data = +data[1] + 4) != version[1]) {
+ description.push('IE ' + version[1] + ' mode');
+ layout[1] = '';
+ version[1] = data;
+ }
+ version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
+ }
+ os = os && format(os);
+ }
+ // detect prerelease phases
+ if (version && (data =
+ /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
+ /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
+ /\bMinefield\b/i.test(ua) && 'a')) {
+ prerelease = /b/i.test(data) ? 'beta' : 'alpha';
+ version = version.replace(RegExp(data + '\\+?$'), '') +
+ (prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
+ }
+ // rename code name "Fennec"
+ if (name == 'Fennec') {
+ name = 'Firefox Mobile';
+ }
+ // obscure Maxthon's unreliable version
+ else if (name == 'Maxthon' && version) {
+ version = version.replace(/\.[\d.]+/, '.x');
+ }
+ // detect Silk desktop/accelerated modes
+ else if (name == 'Silk') {
+ if (!/Mobi/i.test(ua)) {
+ os = 'Android';
+ description.unshift('desktop mode');
+ }
+ if (/Accelerated *= *true/i.test(ua)) {
+ description.unshift('accelerated');
+ }
+ }
+ // detect Windows Phone desktop mode
+ else if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) {
+ name += ' Mobile';
+ os = 'Windows Phone OS ' + data + '.x';
+ description.unshift('desktop mode');
+ }
+ // add mobile postfix
+ else if ((name == 'IE' || name && !product && !/Browser|Mobi/.test(name)) &&
+ (os == 'Windows CE' || /Mobi/i.test(ua))) {
+ name += ' Mobile';
+ }
+ // detect IE platform preview
+ else if (name == 'IE' && useFeatures && typeof external == 'object' && !external) {
+ description.unshift('platform preview');
+ }
+ // detect BlackBerry OS version
+ // http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
+ else if (/BlackBerry/.test(product) && (data =
+ (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
+ version)) {
+ os = 'Device Software ' + data;
+ version = null;
+ }
+ // detect Opera identifying/masking itself as another browser
+ // http://www.opera.com/support/kb/view/843/
+ else if (this != forOwn && (
+ (useFeatures && opera) ||
+ (/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
+ (name == 'Firefox' && /OS X (?:\d+\.){2,}/.test(os)) ||
+ (name == 'IE' && (
+ (os && !/^Win/.test(os) && version > 5.5) ||
+ /Windows XP/.test(os) && version > 8 ||
+ version == 8 && !/Trident/.test(ua)
+ ))
+ ) && !reOpera.test(data = parse.call(forOwn, ua.replace(reOpera, '') + ';')) && data.name) {
+
+ // when "indentifying", the UA contains both Opera and the other browser's name
+ data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
+ if (reOpera.test(name)) {
+ if (/IE/.test(data) && os == 'Mac OS') {
+ os = null;
+ }
+ data = 'identify' + data;
+ }
+ // when "masking", the UA contains only the other browser's name
+ else {
+ data = 'mask' + data;
+ if (operaClass) {
+ name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));
+ } else {
+ name = 'Opera';
+ }
+ if (/IE/.test(data)) {
+ os = null;
+ }
+ if (!useFeatures) {
+ version = null;
+ }
+ }
+ layout = ['Presto'];
+ description.push(data);
+ }
+ // detect WebKit Nightly and approximate Chrome/Safari versions
+ if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
+ // correct build for numeric comparison
+ // (e.g. "532.5" becomes "532.05")
+ data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data];
+ // nightly builds are postfixed with a `+`
+ if (name == 'Safari' && data[1].slice(-1) == '+') {
+ name = 'WebKit Nightly';
+ prerelease = 'alpha';
+ version = data[1].slice(0, -1);
+ }
+ // clear incorrect browser versions
+ else if (version == data[1] ||
+ version == (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1]) {
+ version = null;
+ }
+ // use the full Chrome version when available
+ data = [data[0], (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1]];
+
+ // detect JavaScriptCore
+ // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi
+ if (!useFeatures || (/internal|\n/i.test(toString.toString()) && !data[1])) {
+ layout[1] = 'like Safari';
+ data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : '5');
+ } else {
+ layout[1] = 'like Chrome';
+ data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : '21');
+ }
+ // add the postfix of ".x" or "+" for approximate versions
+ layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+');
+ // obscure version for some Safari 1-2 releases
+ if (name == 'Safari' && (!version || parseInt(version) > 45)) {
+ version = data;
+ }
+ }
+ // detect Opera desktop modes
+ if (name == 'Opera' && (data = /(?:zbov|zvav)$/.exec(os))) {
+ name += ' ';
+ description.unshift('desktop mode');
+ if (data == 'zvav') {
+ name += 'Mini';
+ version = null;
+ } else {
+ name += 'Mobile';
+ }
+ }
+ // detect Chrome desktop mode
+ else if (name == 'Safari' && /Chrome/.exec(layout[1])) {
+ description.unshift('desktop mode');
+ name = 'Chrome Mobile';
+ version = null;
+
+ if (/Mac OS X/.test(os)) {
+ manufacturer = 'Apple';
+ os = 'iOS 4.3+';
+ } else {
+ os = null;
+ }
+ }
+ // strip incorrect OS versions
+ if (version && version.indexOf(data = /[\d.]+$/.exec(os)) == 0 &&
+ ua.indexOf('/' + data + '-') > -1) {
+ os = trim(os.replace(data, ''));
+ }
+ // add layout engine
+ if (layout && !/Avant|Nook/.test(name) && (
+ /Browser|Lunascape|Maxthon/.test(name) ||
+ /^(?:Adobe|Arora|Midori|Phantom|Rekonq|Rock|Sleipnir|Web)/.test(name) && layout[1])) {
+ // don't add layout details to description if they are falsey
+ (data = layout[layout.length - 1]) && description.push(data);
+ }
+ // combine contextual information
+ if (description.length) {
+ description = ['(' + description.join('; ') + ')'];
+ }
+ // append manufacturer
+ if (manufacturer && product && product.indexOf(manufacturer) < 0) {
+ description.push('on ' + manufacturer);
+ }
+ // append product
+ if (product) {
+ description.push((/^on /.test(description[description.length -1]) ? '' : 'on ') + product);
+ }
+ // parse OS into an object
+ if (os) {
+ data = / ([\d.+]+)$/.exec(os);
+ os = {
+ 'architecture': 32,
+ 'family': data ? os.replace(data[0], '') : os,
+ 'version': data ? data[1] : null,
+ 'toString': function() {
+ var version = this.version;
+ return this.family + (version ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : '');
+ }
+ };
+ }
+ // add browser/OS architecture
+ if ((data = / (?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) {
+ if (os) {
+ os.architecture = 64;
+ os.family = os.family.replace(data, '');
+ }
+ if (name && (/WOW64/i.test(ua) ||
+ (useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform)))) {
+ description.unshift('32-bit');
+ }
+ }
+
+ ua || (ua = null);
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * The platform object.
+ *
+ * @name platform
+ * @type Object
+ */
+ return {
+
+ /**
+ * The browser/environment version.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'version': name && version && (description.unshift(version), version),
+
+ /**
+ * The name of the browser/environment.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'name': name && (description.unshift(name), name),
+
+ /**
+ * The name of the operating system.
+ *
+ * @memberOf platform
+ * @type Object
+ */
+ 'os': os
+ ? (name &&
+ !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product)) &&
+ description.push(product ? '(' + os + ')' : 'on ' + os), os)
+ : {
+
+ /**
+ * The CPU architecture the OS is built for.
+ *
+ * @memberOf platform.os
+ * @type String|Null
+ */
+ 'architecture': null,
+
+ /**
+ * The family of the OS.
+ *
+ * @memberOf platform.os
+ * @type String|Null
+ */
+ 'family': null,
+
+ /**
+ * The version of the OS.
+ *
+ * @memberOf platform.os
+ * @type String|Null
+ */
+ 'version': null,
+
+ /**
+ * Returns the OS string.
+ *
+ * @memberOf platform.os
+ * @returns {String} The OS string.
+ */
+ 'toString': function() { return 'null'; }
+ },
+
+ /**
+ * The platform description.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'description': description.length ? description.join(' ') : ua,
+
+ /**
+ * The name of the browser layout engine.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'layout': layout && layout[0],
+
+ /**
+ * The name of the product's manufacturer.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'manufacturer': manufacturer,
+
+ /**
+ * The alpha/beta release indicator.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'prerelease': prerelease,
+
+ /**
+ * The name of the product hosting the browser.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'product': product,
+
+ /**
+ * The browser's user agent string.
+ *
+ * @memberOf platform
+ * @type String|Null
+ */
+ 'ua': ua,
+
+ // parses a user agent string into a platform object
+ 'parse': parse,
+
+ // returns the platform description
+ 'toString': toStringPlatform
+ };
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose platform
+ // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // define as an anonymous module so, through path mapping, it can be aliased
+ define(function() {
+ return parse();
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports) {
+ // in Narwhal, Node.js, or RingoJS
+ forOwn(parse(), function(value, key) {
+ freeExports[key] = value;
+ });
+ }
+ // in a browser or Rhino
+ else {
+ // use square bracket notation so Closure Compiler won't munge `platform`
+ // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
+ window['platform'] = parse();
+ }
+}(this));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/LICENSE.txt b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/LICENSE.txt
new file mode 100644
index 0000000..dadad22
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/README.md
new file mode 100644
index 0000000..b84a21c
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/README.md
@@ -0,0 +1,57 @@
+# QUnit CLIB <sup>v1.0.0</sup>
+## command-line interface boilerplate
+
+QUnit CLIB helps extend QUnit's CLI support to many common CLI environments.
+
+## Screenshot
+
+
+
+## Support
+
+QUnit CLIB has been tested in at least Node.js 0.4.8-0.8.6, Narwhal v0.3.2, RingoJS v0.8.0, and Rhino v1.7RC3-RC5.
+
+## Usage
+
+```js
+(function(window) {
+
+ // use a single load function
+ var load = typeof require == 'function' ? require : window.load;
+
+ // load QUnit and CLIB if needed
+ var QUnit =
+ window.QUnit || (
+ window.setTimeout || (window.addEventListener = window.setTimeout = / /),
+ window.QUnit = load('path/to/qunit.js') || window.QUnit,
+ load('path/to/qunit-clib.js'),
+ (window.addEventListener || 0).test && delete window.addEventListener,
+ window.QUnit
+ );
+
+ // explicitly call `QUnit.module()` instead of `module()`
+ // in case we are in a CLI environment
+ QUnit.module('A Test Module');
+
+ test('A Test', function() {
+ // ...
+ });
+
+ // must call `QUnit.start()` if using QUnit < 1.3.0 with Node.js or any
+ // version of QUnit with Narwhal, Rhino, or RingoJS
+ if (!window.document) {
+ QUnit.start();
+ }
+}(typeof global == 'object' && global || this));
+```
+
+## Footnotes
+
+ 1. QUnit v1.3.0 does not work with Narwhal or Ringo < v0.8.0
+
+ 2. Rhino v1.7RC4 does not support timeout fallbacks `clearTimeout` and `setTimeout`
+
+## Author
+
+* [John-David Dalton](http://allyoucanleet.com/)
+ [](https://twitter.com/jdalton "Follow @jdalton on Twitter")
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/qunit-clib.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/qunit-clib.js
new file mode 100644
index 0000000..e487c03
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit-clib/qunit-clib.js
@@ -0,0 +1,319 @@
+/*!
+ * QUnit CLI Boilerplate v1.0.0
+ * Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
+ * Based on a gist by Jörn Zaefferer <https://gist.github.com/722381>
+ * Available under MIT license <http://mths.be/mit>
+ */
+;(function(global) {
+ 'use strict';
+
+ /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
+ global.console || (global.console = { 'log': global.print });
+
+ /** Reduce global.QUnit.QUnit -> global.QUnit */
+ global.QUnit && (QUnit = QUnit.QUnit || QUnit);
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Used as a horizontal rule in console output */
+ var hr = '----------------------------------------';
+
+ /** Shortcut used to convert array-like objects to arrays */
+ var slice = [].slice;
+
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = {}.toString;
+
+ /** Used by timer methods */
+ var doneCalled,
+ timer,
+ counter = 0,
+ ids = {};
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * An iteration utility for arrays.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ */
+ function each(array, callback) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ callback(array[index], index, array);
+ }
+ }
+
+ /**
+ * Checks if the specified `value` is a function.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if `value` is a function, else `false`.
+ */
+ function isFunction(value) {
+ return toString.call(value) == '[object Function]';
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
+ * https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
+ * http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
+ */
+
+ /**
+ * Clears the delay set by `setInterval` or `setTimeout`.
+ *
+ * @memberOf global
+ * @param {Number} id The ID of the timeout to be cleared.
+ */
+ function clearTimer(id) {
+ if (ids[id]) {
+ ids[id].cancel();
+ timer.purge();
+ delete ids[id];
+ }
+ }
+
+ /**
+ * Schedules timer-based callbacks.
+ *
+ * @private
+ * @param {Function} fn The function to call.
+ * @oaram {Number} delay The number of milliseconds to delay the `fn` call.
+ * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
+ * @param {Boolean} repeated A flag to specify whether `fn` is called repeatedly.
+ * @returns {Number} The the ID of the timeout.
+ */
+ function schedule(fn, delay, args, repeated) {
+ // Rhino 1.7RC4 will error assigning `task` below
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=775566
+ var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, {
+ 'run': function() {
+ fn.apply(global, args);
+ }
+ });
+ // support non-functions
+ if (!isFunction(fn)) {
+ fn = (function(code) {
+ code = String(code);
+ return function() { eval(code); };
+ }(fn));
+ }
+ // used by setInterval
+ if (repeated) {
+ timer.schedule(task, delay, delay);
+ }
+ // used by setTimeout
+ else {
+ timer.schedule(task, delay);
+ }
+ return counter;
+ }
+
+ /**
+ * Executes a code snippet or function repeatedly, with a delay between each call.
+ *
+ * @memberOf global
+ * @param {Function|String} fn The function to call or string to evaluate.
+ * @oaram {Number} delay The number of milliseconds to delay each `fn` call.
+ * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
+ * @returns {Number} The the ID of the timeout.
+ */
+ function setInterval(fn, delay) {
+ return schedule(fn, delay, slice.call(arguments, 2), true);
+ }
+
+ /**
+ * Executes a code snippet or a function after specified delay.
+ *
+ * @memberOf global
+ * @param {Function|String} fn The function to call or string to evaluate.
+ * @oaram {Number} delay The number of milliseconds to delay the `fn` call.
+ * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
+ * @returns {Number} The the ID of the timeout.
+ */
+ function setTimeout(fn, delay) {
+ return schedule(fn, delay, slice.call(arguments, 2));
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A logging callback triggered when all testing is completed.
+ *
+ * @memberOf QUnit
+ * @param {Object} details An object with properties `failed`, `passed`,
+ * `runtime`, and `total`.
+ */
+ function done(details) {
+ // stop `asyncTest()` from erroneously calling `done()` twice in
+ // environments w/o timeouts
+ if (doneCalled) {
+ return;
+ }
+ doneCalled = true;
+ console.log(hr);
+ console.log(' PASS: ' + details.passed + ' FAIL: ' + details.failed + ' TOTAL: ' + details.total);
+ console.log(' Finished in ' + details.runtime + ' milliseconds.');
+ console.log(hr);
+
+ // exit out of Rhino
+ try {
+ quit();
+ } catch(e) { }
+
+ // exit out of Node.js
+ try {
+ process.exit();
+ } catch(e) { }
+ }
+
+ /**
+ * A logging callback triggered after every assertion.
+ *
+ * @memberOf QUnit
+ * @param {Object} details An object with properties `actual`, `expected`,
+ * `message`, and `result`.
+ */
+ function log(details) {
+ var expected = details.expected,
+ result = details.result,
+ type = typeof expected != 'undefined' ? 'EQ' : 'OK';
+
+ var assertion = [
+ result ? 'PASS' : 'FAIL',
+ type,
+ details.message || 'ok'
+ ];
+
+ if (!result && type == 'EQ') {
+ assertion.push('Expected: ' + expected + ', Actual: ' + details.actual);
+ }
+ QUnit.config.testStats.assertions.push(assertion.join(' | '));
+ }
+
+ /**
+ * A logging callback triggered at the start of every test module.
+ *
+ * @memberOf QUnit
+ * @param {Object} details An object with property `name`.
+ */
+ function moduleStart(details) {
+ console.log(hr);
+ console.log(details.name);
+ console.log(hr);
+ }
+
+ /**
+ * Converts an object into a string representation.
+ *
+ * @memberOf QUnit
+ * @type Function
+ * @param {Object} object The object to stringify.
+ * @returns {String} The result string.
+ */
+ var parseObject = (function() {
+ var func = QUnit.jsDump.parsers.object;
+ return function(object) {
+ // fork to support Rhino's error objects
+ if (typeof object.rhinoException == 'object') {
+ return object.name +
+ ' { message: "' + object.message +
+ '", fileName: "' + object.fileName +
+ '", lineNumber: ' + object.lineNumber + ' }';
+ }
+ return func(object);
+ };
+ }());
+
+ /**
+ * A logging callback triggered after a test is completed.
+ *
+ * @memberOf QUnit
+ * @param {Object} details An object with properties `failed`, `name`,
+ * `passed`, and `total`.
+ */
+ function testDone(details) {
+ var assertions = QUnit.config.testStats.assertions,
+ testName = details.name;
+
+ if (details.failed > 0) {
+ console.log(' FAIL - '+ testName);
+ each(assertions, function(value) {
+ console.log(' ' + value);
+ });
+ }
+ else {
+ console.log(' PASS - ' + testName);
+ }
+ assertions.length = 0;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * An object used to hold information about the current running test.
+ *
+ * @memberOf QUnit.config
+ * @type Object
+ */
+ QUnit.config.testStats = {
+
+ /**
+ * An array of test summaries (pipe separated).
+ *
+ * @memberOf QUnit.config.testStats
+ * @type Array
+ */
+ 'assertions': []
+ };
+
+ // add shortcuts to the global
+ // exclude `module` because some environments have it as a built-in object
+ each(['asyncTest', 'deepEqual', 'equal', 'equals', 'expect', 'notDeepEqual',
+ 'notEqual', 'notStrictEqual', 'ok', 'raises', 'same', 'start', 'stop',
+ 'strictEqual', 'test', 'throws'], function(funcName) {
+ var func = QUnit[funcName];
+ if (func) {
+ global[funcName] = func;
+ }
+ });
+
+ // expose timer methods to global
+ try {
+ timer = new java.util.Timer;
+ if (!isFunction(global.clearInterval)) {
+ global.clearInterval = clearTimer;
+ }
+ if (!isFunction(global.clearTimeout)) {
+ global.clearTimeout = clearTimer;
+ }
+ if (!isFunction(global.setInterval)) {
+ global.setInterval = setInterval;
+ }
+ if (!isFunction(global.setTimeout)) {
+ global.setTimeout = setTimeout;
+ }
+ } catch(e) { }
+
+ // add callbacks
+ QUnit.done(done);
+ QUnit.log(log);
+ QUnit.moduleStart(moduleStart);
+ QUnit.testDone(testDone);
+
+ // add wrapped function
+ QUnit.jsDump.parsers.object = parseObject;
+
+ // must call `QUnit.start()` in the test file if using QUnit < 1.3.0 with
+ // Node.js or any version of QUnit with Narwhal, Rhino, or RingoJS
+ QUnit.init();
+
+}(typeof global == 'object' && global || this));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/README.md
new file mode 100644
index 0000000..3778a27
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/README.md
@@ -0,0 +1,49 @@
+[QUnit](http://docs.jquery.com/QUnit) - A JavaScript Unit Testing framework.
+================================
+
+QUnit is a powerful, easy-to-use, JavaScript test suite. It's used by the jQuery
+project to test its code and plugins but is capable of testing any generic
+JavaScript code (and even capable of testing JavaScript code on the server-side).
+
+QUnit is especially useful for regression testing: Whenever a bug is reported,
+write a test that asserts the existence of that particular bug. Then fix it and
+commit both. Every time you work on the code again, run the tests. If the bug
+comes up again - a regression - you'll spot it immediately and know how to fix
+it, because you know what code you just changed.
+
+Having good unit test coverage makes safe refactoring easy and cheap. You can
+run the tests after each small refactoring step and always know what change
+broke something.
+
+QUnit is similar to other unit testing frameworks like JUnit, but makes use of
+the features JavaScript provides and helps with testing code in the browser, e.g.
+with its stop/start facilities for testing asynchronous code.
+
+If you are interested in helping developing QUnit, you are in the right place.
+For related discussions, visit the
+[QUnit and Testing forum](http://forum.jquery.com/qunit-and-testing).
+
+Planning for a qunitjs.com site and other testing tools related work now happens
+on the [jQuery Testing Team planning wiki](http://jquerytesting.pbworks.com/w/page/41556026/FrontPage).
+
+Development
+-----------
+
+To submit patches, fork the repository, create a branch for the change. Then implement
+the change, run `grunt` to lint and test it, then commit, push and create a pull request.
+
+Include some background for the change in the commit message and `Fixes #nnn`, referring
+to the issue number you're addressing.
+
+To run `grunt`, you need `node` and `npm`, then `npm install grunt -g`.
+
+Releases
+--------
+
+Install git-extras and run `git changelog` to update History.md.
+Update qunit/qunit.js|css and package.json to the release version, commit and
+tag, update them again to the next version, commit and push commits and tags
+(`git push --tags origin master`).
+
+Put the 'v' in front of the tag, e.g. `v1.8.0`. Clean up the changelog, removing merge commits
+or whitespace cleanups.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit-1.8.0.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit-1.8.0.js
new file mode 100644
index 0000000..c1570c2
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit-1.8.0.js
@@ -0,0 +1,1863 @@
+/**
+ * QUnit v1.8.0 - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * or GPL (GPL-LICENSE.txt) licenses.
+ */
+
+(function( window ) {
+
+var QUnit,
+ config,
+ onErrorFnPrev,
+ testId = 0,
+ fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ defined = {
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch( e ) {
+ return false;
+ }
+ }())
+};
+
+function Test( settings ) {
+ extend( this, settings );
+ this.assertions = [];
+ this.testNumber = ++Test.count;
+}
+
+Test.count = 0;
+
+Test.prototype = {
+ init: function() {
+ var a, b, li,
+ tests = id( "qunit-tests" );
+
+ if ( tests ) {
+ b = document.createElement( "strong" );
+ b.innerHTML = this.name;
+
+ // `a` initialized at top of scope
+ a = document.createElement( "a" );
+ a.innerHTML = "Rerun";
+ a.href = QUnit.url({ testNumber: this.testNumber });
+
+ li = document.createElement( "li" );
+ li.appendChild( b );
+ li.appendChild( a );
+ li.className = "running";
+ li.id = this.id = "qunit-test-output" + testId++;
+
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if ( this.module !== config.previousModule ) {
+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ } else if ( config.autorun ) {
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment );
+
+ runLoggingCallbacks( "testStart", QUnit, {
+ name: this.testName,
+ module: this.module
+ });
+
+ // allow utility functions to access the current test environment
+ // TODO why??
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ if ( config.notrycatch ) {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ return;
+ }
+ try {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ },
+ run: function() {
+ config.current = this;
+
+ var running = id( "qunit-testresult" );
+
+ if ( running ) {
+ running.innerHTML = "Running: <br/>" + this.name;
+ }
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ if ( config.notrycatch ) {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ return;
+ }
+
+ try {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ } catch( e ) {
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+ teardown: function() {
+ config.current = this;
+ if ( config.notrycatch ) {
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ return;
+ } else {
+ try {
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ }
+ checkPollution();
+ },
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected == null ) {
+ QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
+ } else if ( this.expected != null && this.expected != this.assertions.length ) {
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
+ } else if ( this.expected == null && !this.assertions.length ) {
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var assertion, a, b, i, li, ol,
+ test = this,
+ good = 0,
+ bad = 0,
+ tests = id( "qunit-tests" );
+
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ ol = document.createElement( "ol" );
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ assertion = this.assertions[i];
+
+ li = document.createElement( "li" );
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ // store result when possible
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
+ }
+ }
+
+ if ( bad === 0 ) {
+ ol.style.display = "none";
+ }
+
+ // `b` initialized at top of scope
+ b = document.createElement( "strong" );
+ b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+ addEvent(b, "click", function() {
+ var next = b.nextSibling.nextSibling,
+ display = next.style.display;
+ next.style.display = display === "none" ? "block" : "none";
+ });
+
+ addEvent(b, "dblclick", function( e ) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+ target = target.parentNode;
+ }
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+ window.location = QUnit.url({ testNumber: test.testNumber });
+ }
+ });
+
+ // `li` initialized at top of scope
+ li = id( this.id );
+ li.className = bad ? "fail" : "pass";
+ li.removeChild( li.firstChild );
+ a = li.firstChild;
+ li.appendChild( b );
+ li.appendChild ( a );
+ li.appendChild( ol );
+
+ } else {
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ runLoggingCallbacks( "testDone", QUnit, {
+ name: this.testName,
+ module: this.module,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length
+ });
+
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ synchronize(function() {
+ test.init();
+ });
+ function run() {
+ // each of these can by async
+ synchronize(function() {
+ test.setup();
+ });
+ synchronize(function() {
+ test.run();
+ });
+ synchronize(function() {
+ test.teardown();
+ });
+ synchronize(function() {
+ test.finish();
+ });
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ }
+};
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+QUnit = {
+
+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ config.currentModule = name;
+ config.currentModuleTestEnviroment = testEnvironment;
+ },
+
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ QUnit.test( testName, expected, callback, true );
+ },
+
+ test: function( testName, expected, callback, async ) {
+ var test,
+ name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ if ( config.currentModule ) {
+ name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
+ }
+
+ test = new Test({
+ name: name,
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback,
+ module: config.currentModule,
+ moduleTestEnvironment: config.currentModuleTestEnviroment,
+ stack: sourceFromStacktrace( 2 )
+ });
+
+ if ( !validTest( test ) ) {
+ return;
+ }
+
+ test.queue();
+ },
+
+ // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ config.current.expected = asserts;
+ },
+
+ start: function( count ) {
+ config.semaphore -= count || 1;
+ // don't start until equal number of stop-calls
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ // ignore if start is called more often then stop
+ if ( config.semaphore < 0 ) {
+ config.semaphore = 0;
+ }
+ // A slight delay, to avoid any current callbacks
+ if ( defined.setTimeout ) {
+ window.setTimeout(function() {
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ config.blocking = false;
+ process( true );
+ }, 13);
+ } else {
+ config.blocking = false;
+ process( true );
+ }
+ },
+
+ stop: function( count ) {
+ config.semaphore += count || 1;
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = window.setTimeout(function() {
+ QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
+ QUnit.start();
+ }, config.testTimeout );
+ }
+ }
+};
+
+// Asssert helpers
+// All of these must call either QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+QUnit.assert = {
+ /**
+ * Asserts rough true-ish result.
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function( result, msg ) {
+ if ( !config.current ) {
+ throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+ result = !!result;
+
+ var source,
+ details = {
+ result: result,
+ message: msg
+ };
+
+ msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
+ msg = "<span class='test-message'>" + msg + "</span>";
+
+ if ( !result ) {
+ source = sourceFromStacktrace( 2 );
+ if ( source ) {
+ details.source = source;
+ msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+ }
+ }
+ runLoggingCallbacks( "log", QUnit, details );
+ config.current.assertions.push({
+ result: result,
+ message: msg
+ });
+ },
+
+ /**
+ * Assert that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
+ */
+ equal: function( actual, expected, message ) {
+ QUnit.push( expected == actual, actual, expected, message );
+ },
+
+ notEqual: function( actual, expected, message ) {
+ QUnit.push( expected != actual, actual, expected, message );
+ },
+
+ deepEqual: function( actual, expected, message ) {
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ notDeepEqual: function( actual, expected, message ) {
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ strictEqual: function( actual, expected, message ) {
+ QUnit.push( expected === actual, actual, expected, message );
+ },
+
+ notStrictEqual: function( actual, expected, message ) {
+ QUnit.push( expected !== actual, actual, expected, message );
+ },
+
+ raises: function( block, expected, message ) {
+ var actual,
+ ok = false;
+
+ if ( typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ config.current.ignoreGlobalErrors = true;
+ try {
+ block.call( config.current.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ config.current.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ // expected is a regexp
+ } else if ( QUnit.objectType( expected ) === "regexp" ) {
+ ok = expected.test( actual );
+ // expected is a constructor
+ } else if ( actual instanceof expected ) {
+ ok = true;
+ // expected is a validation function which returns true is validation passed
+ } else if ( expected.call( {}, actual ) === true ) {
+ ok = true;
+ }
+ }
+
+ QUnit.push( ok, actual, null, message );
+ }
+};
+
+// @deprecated: Kept assertion helpers in root for backwards compatibility
+extend( QUnit, QUnit.assert );
+
+/**
+ * @deprecated: Kept for backwards compatibility
+ * next step: remove entirely
+ */
+QUnit.equals = function() {
+ QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.same = function() {
+ QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
+};
+
+// We want access to the constructor's prototype
+(function() {
+ function F() {}
+ F.prototype = QUnit;
+ QUnit = new F();
+ // Make F QUnit's constructor so that we can add to the prototype later
+ QUnit.constructor = F;
+}());
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true,
+
+ // when enabled, show only failing tests
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
+ hidepassed: false,
+
+ // by default, run previously failed tests first
+ // very useful in combination with "Hide passed tests" checked
+ reorder: true,
+
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ // when enabled, all tests must call expect()
+ requireExpects: false,
+
+ urlConfig: [ "noglobals", "notrycatch" ],
+
+ // logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
+};
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+ var i,
+ location = window.location || { search: "", protocol: "file:" },
+ params = location.search.slice( 1 ).split( "&" ),
+ length = params.length,
+ urlParams = {},
+ current;
+
+ if ( params[ 0 ] ) {
+ for ( i = 0; i < length; i++ ) {
+ current = params[ i ].split( "=" );
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
+ }
+
+ QUnit.urlParams = urlParams;
+
+ // String search anywhere in moduleName+testName
+ config.filter = urlParams.filter;
+
+ // Exact match of the module name
+ config.module = urlParams.module;
+
+ config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = location.protocol === "file:";
+}());
+
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
+if ( typeof exports === "undefined" ) {
+ extend( window, QUnit );
+
+ // Expose QUnit object
+ window.QUnit = QUnit;
+}
+
+// Extend QUnit object,
+// these after set here because they should not be exposed as global functions
+extend( QUnit, {
+ config: config,
+
+ // Initialize the configuration options
+ init: function() {
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date(),
+ updateRate: 1000,
+ blocking: false,
+ autostart: true,
+ autorun: false,
+ filter: "",
+ queue: [],
+ semaphore: 0
+ });
+
+ var tests, banner, result,
+ qunit = id( "qunit" );
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br/> ";
+ }
+ },
+
+ // Resets the test setup. Useful for tests that modify the DOM.
+ // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
+ reset: function() {
+ var fixture;
+
+ if ( window.jQuery ) {
+ jQuery( "#qunit-fixture" ).html( config.fixture );
+ } else {
+ fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+ }
+ },
+
+ // Trigger an event on an element.
+ // @example triggerEvent( document.body, "click" );
+ triggerEvent: function( elem, type, event ) {
+ if ( document.createEvent ) {
+ event = document.createEvent( "MouseEvents" );
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+
+ elem.dispatchEvent( event );
+ } else if ( elem.fireEvent ) {
+ elem.fireEvent( "on" + type );
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) == type;
+ },
+
+ objectType: function( obj ) {
+ if ( typeof obj === "undefined" ) {
+ return "undefined";
+ // consider: typeof null === object
+ }
+ if ( obj === null ) {
+ return "null";
+ }
+
+ var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
+
+ switch ( type ) {
+ case "Number":
+ if ( isNaN(obj) ) {
+ return "nan";
+ }
+ return "number";
+ case "String":
+ case "Boolean":
+ case "Array":
+ case "Date":
+ case "RegExp":
+ case "Function":
+ return type.toLowerCase();
+ }
+ if ( typeof obj === "object" ) {
+ return "object";
+ }
+ return undefined;
+ },
+
+ push: function( result, actual, expected, message ) {
+ if ( !config.current ) {
+ throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
+ }
+
+ var output, source,
+ details = {
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected
+ };
+
+ message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ if ( !result ) {
+ expected = escapeInnerText( QUnit.jsDump.parse(expected) );
+ actual = escapeInnerText( QUnit.jsDump.parse(actual) );
+ output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
+
+ if ( actual != expected ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
+ output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ }
+
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+ }
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: !!result,
+ message: output
+ });
+ },
+
+ pushFailure: function( message, source ) {
+ if ( !config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+
+ var output,
+ details = {
+ result: false,
+ message: message
+ };
+
+ message = escapeInnerText(message ) || "error";
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ if ( source ) {
+ details.source = source;
+ output += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+ }
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: false,
+ message: output
+ });
+ },
+
+ url: function( params ) {
+ params = extend( extend( {}, QUnit.urlParams ), params );
+ var key,
+ querystring = "?";
+
+ for ( key in params ) {
+ if ( !hasOwn.call( params, key ) ) {
+ continue;
+ }
+ querystring += encodeURIComponent( key ) + "=" +
+ encodeURIComponent( params[ key ] ) + "&";
+ }
+ return window.location.pathname + querystring.slice( 0, -1 );
+ },
+
+ extend: extend,
+ id: id,
+ addEvent: addEvent
+ // load, equiv, jsDump, diff: Attached later
+});
+
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
+extend( QUnit.constructor.prototype, {
+
+ // Logging callbacks; all receive a single argument with the listed properties
+ // run test/logs.html for any related changes
+ begin: registerLoggingCallback( "begin" ),
+
+ // done: { failed, passed, total, runtime }
+ done: registerLoggingCallback( "done" ),
+
+ // log: { result, actual, expected, message }
+ log: registerLoggingCallback( "log" ),
+
+ // testStart: { name }
+ testStart: registerLoggingCallback( "testStart" ),
+
+ // testDone: { name, failed, passed, total }
+ testDone: registerLoggingCallback( "testDone" ),
+
+ // moduleStart: { name }
+ moduleStart: registerLoggingCallback( "moduleStart" ),
+
+ // moduleDone: { name, failed, passed, total }
+ moduleDone: registerLoggingCallback( "moduleDone" )
+});
+
+if ( typeof document === "undefined" || document.readyState === "complete" ) {
+ config.autorun = true;
+}
+
+QUnit.load = function() {
+ runLoggingCallbacks( "begin", QUnit, {} );
+
+ // Initialize the config, saving the execution queue
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
+ urlConfigHtml = "",
+ oldconfig = extend( {}, config );
+
+ QUnit.init();
+ extend(config, oldconfig);
+
+ config.blocking = false;
+
+ len = config.urlConfig.length;
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[i];
+ config[val] = QUnit.urlParams[val];
+ urlConfigHtml += "<label><input name='" + val + "' type='checkbox'" + ( config[val] ? " checked='checked'" : "" ) + ">" + val + "</label>";
+ }
+
+ // `userAgent` initialized at top of scope
+ userAgent = id( "qunit-userAgent" );
+ if ( userAgent ) {
+ userAgent.innerHTML = navigator.userAgent;
+ }
+
+ // `banner` initialized at top of scope
+ banner = id( "qunit-header" );
+ if ( banner ) {
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined }) + "'>" + banner.innerHTML + "</a> " + urlConfigHtml;
+ addEvent( banner, "change", function( event ) {
+ var params = {};
+ params[ event.target.name ] = event.target.checked ? true : undefined;
+ window.location = QUnit.url( params );
+ });
+ }
+
+ // `toolbar` initialized at top of scope
+ toolbar = id( "qunit-testrunner-toolbar" );
+ if ( toolbar ) {
+ // `filter` initialized at top of scope
+ filter = document.createElement( "input" );
+ filter.type = "checkbox";
+ filter.id = "qunit-filter-pass";
+
+ addEvent( filter, "click", function() {
+ var tmp,
+ ol = document.getElementById( "qunit-tests" );
+
+ if ( filter.checked ) {
+ ol.className = ol.className + " hidepass";
+ } else {
+ tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
+ ol.className = tmp.replace( / hidepass /, " " );
+ }
+ if ( defined.sessionStorage ) {
+ if (filter.checked) {
+ sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
+ } else {
+ sessionStorage.removeItem( "qunit-filter-passed-tests" );
+ }
+ }
+ });
+
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
+ filter.checked = true;
+ // `ol` initialized at top of scope
+ ol = document.getElementById( "qunit-tests" );
+ ol.className = ol.className + " hidepass";
+ }
+ toolbar.appendChild( filter );
+
+ // `label` initialized at top of scope
+ label = document.createElement( "label" );
+ label.setAttribute( "for", "qunit-filter-pass" );
+ label.innerHTML = "Hide passed tests";
+ toolbar.appendChild( label );
+ }
+
+ // `main` initialized at top of scope
+ main = id( "qunit-fixture" );
+ if ( main ) {
+ config.fixture = main.innerHTML;
+ }
+
+ if ( config.autostart ) {
+ QUnit.start();
+ }
+};
+
+addEvent( window, "load", QUnit.load );
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will surpress the default browser handler,
+// returning false will let it run.
+window.onerror = function ( error, filePath, linerNr ) {
+ var ret = false;
+ if ( onErrorFnPrev ) {
+ ret = onErrorFnPrev( error, filePath, linerNr );
+ }
+
+ // Treat return value as window.onerror itself does,
+ // Only do our handling if not surpressed.
+ if ( ret !== true ) {
+ if ( QUnit.config.current ) {
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
+ return true;
+ }
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ } else {
+ QUnit.test( "global failure", function() {
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ });
+ }
+ return false;
+ }
+
+ return ret;
+};
+
+function done() {
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.currentModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.currentModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ runtime = +new Date() - config.started,
+ passed = config.stats.all - config.stats.bad,
+ html = [
+ "Tests completed in ",
+ runtime,
+ " milliseconds.<br/>",
+ "<span class='passed'>",
+ passed,
+ "</span> tests of <span class='total'>",
+ config.stats.all,
+ "</span> passed, <span class='failed'>",
+ config.stats.bad,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && typeof document !== "undefined" && document.title ) {
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( config.stats.bad ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
+ // `key` & `i` initialized at top of scope
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ runLoggingCallbacks( "done", QUnit, {
+ failed: config.stats.bad,
+ passed: passed,
+ total: config.stats.all,
+ runtime: runtime
+ });
+}
+
+/** @return Boolean: true if this test should be ran */
+function validTest( test ) {
+ var include,
+ filter = config.filter && config.filter.toLowerCase(),
+ module = config.module,
+ fullName = (test.module + ": " + test.testName).toLowerCase();
+
+ if ( config.testNumber ) {
+ return test.testNumber === config.testNumber;
+ }
+
+ if ( module && test.module !== module ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
+// Later Safari and IE10 are supposed to support error.stack as well
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+ offset = offset === undefined ? 3 : offset;
+
+ var stack, include, i, regex;
+
+ if ( e.stacktrace ) {
+ // Opera
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
+ } else if ( e.stack ) {
+ // Firefox, Chrome
+ stack = e.stack.split( "\n" );
+ if (/^error$/i.test( stack[0] ) ) {
+ stack.shift();
+ }
+ if ( fileName ) {
+ include = [];
+ for ( i = offset; i < stack.length; i++ ) {
+ if ( stack[ i ].indexOf( fileName ) != -1 ) {
+ break;
+ }
+ include.push( stack[ i ] );
+ }
+ if ( include.length ) {
+ return include.join( "\n" );
+ }
+ }
+ return stack[ offset ];
+ } else if ( e.sourceURL ) {
+ // Safari, PhantomJS
+ // hopefully one day Safari provides actual stacktraces
+ // exclude useless self-reference for generated Error objects
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
+ return;
+ }
+ // for actual exceptions, this is useful
+ return e.sourceURL + ":" + e.line;
+ }
+}
+function sourceFromStacktrace( offset ) {
+ try {
+ throw new Error();
+ } catch ( e ) {
+ return extractStacktrace( e, offset );
+ }
+}
+
+function escapeInnerText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+ return s.replace( /[\&<>]/g, function( s ) {
+ switch( s ) {
+ case "&": return "&";
+ case "<": return "<";
+ case ">": return ">";
+ default: return s;
+ }
+ });
+}
+
+function synchronize( callback, last ) {
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process( last );
+ }
+}
+
+function process( last ) {
+ function next() {
+ process( last );
+ }
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
+
+ while ( config.queue.length && !config.blocking ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
+ config.queue.shift()();
+ } else {
+ window.setTimeout( next, 13 );
+ break;
+ }
+ }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
+ }
+ }
+}
+
+function checkPollution( name ) {
+ var newGlobals,
+ deletedGlobals,
+ old = config.pollution;
+
+ saveGlobal();
+
+ newGlobals = diff( config.pollution, old );
+ if ( newGlobals.length > 0 ) {
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
+ }
+
+ deletedGlobals = diff( old, config.pollution );
+ if ( deletedGlobals.length > 0 ) {
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var i, j,
+ result = a.slice();
+
+ for ( i = 0; i < result.length; i++ ) {
+ for ( j = 0; j < b.length; j++ ) {
+ if ( result[i] === b[j] ) {
+ result.splice( i, 1 );
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function extend( a, b ) {
+ for ( var prop in b ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
+ } else if ( prop !== "constructor" || a !== window ) {
+ a[ prop ] = b[ prop ];
+ }
+ }
+
+ return a;
+}
+
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, fn );
+ } else {
+ fn();
+ }
+}
+
+function id( name ) {
+ return !!( typeof document !== "undefined" && document && document.getElementById ) &&
+ document.getElementById( name );
+}
+
+function registerLoggingCallback( key ) {
+ return function( callback ) {
+ config[key].push( callback );
+ };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks( key, scope, args ) {
+ //debugger;
+ var i, callbacks;
+ if ( QUnit.hasOwnProperty( key ) ) {
+ QUnit[ key ].call(scope, args );
+ } else {
+ callbacks = config[ key ];
+ for ( i = 0; i < callbacks.length; i++ ) {
+ callbacks[ i ].call( scope, args );
+ }
+ }
+}
+
+// Test for equality any JavaScript type.
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = (function() {
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks( o, callbacks, args ) {
+ var prop = QUnit.objectType( o );
+ if ( prop ) {
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+ return callbacks[ prop ].apply( callbacks, args );
+ } else {
+ return callbacks[ prop ]; // or undefined
+ }
+ }
+ }
+
+ // the real equiv function
+ var innerEquiv,
+ // stack to decide between skip/abort functions
+ callers = [],
+ // stack to avoiding loops from circular referencing
+ parents = [],
+
+ getProto = Object.getPrototypeOf || function ( obj ) {
+ return obj.__proto__;
+ },
+ callbacks = (function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality( b, a ) {
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
+ // to catch short annotaion VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function( b ) {
+ return isNaN( b );
+ },
+
+ "date": function( b, a ) {
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function( b, a ) {
+ return QUnit.objectType( b ) === "regexp" &&
+ // the regex itself
+ a.source === b.source &&
+ // and its modifers
+ a.global === b.global &&
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function() {
+ var caller = callers[callers.length - 1];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array": function( b, a ) {
+ var i, j, len, loop;
+
+ // b could be an object literal here
+ if ( QUnit.objectType( b ) !== "array" ) {
+ return false;
+ }
+
+ len = a.length;
+ if ( len !== b.length ) {
+ // safe and faster
+ return false;
+ }
+
+ // track reference to avoid circular references
+ parents.push( a );
+ for ( i = 0; i < len; i++ ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ loop = true;// dont rewalk array
+ }
+ }
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
+ parents.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ return true;
+ },
+
+ "object": function( b, a ) {
+ var i, j, loop,
+ // Default to true
+ eq = true,
+ aProperties = [],
+ bProperties = [];
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if ( a.constructor !== b.constructor ) {
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
+ ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push( a.constructor );
+ // track reference to avoid circular references
+ parents.push( a );
+
+ for ( i in a ) { // be strict: don't ensures hasOwnProperty
+ // and go deep
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ // don't go down the same path twice
+ loop = true;
+ }
+ }
+ aProperties.push(i); // collect a's properties
+
+ if (!loop && !innerEquiv( a[i], b[i] ) ) {
+ eq = false;
+ break;
+ }
+ }
+
+ callers.pop(); // unstack, we are done
+ parents.pop();
+
+ for ( i in b ) {
+ bProperties.push( i ); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+ }
+ };
+ }());
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = [].slice.apply( arguments );
+ if ( args.length < 2 ) {
+ return true; // end transition
+ }
+
+ return (function( a, b ) {
+ if ( a === b ) {
+ return true; // catch the most you can
+ } else if ( a === null || b === null || typeof a === "undefined" ||
+ typeof b === "undefined" ||
+ QUnit.objectType(a) !== QUnit.objectType(b) ) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [ b, a ]);
+ }
+
+ // apply transition with (1..n) arguments
+ }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
+ };
+
+ return innerEquiv;
+}());
+
+/**
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+ function quote( str ) {
+ return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
+ }
+ function literal( o ) {
+ return o + "";
+ }
+ function join( pre, arr, post ) {
+ var s = jsDump.separator(),
+ base = jsDump.indent(),
+ inner = jsDump.indent(1);
+ if ( arr.join ) {
+ arr = arr.join( "," + s + inner );
+ }
+ if ( !arr ) {
+ return pre + post;
+ }
+ return [ pre, inner + arr, base + post ].join(s);
+ }
+ function array( arr, stack ) {
+ var i = arr.length, ret = new Array(i);
+ this.up();
+ while ( i-- ) {
+ ret[i] = this.parse( arr[i] , undefined , stack);
+ }
+ this.down();
+ return join( "[", ret, "]" );
+ }
+
+ var reName = /^function (\w+)/,
+ jsDump = {
+ parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
+ stack = stack || [ ];
+ var inStack, res,
+ parser = this.parsers[ type || this.typeOf(obj) ];
+
+ type = typeof parser;
+ inStack = inArray( obj, stack );
+
+ if ( inStack != -1 ) {
+ return "recursion(" + (inStack - stack.length) + ")";
+ }
+ //else
+ if ( type == "function" ) {
+ stack.push( obj );
+ res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ // else
+ return ( type == "string" ) ? parser : this.parsers.error;
+ },
+ typeOf: function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if ( typeof obj === "undefined" ) {
+ type = "undefined";
+ } else if ( QUnit.is( "regexp", obj) ) {
+ type = "regexp";
+ } else if ( QUnit.is( "date", obj) ) {
+ type = "date";
+ } else if ( QUnit.is( "function", obj) ) {
+ type = "function";
+ } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
+ type = "window";
+ } else if ( obj.nodeType === 9 ) {
+ type = "document";
+ } else if ( obj.nodeType ) {
+ type = "node";
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+ ) {
+ type = "array";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator: function() {
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
+ },
+ indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
+ if ( !this.multiline ) {
+ return "";
+ }
+ var chr = this.indentChar;
+ if ( this.HTML ) {
+ chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
+ }
+ return new Array( this._depth_ + (extra||0) ).join(chr);
+ },
+ up: function( a ) {
+ this._depth_ += a || 1;
+ },
+ down: function( a ) {
+ this._depth_ -= a || 1;
+ },
+ setParser: function( name, parser ) {
+ this.parsers[name] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote: quote,
+ literal: literal,
+ join: join,
+ //
+ _depth_: 1,
+ // This is the list of parsers, to modify them, use jsDump.setParser
+ parsers: {
+ window: "[Window]",
+ document: "[Document]",
+ error: "[ERROR]", //when no parser is found, shouldn"t happen
+ unknown: "[Unknown]",
+ "null": "null",
+ "undefined": "undefined",
+ "function": function( fn ) {
+ var ret = "function",
+ name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
+
+ if ( name ) {
+ ret += " " + name;
+ }
+ ret += "( ";
+
+ ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
+ },
+ array: array,
+ nodelist: array,
+ "arguments": array,
+ object: function( map, stack ) {
+ var ret = [ ], keys, key, val, i;
+ QUnit.jsDump.up();
+ if ( Object.keys ) {
+ keys = Object.keys( map );
+ } else {
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
+ }
+ }
+ keys.sort();
+ for ( i = 0; i < keys.length; i++ ) {
+ key = keys[ i ];
+ val = map[ key ];
+ ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
+ }
+ QUnit.jsDump.down();
+ return join( "{", ret, "}" );
+ },
+ node: function( node ) {
+ var a, val,
+ open = QUnit.jsDump.HTML ? "<" : "<",
+ close = QUnit.jsDump.HTML ? ">" : ">",
+ tag = node.nodeName.toLowerCase(),
+ ret = open + tag;
+
+ for ( a in QUnit.jsDump.DOMAttrs ) {
+ val = node[ QUnit.jsDump.DOMAttrs[a] ];
+ if ( val ) {
+ ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
+ }
+ }
+ return ret + close + open + "/" + tag + close;
+ },
+ functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
+ var args,
+ l = fn.length;
+
+ if ( !l ) {
+ return "";
+ }
+
+ args = new Array(l);
+ while ( l-- ) {
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
+ }
+ return " " + args.join( ", " ) + " ";
+ },
+ key: quote, //object calls it internally, the key part of an item in a map
+ functionCode: "[code]", //function calls it internally, it's the content of the function
+ attribute: quote, //node calls it internally, it's an html attribute value
+ string: quote,
+ date: quote,
+ regexp: literal, //regex
+ number: literal,
+ "boolean": literal
+ },
+ DOMAttrs: {
+ //attributes to dump from nodes, name=>realName
+ id: "id",
+ name: "name",
+ "class": "className"
+ },
+ HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
+ indentChar: " ",//indentation unit
+ multiline: true //if true, items in a collection, are separated by a \n, else just a space.
+ };
+
+ return jsDump;
+}());
+
+// from Sizzle.js
+function getText( elems ) {
+ var i, elem,
+ ret = "";
+
+ for ( i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+ function diff( o, n ) {
+ var i,
+ ns = {},
+ os = {};
+
+ for ( i = 0; i < n.length; i++ ) {
+ if ( ns[ n[i] ] == null ) {
+ ns[ n[i] ] = {
+ rows: [],
+ o: null
+ };
+ }
+ ns[ n[i] ].rows.push( i );
+ }
+
+ for ( i = 0; i < o.length; i++ ) {
+ if ( os[ o[i] ] == null ) {
+ os[ o[i] ] = {
+ rows: [],
+ n: null
+ };
+ }
+ os[ o[i] ].rows.push( i );
+ }
+
+ for ( i in ns ) {
+ if ( !hasOwn.call( ns, i ) ) {
+ continue;
+ }
+ if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
+ n[ ns[i].rows[0] ] = {
+ text: n[ ns[i].rows[0] ],
+ row: os[i].rows[0]
+ };
+ o[ os[i].rows[0] ] = {
+ text: o[ os[i].rows[0] ],
+ row: ns[i].rows[0]
+ };
+ }
+ }
+
+ for ( i = 0; i < n.length - 1; i++ ) {
+ if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+ n[ i + 1 ] == o[ n[i].row + 1 ] ) {
+
+ n[ i + 1 ] = {
+ text: n[ i + 1 ],
+ row: n[i].row + 1
+ };
+ o[ n[i].row + 1 ] = {
+ text: o[ n[i].row + 1 ],
+ row: i + 1
+ };
+ }
+ }
+
+ for ( i = n.length - 1; i > 0; i-- ) {
+ if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+ n[ i - 1 ] == o[ n[i].row - 1 ]) {
+
+ n[ i - 1 ] = {
+ text: n[ i - 1 ],
+ row: n[i].row - 1
+ };
+ o[ n[i].row - 1 ] = {
+ text: o[ n[i].row - 1 ],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function( o, n ) {
+ o = o.replace( /\s+$/, "" );
+ n = n.replace( /\s+$/, "" );
+
+ var i, pre,
+ str = "",
+ out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
+ oSpace = o.match(/\s+/g),
+ nSpace = n.match(/\s+/g);
+
+ if ( oSpace == null ) {
+ oSpace = [ " " ];
+ }
+ else {
+ oSpace.push( " " );
+ }
+
+ if ( nSpace == null ) {
+ nSpace = [ " " ];
+ }
+ else {
+ nSpace.push( " " );
+ }
+
+ if ( out.n.length === 0 ) {
+ for ( i = 0; i < out.o.length; i++ ) {
+ str += "<del>" + out.o[i] + oSpace[i] + "</del>";
+ }
+ }
+ else {
+ if ( out.n[0].text == null ) {
+ for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
+ str += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ }
+
+ for ( i = 0; i < out.n.length; i++ ) {
+ if (out.n[i].text == null) {
+ str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
+ }
+ else {
+ // `pre` initialized at top of scope
+ pre = "";
+
+ for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+ pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+}());
+
+// for CommonJS enviroments, export everything
+if ( typeof exports !== "undefined" ) {
+ extend(exports, QUnit);
+}
+
+// get at whatever the global object is, like window in browsers
+}( (function() {return this;}.call()) ));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.css b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.css
new file mode 100644
index 0000000..257b224
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.css
@@ -0,0 +1,231 @@
+/**
+ * QUnit v1.9.0 - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * or GPL (GPL-LICENSE.txt) licenses.
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699a4;
+ background-color: #0d3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: normal;
+
+ border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ -webkit-border-top-right-radius: 5px;
+ -webkit-border-top-left-radius: 5px;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #c2ccd1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #fff;
+}
+
+#qunit-testrunner-toolbar label {
+ display: inline-block;
+ padding: 0 .5em 0 .1em;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0.5em 0 0.5em 2em;
+ color: #5E740B;
+ background-color: #eee;
+}
+
+#qunit-userAgent {
+ padding: 0.5em 0 0.5em 2.5em;
+ background-color: #2b81af;
+ color: #fff;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 0.5em 0.4em 2.5em;
+ border-bottom: 1px solid #fff;
+ list-style-position: inside;
+}
+
+#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
+ display: none;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests li a {
+ padding: 0.5em;
+ color: #c2ccd1;
+ text-decoration: none;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+ color: #000;
+}
+
+#qunit-tests ol {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #fff;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: .2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 .5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #e0f2be;
+ color: #374e0c;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #ffcaca;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: black; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ padding: 5px;
+ background-color: #fff;
+ border-bottom: none;
+ list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #3c510c;
+ background-color: #fff;
+ border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #fff;
+ border-left: 10px solid #EE5757;
+ white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+ border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+}
+
+#qunit-tests .fail { color: #000000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: green; }
+
+#qunit-banner.qunit-fail { background-color: #EE5757; }
+
+
+/** Result */
+
+#qunit-testresult {
+ padding: 0.5em 0.5em 0.5em 2.5em;
+
+ color: #2b81af;
+ background-color: #D2E0E6;
+
+ border-bottom: 1px solid white;
+}
+#qunit-testresult .module-name {
+ font-weight: bold;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 1000px;
+ height: 1000px;
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.js
new file mode 100644
index 0000000..9efedcb
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/qunit/qunit/qunit.js
@@ -0,0 +1,1932 @@
+/**
+ * QUnit v1.9.0 - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * or GPL (GPL-LICENSE.txt) licenses.
+ */
+
+(function( window ) {
+
+var QUnit,
+ config,
+ onErrorFnPrev,
+ testId = 0,
+ fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ defined = {
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch( e ) {
+ return false;
+ }
+ }())
+};
+
+function Test( settings ) {
+ extend( this, settings );
+ this.assertions = [];
+ this.testNumber = ++Test.count;
+}
+
+Test.count = 0;
+
+Test.prototype = {
+ init: function() {
+ var a, b, li,
+ tests = id( "qunit-tests" );
+
+ if ( tests ) {
+ b = document.createElement( "strong" );
+ b.innerHTML = this.name;
+
+ // `a` initialized at top of scope
+ a = document.createElement( "a" );
+ a.innerHTML = "Rerun";
+ a.href = QUnit.url({ testNumber: this.testNumber });
+
+ li = document.createElement( "li" );
+ li.appendChild( b );
+ li.appendChild( a );
+ li.className = "running";
+ li.id = this.id = "qunit-test-output" + testId++;
+
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if ( this.module !== config.previousModule ) {
+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ } else if ( config.autorun ) {
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment );
+
+ runLoggingCallbacks( "testStart", QUnit, {
+ name: this.testName,
+ module: this.module
+ });
+
+ // allow utility functions to access the current test environment
+ // TODO why??
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ if ( config.notrycatch ) {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ return;
+ }
+ try {
+ this.testEnvironment.setup.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ },
+ run: function() {
+ config.current = this;
+
+ var running = id( "qunit-testresult" );
+
+ if ( running ) {
+ running.innerHTML = "Running: <br/>" + this.name;
+ }
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ if ( config.notrycatch ) {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ return;
+ }
+
+ try {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ } catch( e ) {
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+ teardown: function() {
+ config.current = this;
+ if ( config.notrycatch ) {
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ return;
+ } else {
+ try {
+ this.testEnvironment.teardown.call( this.testEnvironment );
+ } catch( e ) {
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ }
+ checkPollution();
+ },
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected == null ) {
+ QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
+ } else if ( this.expected != null && this.expected != this.assertions.length ) {
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
+ } else if ( this.expected == null && !this.assertions.length ) {
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var assertion, a, b, i, li, ol,
+ test = this,
+ good = 0,
+ bad = 0,
+ tests = id( "qunit-tests" );
+
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ ol = document.createElement( "ol" );
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ assertion = this.assertions[i];
+
+ li = document.createElement( "li" );
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ // store result when possible
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
+ }
+ }
+
+ if ( bad === 0 ) {
+ ol.style.display = "none";
+ }
+
+ // `b` initialized at top of scope
+ b = document.createElement( "strong" );
+ b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+ addEvent(b, "click", function() {
+ var next = b.nextSibling.nextSibling,
+ display = next.style.display;
+ next.style.display = display === "none" ? "block" : "none";
+ });
+
+ addEvent(b, "dblclick", function( e ) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+ target = target.parentNode;
+ }
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+ window.location = QUnit.url({ testNumber: test.testNumber });
+ }
+ });
+
+ // `li` initialized at top of scope
+ li = id( this.id );
+ li.className = bad ? "fail" : "pass";
+ li.removeChild( li.firstChild );
+ a = li.firstChild;
+ li.appendChild( b );
+ li.appendChild ( a );
+ li.appendChild( ol );
+
+ } else {
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ runLoggingCallbacks( "testDone", QUnit, {
+ name: this.testName,
+ module: this.module,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length
+ });
+
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ synchronize(function() {
+ test.init();
+ });
+ function run() {
+ // each of these can by async
+ synchronize(function() {
+ test.setup();
+ });
+ synchronize(function() {
+ test.run();
+ });
+ synchronize(function() {
+ test.teardown();
+ });
+ synchronize(function() {
+ test.finish();
+ });
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ }
+};
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+QUnit = {
+
+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ config.currentModule = name;
+ config.currentModuleTestEnviroment = testEnvironment;
+ },
+
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ QUnit.test( testName, expected, callback, true );
+ },
+
+ test: function( testName, expected, callback, async ) {
+ var test,
+ name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ if ( config.currentModule ) {
+ name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
+ }
+
+ test = new Test({
+ name: name,
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback,
+ module: config.currentModule,
+ moduleTestEnvironment: config.currentModuleTestEnviroment,
+ stack: sourceFromStacktrace( 2 )
+ });
+
+ if ( !validTest( test ) ) {
+ return;
+ }
+
+ test.queue();
+ },
+
+ // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ config.current.expected = asserts;
+ },
+
+ start: function( count ) {
+ config.semaphore -= count || 1;
+ // don't start until equal number of stop-calls
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ // ignore if start is called more often then stop
+ if ( config.semaphore < 0 ) {
+ config.semaphore = 0;
+ }
+ // A slight delay, to avoid any current callbacks
+ if ( defined.setTimeout ) {
+ window.setTimeout(function() {
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ config.blocking = false;
+ process( true );
+ }, 13);
+ } else {
+ config.blocking = false;
+ process( true );
+ }
+ },
+
+ stop: function( count ) {
+ config.semaphore += count || 1;
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = window.setTimeout(function() {
+ QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
+ QUnit.start();
+ }, config.testTimeout );
+ }
+ }
+};
+
+// Asssert helpers
+// All of these must call either QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+QUnit.assert = {
+ /**
+ * Asserts rough true-ish result.
+ * @name ok
+ * @function
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function( result, msg ) {
+ if ( !config.current ) {
+ throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+ result = !!result;
+
+ var source,
+ details = {
+ result: result,
+ message: msg
+ };
+
+ msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
+ msg = "<span class='test-message'>" + msg + "</span>";
+
+ if ( !result ) {
+ source = sourceFromStacktrace( 2 );
+ if ( source ) {
+ details.source = source;
+ msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+ }
+ }
+ runLoggingCallbacks( "log", QUnit, details );
+ config.current.assertions.push({
+ result: result,
+ message: msg
+ });
+ },
+
+ /**
+ * Assert that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ * @name equal
+ * @function
+ * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
+ */
+ equal: function( actual, expected, message ) {
+ QUnit.push( expected == actual, actual, expected, message );
+ },
+
+ /**
+ * @name notEqual
+ * @function
+ */
+ notEqual: function( actual, expected, message ) {
+ QUnit.push( expected != actual, actual, expected, message );
+ },
+
+ /**
+ * @name deepEqual
+ * @function
+ */
+ deepEqual: function( actual, expected, message ) {
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notDeepEqual
+ * @function
+ */
+ notDeepEqual: function( actual, expected, message ) {
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name strictEqual
+ * @function
+ */
+ strictEqual: function( actual, expected, message ) {
+ QUnit.push( expected === actual, actual, expected, message );
+ },
+
+ /**
+ * @name notStrictEqual
+ * @function
+ */
+ notStrictEqual: function( actual, expected, message ) {
+ QUnit.push( expected !== actual, actual, expected, message );
+ },
+
+ throws: function( block, expected, message ) {
+ var actual,
+ ok = false;
+
+ // 'expected' is optional
+ if ( typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ config.current.ignoreGlobalErrors = true;
+ try {
+ block.call( config.current.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ config.current.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ // expected is a regexp
+ } else if ( QUnit.objectType( expected ) === "regexp" ) {
+ ok = expected.test( actual );
+ // expected is a constructor
+ } else if ( actual instanceof expected ) {
+ ok = true;
+ // expected is a validation function which returns true is validation passed
+ } else if ( expected.call( {}, actual ) === true ) {
+ ok = true;
+ }
+
+ QUnit.push( ok, actual, null, message );
+ } else {
+ QUnit.pushFailure( message, null, 'No exception was thrown.' );
+ }
+ }
+};
+
+/**
+ * @deprecate since 1.8.0
+ * Kept assertion helpers in root for backwards compatibility
+ */
+extend( QUnit, QUnit.assert );
+
+/**
+ * @deprecated since 1.9.0
+ * Kept global "raises()" for backwards compatibility
+ */
+QUnit.raises = QUnit.assert.throws;
+
+/**
+ * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
+ * Kept to avoid TypeErrors for undefined methods.
+ */
+QUnit.equals = function() {
+ QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.same = function() {
+ QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
+};
+
+// We want access to the constructor's prototype
+(function() {
+ function F() {}
+ F.prototype = QUnit;
+ QUnit = new F();
+ // Make F QUnit's constructor so that we can add to the prototype later
+ QUnit.constructor = F;
+}());
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true,
+
+ // when enabled, show only failing tests
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
+ hidepassed: false,
+
+ // by default, run previously failed tests first
+ // very useful in combination with "Hide passed tests" checked
+ reorder: true,
+
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ // when enabled, all tests must call expect()
+ requireExpects: false,
+
+ // add checkboxes that are persisted in the query-string
+ // when enabled, the id is set to `true` as a `QUnit.config` property
+ urlConfig: [
+ {
+ id: "noglobals",
+ label: "Check for Globals",
+ tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
+ },
+ {
+ id: "notrycatch",
+ label: "No try-catch",
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
+ }
+ ],
+
+ // logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
+};
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+ var i,
+ location = window.location || { search: "", protocol: "file:" },
+ params = location.search.slice( 1 ).split( "&" ),
+ length = params.length,
+ urlParams = {},
+ current;
+
+ if ( params[ 0 ] ) {
+ for ( i = 0; i < length; i++ ) {
+ current = params[ i ].split( "=" );
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
+ }
+
+ QUnit.urlParams = urlParams;
+
+ // String search anywhere in moduleName+testName
+ config.filter = urlParams.filter;
+
+ // Exact match of the module name
+ config.module = urlParams.module;
+
+ config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = location.protocol === "file:";
+}());
+
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
+if ( typeof exports === "undefined" ) {
+ extend( window, QUnit );
+
+ // Expose QUnit object
+ window.QUnit = QUnit;
+}
+
+// Extend QUnit object,
+// these after set here because they should not be exposed as global functions
+extend( QUnit, {
+ config: config,
+
+ // Initialize the configuration options
+ init: function() {
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date(),
+ updateRate: 1000,
+ blocking: false,
+ autostart: true,
+ autorun: false,
+ filter: "",
+ queue: [],
+ semaphore: 0
+ });
+
+ var tests, banner, result,
+ qunit = id( "qunit" );
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br/> ";
+ }
+ },
+
+ // Resets the test setup. Useful for tests that modify the DOM.
+ // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
+ reset: function() {
+ var fixture;
+
+ if ( window.jQuery ) {
+ jQuery( "#qunit-fixture" ).html( config.fixture );
+ } else {
+ fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+ }
+ },
+
+ // Trigger an event on an element.
+ // @example triggerEvent( document.body, "click" );
+ triggerEvent: function( elem, type, event ) {
+ if ( document.createEvent ) {
+ event = document.createEvent( "MouseEvents" );
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+
+ elem.dispatchEvent( event );
+ } else if ( elem.fireEvent ) {
+ elem.fireEvent( "on" + type );
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) == type;
+ },
+
+ objectType: function( obj ) {
+ if ( typeof obj === "undefined" ) {
+ return "undefined";
+ // consider: typeof null === object
+ }
+ if ( obj === null ) {
+ return "null";
+ }
+
+ var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
+
+ switch ( type ) {
+ case "Number":
+ if ( isNaN(obj) ) {
+ return "nan";
+ }
+ return "number";
+ case "String":
+ case "Boolean":
+ case "Array":
+ case "Date":
+ case "RegExp":
+ case "Function":
+ return type.toLowerCase();
+ }
+ if ( typeof obj === "object" ) {
+ return "object";
+ }
+ return undefined;
+ },
+
+ push: function( result, actual, expected, message ) {
+ if ( !config.current ) {
+ throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
+ }
+
+ var output, source,
+ details = {
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected
+ };
+
+ message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ if ( !result ) {
+ expected = escapeInnerText( QUnit.jsDump.parse(expected) );
+ actual = escapeInnerText( QUnit.jsDump.parse(actual) );
+ output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
+
+ if ( actual != expected ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
+ output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ }
+
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+ }
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: !!result,
+ message: output
+ });
+ },
+
+ pushFailure: function( message, source, actual ) {
+ if ( !config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+
+ var output,
+ details = {
+ result: false,
+ message: message
+ };
+
+ message = escapeInnerText( message ) || "error";
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ output += "<table>";
+
+ if ( actual ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>";
+ }
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: false,
+ message: output
+ });
+ },
+
+ url: function( params ) {
+ params = extend( extend( {}, QUnit.urlParams ), params );
+ var key,
+ querystring = "?";
+
+ for ( key in params ) {
+ if ( !hasOwn.call( params, key ) ) {
+ continue;
+ }
+ querystring += encodeURIComponent( key ) + "=" +
+ encodeURIComponent( params[ key ] ) + "&";
+ }
+ return window.location.pathname + querystring.slice( 0, -1 );
+ },
+
+ extend: extend,
+ id: id,
+ addEvent: addEvent
+ // load, equiv, jsDump, diff: Attached later
+});
+
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
+extend( QUnit.constructor.prototype, {
+
+ // Logging callbacks; all receive a single argument with the listed properties
+ // run test/logs.html for any related changes
+ begin: registerLoggingCallback( "begin" ),
+
+ // done: { failed, passed, total, runtime }
+ done: registerLoggingCallback( "done" ),
+
+ // log: { result, actual, expected, message }
+ log: registerLoggingCallback( "log" ),
+
+ // testStart: { name }
+ testStart: registerLoggingCallback( "testStart" ),
+
+ // testDone: { name, failed, passed, total }
+ testDone: registerLoggingCallback( "testDone" ),
+
+ // moduleStart: { name }
+ moduleStart: registerLoggingCallback( "moduleStart" ),
+
+ // moduleDone: { name, failed, passed, total }
+ moduleDone: registerLoggingCallback( "moduleDone" )
+});
+
+if ( typeof document === "undefined" || document.readyState === "complete" ) {
+ config.autorun = true;
+}
+
+QUnit.load = function() {
+ runLoggingCallbacks( "begin", QUnit, {} );
+
+ // Initialize the config, saving the execution queue
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes,
+ urlConfigHtml = "",
+ oldconfig = extend( {}, config );
+
+ QUnit.init();
+ extend(config, oldconfig);
+
+ config.blocking = false;
+
+ len = config.urlConfig.length;
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[i];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val,
+ tooltip: "[no tooltip available]"
+ };
+ }
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
+ }
+
+ // `userAgent` initialized at top of scope
+ userAgent = id( "qunit-userAgent" );
+ if ( userAgent ) {
+ userAgent.innerHTML = navigator.userAgent;
+ }
+
+ // `banner` initialized at top of scope
+ banner = id( "qunit-header" );
+ if ( banner ) {
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
+ }
+
+ // `toolbar` initialized at top of scope
+ toolbar = id( "qunit-testrunner-toolbar" );
+ if ( toolbar ) {
+ // `filter` initialized at top of scope
+ filter = document.createElement( "input" );
+ filter.type = "checkbox";
+ filter.id = "qunit-filter-pass";
+
+ addEvent( filter, "click", function() {
+ var tmp,
+ ol = document.getElementById( "qunit-tests" );
+
+ if ( filter.checked ) {
+ ol.className = ol.className + " hidepass";
+ } else {
+ tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
+ ol.className = tmp.replace( / hidepass /, " " );
+ }
+ if ( defined.sessionStorage ) {
+ if (filter.checked) {
+ sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
+ } else {
+ sessionStorage.removeItem( "qunit-filter-passed-tests" );
+ }
+ }
+ });
+
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
+ filter.checked = true;
+ // `ol` initialized at top of scope
+ ol = document.getElementById( "qunit-tests" );
+ ol.className = ol.className + " hidepass";
+ }
+ toolbar.appendChild( filter );
+
+ // `label` initialized at top of scope
+ label = document.createElement( "label" );
+ label.setAttribute( "for", "qunit-filter-pass" );
+ label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
+ label.innerHTML = "Hide passed tests";
+ toolbar.appendChild( label );
+
+ urlConfigCheckboxes = document.createElement( 'span' );
+ urlConfigCheckboxes.innerHTML = urlConfigHtml;
+ addEvent( urlConfigCheckboxes, "change", function( event ) {
+ var params = {};
+ params[ event.target.name ] = event.target.checked ? true : undefined;
+ window.location = QUnit.url( params );
+ });
+ toolbar.appendChild( urlConfigCheckboxes );
+ }
+
+ // `main` initialized at top of scope
+ main = id( "qunit-fixture" );
+ if ( main ) {
+ config.fixture = main.innerHTML;
+ }
+
+ if ( config.autostart ) {
+ QUnit.start();
+ }
+};
+
+addEvent( window, "load", QUnit.load );
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will surpress the default browser handler,
+// returning false will let it run.
+window.onerror = function ( error, filePath, linerNr ) {
+ var ret = false;
+ if ( onErrorFnPrev ) {
+ ret = onErrorFnPrev( error, filePath, linerNr );
+ }
+
+ // Treat return value as window.onerror itself does,
+ // Only do our handling if not surpressed.
+ if ( ret !== true ) {
+ if ( QUnit.config.current ) {
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
+ return true;
+ }
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ } else {
+ QUnit.test( "global failure", function() {
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ });
+ }
+ return false;
+ }
+
+ return ret;
+};
+
+function done() {
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.currentModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.currentModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ runtime = +new Date() - config.started,
+ passed = config.stats.all - config.stats.bad,
+ html = [
+ "Tests completed in ",
+ runtime,
+ " milliseconds.<br/>",
+ "<span class='passed'>",
+ passed,
+ "</span> tests of <span class='total'>",
+ config.stats.all,
+ "</span> passed, <span class='failed'>",
+ config.stats.bad,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && typeof document !== "undefined" && document.title ) {
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( config.stats.bad ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
+ // `key` & `i` initialized at top of scope
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ runLoggingCallbacks( "done", QUnit, {
+ failed: config.stats.bad,
+ passed: passed,
+ total: config.stats.all,
+ runtime: runtime
+ });
+}
+
+/** @return Boolean: true if this test should be ran */
+function validTest( test ) {
+ var include,
+ filter = config.filter && config.filter.toLowerCase(),
+ module = config.module && config.module.toLowerCase(),
+ fullName = (test.module + ": " + test.testName).toLowerCase();
+
+ if ( config.testNumber ) {
+ return test.testNumber === config.testNumber;
+ }
+
+ if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
+// Later Safari and IE10 are supposed to support error.stack as well
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+ offset = offset === undefined ? 3 : offset;
+
+ var stack, include, i, regex;
+
+ if ( e.stacktrace ) {
+ // Opera
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
+ } else if ( e.stack ) {
+ // Firefox, Chrome
+ stack = e.stack.split( "\n" );
+ if (/^error$/i.test( stack[0] ) ) {
+ stack.shift();
+ }
+ if ( fileName ) {
+ include = [];
+ for ( i = offset; i < stack.length; i++ ) {
+ if ( stack[ i ].indexOf( fileName ) != -1 ) {
+ break;
+ }
+ include.push( stack[ i ] );
+ }
+ if ( include.length ) {
+ return include.join( "\n" );
+ }
+ }
+ return stack[ offset ];
+ } else if ( e.sourceURL ) {
+ // Safari, PhantomJS
+ // hopefully one day Safari provides actual stacktraces
+ // exclude useless self-reference for generated Error objects
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
+ return;
+ }
+ // for actual exceptions, this is useful
+ return e.sourceURL + ":" + e.line;
+ }
+}
+function sourceFromStacktrace( offset ) {
+ try {
+ throw new Error();
+ } catch ( e ) {
+ return extractStacktrace( e, offset );
+ }
+}
+
+function escapeInnerText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+ return s.replace( /[\&<>]/g, function( s ) {
+ switch( s ) {
+ case "&": return "&";
+ case "<": return "<";
+ case ">": return ">";
+ default: return s;
+ }
+ });
+}
+
+function synchronize( callback, last ) {
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process( last );
+ }
+}
+
+function process( last ) {
+ function next() {
+ process( last );
+ }
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
+
+ while ( config.queue.length && !config.blocking ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
+ config.queue.shift()();
+ } else {
+ window.setTimeout( next, 13 );
+ break;
+ }
+ }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
+ }
+ }
+}
+
+function checkPollution( name ) {
+ var newGlobals,
+ deletedGlobals,
+ old = config.pollution;
+
+ saveGlobal();
+
+ newGlobals = diff( config.pollution, old );
+ if ( newGlobals.length > 0 ) {
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
+ }
+
+ deletedGlobals = diff( old, config.pollution );
+ if ( deletedGlobals.length > 0 ) {
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var i, j,
+ result = a.slice();
+
+ for ( i = 0; i < result.length; i++ ) {
+ for ( j = 0; j < b.length; j++ ) {
+ if ( result[i] === b[j] ) {
+ result.splice( i, 1 );
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function extend( a, b ) {
+ for ( var prop in b ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
+ } else if ( prop !== "constructor" || a !== window ) {
+ a[ prop ] = b[ prop ];
+ }
+ }
+
+ return a;
+}
+
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, fn );
+ } else {
+ fn();
+ }
+}
+
+function id( name ) {
+ return !!( typeof document !== "undefined" && document && document.getElementById ) &&
+ document.getElementById( name );
+}
+
+function registerLoggingCallback( key ) {
+ return function( callback ) {
+ config[key].push( callback );
+ };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks( key, scope, args ) {
+ //debugger;
+ var i, callbacks;
+ if ( QUnit.hasOwnProperty( key ) ) {
+ QUnit[ key ].call(scope, args );
+ } else {
+ callbacks = config[ key ];
+ for ( i = 0; i < callbacks.length; i++ ) {
+ callbacks[ i ].call( scope, args );
+ }
+ }
+}
+
+// Test for equality any JavaScript type.
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = (function() {
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks( o, callbacks, args ) {
+ var prop = QUnit.objectType( o );
+ if ( prop ) {
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+ return callbacks[ prop ].apply( callbacks, args );
+ } else {
+ return callbacks[ prop ]; // or undefined
+ }
+ }
+ }
+
+ // the real equiv function
+ var innerEquiv,
+ // stack to decide between skip/abort functions
+ callers = [],
+ // stack to avoiding loops from circular referencing
+ parents = [],
+
+ getProto = Object.getPrototypeOf || function ( obj ) {
+ return obj.__proto__;
+ },
+ callbacks = (function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality( b, a ) {
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
+ // to catch short annotaion VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function( b ) {
+ return isNaN( b );
+ },
+
+ "date": function( b, a ) {
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function( b, a ) {
+ return QUnit.objectType( b ) === "regexp" &&
+ // the regex itself
+ a.source === b.source &&
+ // and its modifers
+ a.global === b.global &&
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function() {
+ var caller = callers[callers.length - 1];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array": function( b, a ) {
+ var i, j, len, loop;
+
+ // b could be an object literal here
+ if ( QUnit.objectType( b ) !== "array" ) {
+ return false;
+ }
+
+ len = a.length;
+ if ( len !== b.length ) {
+ // safe and faster
+ return false;
+ }
+
+ // track reference to avoid circular references
+ parents.push( a );
+ for ( i = 0; i < len; i++ ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ loop = true;// dont rewalk array
+ }
+ }
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
+ parents.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ return true;
+ },
+
+ "object": function( b, a ) {
+ var i, j, loop,
+ // Default to true
+ eq = true,
+ aProperties = [],
+ bProperties = [];
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if ( a.constructor !== b.constructor ) {
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
+ ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push( a.constructor );
+ // track reference to avoid circular references
+ parents.push( a );
+
+ for ( i in a ) { // be strict: don't ensures hasOwnProperty
+ // and go deep
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ if ( parents[j] === a[i] ) {
+ // don't go down the same path twice
+ loop = true;
+ }
+ }
+ aProperties.push(i); // collect a's properties
+
+ if (!loop && !innerEquiv( a[i], b[i] ) ) {
+ eq = false;
+ break;
+ }
+ }
+
+ callers.pop(); // unstack, we are done
+ parents.pop();
+
+ for ( i in b ) {
+ bProperties.push( i ); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+ }
+ };
+ }());
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = [].slice.apply( arguments );
+ if ( args.length < 2 ) {
+ return true; // end transition
+ }
+
+ return (function( a, b ) {
+ if ( a === b ) {
+ return true; // catch the most you can
+ } else if ( a === null || b === null || typeof a === "undefined" ||
+ typeof b === "undefined" ||
+ QUnit.objectType(a) !== QUnit.objectType(b) ) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [ b, a ]);
+ }
+
+ // apply transition with (1..n) arguments
+ }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
+ };
+
+ return innerEquiv;
+}());
+
+/**
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+ function quote( str ) {
+ return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
+ }
+ function literal( o ) {
+ return o + "";
+ }
+ function join( pre, arr, post ) {
+ var s = jsDump.separator(),
+ base = jsDump.indent(),
+ inner = jsDump.indent(1);
+ if ( arr.join ) {
+ arr = arr.join( "," + s + inner );
+ }
+ if ( !arr ) {
+ return pre + post;
+ }
+ return [ pre, inner + arr, base + post ].join(s);
+ }
+ function array( arr, stack ) {
+ var i = arr.length, ret = new Array(i);
+ this.up();
+ while ( i-- ) {
+ ret[i] = this.parse( arr[i] , undefined , stack);
+ }
+ this.down();
+ return join( "[", ret, "]" );
+ }
+
+ var reName = /^function (\w+)/,
+ jsDump = {
+ parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
+ stack = stack || [ ];
+ var inStack, res,
+ parser = this.parsers[ type || this.typeOf(obj) ];
+
+ type = typeof parser;
+ inStack = inArray( obj, stack );
+
+ if ( inStack != -1 ) {
+ return "recursion(" + (inStack - stack.length) + ")";
+ }
+ //else
+ if ( type == "function" ) {
+ stack.push( obj );
+ res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ // else
+ return ( type == "string" ) ? parser : this.parsers.error;
+ },
+ typeOf: function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if ( typeof obj === "undefined" ) {
+ type = "undefined";
+ } else if ( QUnit.is( "regexp", obj) ) {
+ type = "regexp";
+ } else if ( QUnit.is( "date", obj) ) {
+ type = "date";
+ } else if ( QUnit.is( "function", obj) ) {
+ type = "function";
+ } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
+ type = "window";
+ } else if ( obj.nodeType === 9 ) {
+ type = "document";
+ } else if ( obj.nodeType ) {
+ type = "node";
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+ ) {
+ type = "array";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator: function() {
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
+ },
+ indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
+ if ( !this.multiline ) {
+ return "";
+ }
+ var chr = this.indentChar;
+ if ( this.HTML ) {
+ chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
+ }
+ return new Array( this._depth_ + (extra||0) ).join(chr);
+ },
+ up: function( a ) {
+ this._depth_ += a || 1;
+ },
+ down: function( a ) {
+ this._depth_ -= a || 1;
+ },
+ setParser: function( name, parser ) {
+ this.parsers[name] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote: quote,
+ literal: literal,
+ join: join,
+ //
+ _depth_: 1,
+ // This is the list of parsers, to modify them, use jsDump.setParser
+ parsers: {
+ window: "[Window]",
+ document: "[Document]",
+ error: "[ERROR]", //when no parser is found, shouldn"t happen
+ unknown: "[Unknown]",
+ "null": "null",
+ "undefined": "undefined",
+ "function": function( fn ) {
+ var ret = "function",
+ name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
+
+ if ( name ) {
+ ret += " " + name;
+ }
+ ret += "( ";
+
+ ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
+ },
+ array: array,
+ nodelist: array,
+ "arguments": array,
+ object: function( map, stack ) {
+ var ret = [ ], keys, key, val, i;
+ QUnit.jsDump.up();
+ if ( Object.keys ) {
+ keys = Object.keys( map );
+ } else {
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
+ }
+ }
+ keys.sort();
+ for ( i = 0; i < keys.length; i++ ) {
+ key = keys[ i ];
+ val = map[ key ];
+ ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
+ }
+ QUnit.jsDump.down();
+ return join( "{", ret, "}" );
+ },
+ node: function( node ) {
+ var a, val,
+ open = QUnit.jsDump.HTML ? "<" : "<",
+ close = QUnit.jsDump.HTML ? ">" : ">",
+ tag = node.nodeName.toLowerCase(),
+ ret = open + tag;
+
+ for ( a in QUnit.jsDump.DOMAttrs ) {
+ val = node[ QUnit.jsDump.DOMAttrs[a] ];
+ if ( val ) {
+ ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
+ }
+ }
+ return ret + close + open + "/" + tag + close;
+ },
+ functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
+ var args,
+ l = fn.length;
+
+ if ( !l ) {
+ return "";
+ }
+
+ args = new Array(l);
+ while ( l-- ) {
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
+ }
+ return " " + args.join( ", " ) + " ";
+ },
+ key: quote, //object calls it internally, the key part of an item in a map
+ functionCode: "[code]", //function calls it internally, it's the content of the function
+ attribute: quote, //node calls it internally, it's an html attribute value
+ string: quote,
+ date: quote,
+ regexp: literal, //regex
+ number: literal,
+ "boolean": literal
+ },
+ DOMAttrs: {
+ //attributes to dump from nodes, name=>realName
+ id: "id",
+ name: "name",
+ "class": "className"
+ },
+ HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
+ indentChar: " ",//indentation unit
+ multiline: true //if true, items in a collection, are separated by a \n, else just a space.
+ };
+
+ return jsDump;
+}());
+
+// from Sizzle.js
+function getText( elems ) {
+ var i, elem,
+ ret = "";
+
+ for ( i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+ function diff( o, n ) {
+ var i,
+ ns = {},
+ os = {};
+
+ for ( i = 0; i < n.length; i++ ) {
+ if ( ns[ n[i] ] == null ) {
+ ns[ n[i] ] = {
+ rows: [],
+ o: null
+ };
+ }
+ ns[ n[i] ].rows.push( i );
+ }
+
+ for ( i = 0; i < o.length; i++ ) {
+ if ( os[ o[i] ] == null ) {
+ os[ o[i] ] = {
+ rows: [],
+ n: null
+ };
+ }
+ os[ o[i] ].rows.push( i );
+ }
+
+ for ( i in ns ) {
+ if ( !hasOwn.call( ns, i ) ) {
+ continue;
+ }
+ if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
+ n[ ns[i].rows[0] ] = {
+ text: n[ ns[i].rows[0] ],
+ row: os[i].rows[0]
+ };
+ o[ os[i].rows[0] ] = {
+ text: o[ os[i].rows[0] ],
+ row: ns[i].rows[0]
+ };
+ }
+ }
+
+ for ( i = 0; i < n.length - 1; i++ ) {
+ if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+ n[ i + 1 ] == o[ n[i].row + 1 ] ) {
+
+ n[ i + 1 ] = {
+ text: n[ i + 1 ],
+ row: n[i].row + 1
+ };
+ o[ n[i].row + 1 ] = {
+ text: o[ n[i].row + 1 ],
+ row: i + 1
+ };
+ }
+ }
+
+ for ( i = n.length - 1; i > 0; i-- ) {
+ if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+ n[ i - 1 ] == o[ n[i].row - 1 ]) {
+
+ n[ i - 1 ] = {
+ text: n[ i - 1 ],
+ row: n[i].row - 1
+ };
+ o[ n[i].row - 1 ] = {
+ text: o[ n[i].row - 1 ],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function( o, n ) {
+ o = o.replace( /\s+$/, "" );
+ n = n.replace( /\s+$/, "" );
+
+ var i, pre,
+ str = "",
+ out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
+ oSpace = o.match(/\s+/g),
+ nSpace = n.match(/\s+/g);
+
+ if ( oSpace == null ) {
+ oSpace = [ " " ];
+ }
+ else {
+ oSpace.push( " " );
+ }
+
+ if ( nSpace == null ) {
+ nSpace = [ " " ];
+ }
+ else {
+ nSpace.push( " " );
+ }
+
+ if ( out.n.length === 0 ) {
+ for ( i = 0; i < out.o.length; i++ ) {
+ str += "<del>" + out.o[i] + oSpace[i] + "</del>";
+ }
+ }
+ else {
+ if ( out.n[0].text == null ) {
+ for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
+ str += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ }
+
+ for ( i = 0; i < out.n.length; i++ ) {
+ if (out.n[i].text == null) {
+ str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
+ }
+ else {
+ // `pre` initialized at top of scope
+ pre = "";
+
+ for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+ pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+}());
+
+// for CommonJS enviroments, export everything
+if ( typeof exports !== "undefined" ) {
+ extend(exports, QUnit);
+}
+
+// get at whatever the global object is, like window in browsers
+}( (function() {return this;}.call()) ));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/LICENSE b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/LICENSE
new file mode 100644
index 0000000..ffaf317
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/LICENSE
@@ -0,0 +1,58 @@
+RequireJS is released under two licenses: new BSD, and MIT. You may pick the
+license that best suits your development needs. The text of both licenses are
+provided below.
+
+
+The "New" BSD License:
+----------------------
+
+Copyright (c) 2010-2011, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Dojo Foundation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+MIT License
+-----------
+
+Copyright (c) 2010-2011, The Dojo Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/README.md
new file mode 100644
index 0000000..4d3f25e
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/README.md
@@ -0,0 +1,51 @@
+# RequireJS
+
+RequireJS loads plain JavaScript files as well as more defined modules. It is
+optimized for in-browser use, including in
+[a Web Worker](http://requirejs.org/docs/api.html#webworker), but it can be used
+in other JavaScript environments, like Rhino and
+[Node](http://requirejs.org/docs/node.html). It implements the
+[Asynchronous Module](https://github.com/amdjs/amdjs-api/wiki/AMD)
+API.
+
+RequireJS uses plain script tags to load modules/files, so it should allow for
+easy debugging. It can be used
+[simply to load existing JavaScript files](http://requirejs.org/docs/api.html#jsfiles),
+so you can add it to your existing project without having to re-write your
+JavaScript files.
+
+RequireJS includes [an optimization tool](http://requirejs.org/docs/optimization.html)
+you can run as part of your packaging steps for deploying your code. The
+optimization tool can combine and minify your JavaScript files to allow for
+better performance.
+
+If the JavaScript file defines a JavaScript module via
+[define()](http://requirejs.org/docs/api.html#define), then there are other benefits
+RequireJS can offer: [improvements over traditional CommonJS modules](http://requirejs.org/docs/commonjs.html)
+and [loading multiple versions](http://requirejs.org/docs/api.html#multiversion)
+of a module in a page. RequireJS also has a plugin system that supports features like
+[i18n string bundles](http://requirejs.org/docs/api.html#i18n), and
+[text file dependencies](http://requirejs.org/docs/api.html#text).
+
+RequireJS does not have any dependencies on a JavaScript framework.
+It is dual-licensed -- new BSD or MIT.
+
+The standard require.js file is around 5.5KB when minified via Closure Compiler
+and gzipped.
+
+RequireJS works in IE 6+, Firefox 2+, Safari 3.2+, Chrome 3+, and Opera 10+.
+
+[Latest Release](http://requirejs.org/docs/download.html)
+
+## Directories
+
+* **dist**: Scripts and assets to generate the requirejs.org docs, and for
+generating a require.js release.
+* **docs**: The raw HTML files for the requirejs.org docs. Only includes the
+body of each page. Files in **dist** are used to generate a complete HTML page.
+* **tests**: Tests for require.js.
+* **testBaseUrl.js**: A file used in the tests inside **tests**. Purposely
+placed outside the tests directory for testing paths that go outside a baseUrl.
+* **updatesubs.sh**: Updates projects that depend on require.js Assumes the
+projects are siblings to this directory and have specific names. Useful to
+copy require.js to dependent projects easily while in development.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/require.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/require.js
new file mode 100644
index 0000000..34cfe23
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/benchmark.js/vendor/requirejs/require.js
@@ -0,0 +1,2053 @@
+/** vim: et:ts=4:sw=4:sts=4
+ * @license RequireJS 2.0.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/requirejs for details
+ */
+//Not using strict: uneven strict support in browsers, #392, and causes
+//problems with requirejs.exec()/transpiler plugins that may not be strict.
+/*jslint regexp: true, nomen: true, sloppy: true */
+/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */
+
+var requirejs, require, define;
+(function (global) {
+ var req, s, head, baseElement, dataMain, src,
+ interactiveScript, currentlyAddingScript, mainScript, subPath,
+ version = '2.0.5',
+ commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
+ cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
+ jsSuffixRegExp = /\.js$/,
+ currDirRegExp = /^\.\//,
+ op = Object.prototype,
+ ostring = op.toString,
+ hasOwn = op.hasOwnProperty,
+ ap = Array.prototype,
+ aps = ap.slice,
+ apsp = ap.splice,
+ isBrowser = !!(typeof window !== 'undefined' && navigator && document),
+ isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
+ //PS3 indicates loaded and complete, but need to wait for complete
+ //specifically. Sequence is 'loading', 'loaded', execution,
+ // then 'complete'. The UA check is unfortunate, but not sure how
+ //to feature test w/o causing perf issues.
+ readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
+ /^complete$/ : /^(complete|loaded)$/,
+ defContextName = '_',
+ //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
+ isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
+ contexts = {},
+ cfg = {},
+ globalDefQueue = [],
+ useInteractive = false;
+
+ function isFunction(it) {
+ return ostring.call(it) === '[object Function]';
+ }
+
+ function isArray(it) {
+ return ostring.call(it) === '[object Array]';
+ }
+
+ /**
+ * Helper function for iterating over an array. If the func returns
+ * a true value, it will break out of the loop.
+ */
+ function each(ary, func) {
+ if (ary) {
+ var i;
+ for (i = 0; i < ary.length; i += 1) {
+ if (ary[i] && func(ary[i], i, ary)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function for iterating over an array backwards. If the func
+ * returns a true value, it will break out of the loop.
+ */
+ function eachReverse(ary, func) {
+ if (ary) {
+ var i;
+ for (i = ary.length - 1; i > -1; i -= 1) {
+ if (ary[i] && func(ary[i], i, ary)) {
+ break;
+ }
+ }
+ }
+ }
+
+ function hasProp(obj, prop) {
+ return hasOwn.call(obj, prop);
+ }
+
+ /**
+ * Cycles over properties in an object and calls a function for each
+ * property value. If the function returns a truthy value, then the
+ * iteration is stopped.
+ */
+ function eachProp(obj, func) {
+ var prop;
+ for (prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ if (func(obj[prop], prop)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Simple function to mix in properties from source into target,
+ * but only if target does not already have a property of the same name.
+ * This is not robust in IE for transferring methods that match
+ * Object.prototype names, but the uses of mixin here seem unlikely to
+ * trigger a problem related to that.
+ */
+ function mixin(target, source, force, deepStringMixin) {
+ if (source) {
+ eachProp(source, function (value, prop) {
+ if (force || !hasProp(target, prop)) {
+ if (deepStringMixin && typeof value !== 'string') {
+ if (!target[prop]) {
+ target[prop] = {};
+ }
+ mixin(target[prop], value, force, deepStringMixin);
+ } else {
+ target[prop] = value;
+ }
+ }
+ });
+ }
+ return target;
+ }
+
+ //Similar to Function.prototype.bind, but the 'this' object is specified
+ //first, since it is easier to read/figure out what 'this' will be.
+ function bind(obj, fn) {
+ return function () {
+ return fn.apply(obj, arguments);
+ };
+ }
+
+ function scripts() {
+ return document.getElementsByTagName('script');
+ }
+
+ //Allow getting a global that expressed in
+ //dot notation, like 'a.b.c'.
+ function getGlobal(value) {
+ if (!value) {
+ return value;
+ }
+ var g = global;
+ each(value.split('.'), function (part) {
+ g = g[part];
+ });
+ return g;
+ }
+
+ function makeContextModuleFunc(func, relMap, enableBuildCallback) {
+ return function () {
+ //A version of a require function that passes a moduleName
+ //value for items that may need to
+ //look up paths relative to the moduleName
+ var args = aps.call(arguments, 0), lastArg;
+ if (enableBuildCallback &&
+ isFunction((lastArg = args[args.length - 1]))) {
+ lastArg.__requireJsBuild = true;
+ }
+ args.push(relMap);
+ return func.apply(null, args);
+ };
+ }
+
+ function addRequireMethods(req, context, relMap) {
+ each([
+ ['toUrl'],
+ ['undef'],
+ ['defined', 'requireDefined'],
+ ['specified', 'requireSpecified']
+ ], function (item) {
+ var prop = item[1] || item[0];
+ req[item[0]] = context ? makeContextModuleFunc(context[prop], relMap) :
+ //If no context, then use default context. Reference from
+ //contexts instead of early binding to default context, so
+ //that during builds, the latest instance of the default
+ //context with its config gets used.
+ function () {
+ var ctx = contexts[defContextName];
+ return ctx[prop].apply(ctx, arguments);
+ };
+ });
+ }
+
+ /**
+ * Constructs an error with a pointer to an URL with more information.
+ * @param {String} id the error ID that maps to an ID on a web page.
+ * @param {String} message human readable error.
+ * @param {Error} [err] the original error, if there is one.
+ *
+ * @returns {Error}
+ */
+ function makeError(id, msg, err, requireModules) {
+ var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
+ e.requireType = id;
+ e.requireModules = requireModules;
+ if (err) {
+ e.originalError = err;
+ }
+ return e;
+ }
+
+ if (typeof define !== 'undefined') {
+ //If a define is already in play via another AMD loader,
+ //do not overwrite.
+ return;
+ }
+
+ if (typeof requirejs !== 'undefined') {
+ if (isFunction(requirejs)) {
+ //Do not overwrite and existing requirejs instance.
+ return;
+ }
+ cfg = requirejs;
+ requirejs = undefined;
+ }
+
+ //Allow for a require config object
+ if (typeof require !== 'undefined' && !isFunction(require)) {
+ //assume it is a config object.
+ cfg = require;
+ require = undefined;
+ }
+
+ function newContext(contextName) {
+ var inCheckLoaded, Module, context, handlers,
+ checkLoadedTimeoutId,
+ config = {
+ waitSeconds: 7,
+ baseUrl: './',
+ paths: {},
+ pkgs: {},
+ shim: {}
+ },
+ registry = {},
+ undefEvents = {},
+ defQueue = [],
+ defined = {},
+ urlFetched = {},
+ requireCounter = 1,
+ unnormalizedCounter = 1,
+ //Used to track the order in which modules
+ //should be executed, by the order they
+ //load. Important for consistent cycle resolution
+ //behavior.
+ waitAry = [];
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part;
+ for (i = 0; ary[i]; i += 1) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @param {Boolean} applyMap apply the map config to the value. Should
+ * only be done if this normalization is for a dependency ID.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName, applyMap) {
+ var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
+ foundMap, foundI, foundStarMap, starI,
+ baseParts = baseName && baseName.split('/'),
+ normalizedBaseParts = baseParts,
+ map = config.map,
+ starMap = map && map['*'];
+
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === '.') {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ if (config.pkgs[baseName]) {
+ //If the baseName is a package name, then just treat it as one
+ //name to concat the name with.
+ normalizedBaseParts = baseParts = [baseName];
+ } else {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that 'directory' and not name of the baseName's
+ //module. For instance, baseName of 'one/two/three', maps to
+ //'one/two/three.js', but we want the directory, 'one/two' for
+ //this normalization.
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+ }
+
+ name = normalizedBaseParts.concat(name.split('/'));
+ trimDots(name);
+
+ //Some use of packages may use a . path to reference the
+ //'main' module name, so normalize for that.
+ pkgConfig = config.pkgs[(pkgName = name[0])];
+ name = name.join('/');
+ if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
+ name = pkgName;
+ }
+ } else if (name.indexOf('./') === 0) {
+ // No baseName, so this is ID is resolved relative
+ // to baseUrl, pull off the leading dot.
+ name = name.substring(2);
+ }
+ }
+
+ //Apply map config if available.
+ if (applyMap && (baseParts || starMap) && map) {
+ nameParts = name.split('/');
+
+ for (i = nameParts.length; i > 0; i -= 1) {
+ nameSegment = nameParts.slice(0, i).join('/');
+
+ if (baseParts) {
+ //Find the longest baseName segment match in the config.
+ //So, do joins on the biggest to smallest lengths of baseParts.
+ for (j = baseParts.length; j > 0; j -= 1) {
+ mapValue = map[baseParts.slice(0, j).join('/')];
+
+ //baseName segment has config, find if it has one for
+ //this name.
+ if (mapValue) {
+ mapValue = mapValue[nameSegment];
+ if (mapValue) {
+ //Match, update name to the new value.
+ foundMap = mapValue;
+ foundI = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (foundMap) {
+ break;
+ }
+
+ //Check for a star map match, but just hold on to it,
+ //if there is a shorter segment match later in a matching
+ //config, then favor over this star map.
+ if (!foundStarMap && starMap && starMap[nameSegment]) {
+ foundStarMap = starMap[nameSegment];
+ starI = i;
+ }
+ }
+
+ if (!foundMap && foundStarMap) {
+ foundMap = foundStarMap;
+ foundI = starI;
+ }
+
+ if (foundMap) {
+ nameParts.splice(0, foundI, foundMap);
+ name = nameParts.join('/');
+ }
+ }
+
+ return name;
+ }
+
+ function removeScript(name) {
+ if (isBrowser) {
+ each(scripts(), function (scriptNode) {
+ if (scriptNode.getAttribute('data-requiremodule') === name &&
+ scriptNode.getAttribute('data-requirecontext') === context.contextName) {
+ scriptNode.parentNode.removeChild(scriptNode);
+ return true;
+ }
+ });
+ }
+ }
+
+ function hasPathFallback(id) {
+ var pathConfig = config.paths[id];
+ if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
+ removeScript(id);
+ //Pop off the first array value, since it failed, and
+ //retry
+ pathConfig.shift();
+ context.undef(id);
+ context.require([id]);
+ return true;
+ }
+ }
+
+ /**
+ * Creates a module mapping that includes plugin prefix, module
+ * name, and path. If parentModuleMap is provided it will
+ * also normalize the name via require.normalize()
+ *
+ * @param {String} name the module name
+ * @param {String} [parentModuleMap] parent module map
+ * for the module name, used to resolve relative names.
+ * @param {Boolean} isNormalized: is the ID already normalized.
+ * This is true if this call is done for a define() module ID.
+ * @param {Boolean} applyMap: apply the map config to the ID.
+ * Should only be true if this map is for a dependency.
+ *
+ * @returns {Object}
+ */
+ function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
+ var url, pluginModule, suffix,
+ index = name ? name.indexOf('!') : -1,
+ prefix = null,
+ parentName = parentModuleMap ? parentModuleMap.name : null,
+ originalName = name,
+ isDefine = true,
+ normalizedName = '';
+
+ //If no name, then it means it is a require call, generate an
+ //internal name.
+ if (!name) {
+ isDefine = false;
+ name = '_@r' + (requireCounter += 1);
+ }
+
+ if (index !== -1) {
+ prefix = name.substring(0, index);
+ name = name.substring(index + 1, name.length);
+ }
+
+ if (prefix) {
+ prefix = normalize(prefix, parentName, applyMap);
+ pluginModule = defined[prefix];
+ }
+
+ //Account for relative paths if there is a base name.
+ if (name) {
+ if (prefix) {
+ if (pluginModule && pluginModule.normalize) {
+ //Plugin is loaded, use its normalize method.
+ normalizedName = pluginModule.normalize(name, function (name) {
+ return normalize(name, parentName, applyMap);
+ });
+ } else {
+ normalizedName = normalize(name, parentName, applyMap);
+ }
+ } else {
+ //A regular module.
+ normalizedName = normalize(name, parentName, applyMap);
+ url = context.nameToUrl(normalizedName);
+ }
+ }
+
+ //If the id is a plugin id that cannot be determined if it needs
+ //normalization, stamp it with a unique ID so two matching relative
+ //ids that may conflict can be separate.
+ suffix = prefix && !pluginModule && !isNormalized ?
+ '_unnormalized' + (unnormalizedCounter += 1) :
+ '';
+
+ return {
+ prefix: prefix,
+ name: normalizedName,
+ parentMap: parentModuleMap,
+ unnormalized: !!suffix,
+ url: url,
+ originalName: originalName,
+ isDefine: isDefine,
+ id: (prefix ?
+ prefix + '!' + normalizedName :
+ normalizedName) + suffix
+ };
+ }
+
+ function getModule(depMap) {
+ var id = depMap.id,
+ mod = registry[id];
+
+ if (!mod) {
+ mod = registry[id] = new context.Module(depMap);
+ }
+
+ return mod;
+ }
+
+ function on(depMap, name, fn) {
+ var id = depMap.id,
+ mod = registry[id];
+
+ if (hasProp(defined, id) &&
+ (!mod || mod.defineEmitComplete)) {
+ if (name === 'defined') {
+ fn(defined[id]);
+ }
+ } else {
+ getModule(depMap).on(name, fn);
+ }
+ }
+
+ function onError(err, errback) {
+ var ids = err.requireModules,
+ notified = false;
+
+ if (errback) {
+ errback(err);
+ } else {
+ each(ids, function (id) {
+ var mod = registry[id];
+ if (mod) {
+ //Set error on module, so it skips timeout checks.
+ mod.error = err;
+ if (mod.events.error) {
+ notified = true;
+ mod.emit('error', err);
+ }
+ }
+ });
+
+ if (!notified) {
+ req.onError(err);
+ }
+ }
+ }
+
+ /**
+ * Internal method to transfer globalQueue items to this context's
+ * defQueue.
+ */
+ function takeGlobalQueue() {
+ //Push all the globalDefQueue items into the context's defQueue
+ if (globalDefQueue.length) {
+ //Array splice in the values since the context code has a
+ //local var ref to defQueue, so cannot just reassign the one
+ //on context.
+ apsp.apply(defQueue,
+ [defQueue.length - 1, 0].concat(globalDefQueue));
+ globalDefQueue = [];
+ }
+ }
+
+ /**
+ * Helper function that creates a require function object to give to
+ * modules that ask for it as a dependency. It needs to be specific
+ * per module because of the implication of path mappings that may
+ * need to be relative to the module name.
+ */
+ function makeRequire(mod, enableBuildCallback, altRequire) {
+ var relMap = mod && mod.map,
+ modRequire = makeContextModuleFunc(altRequire || context.require,
+ relMap,
+ enableBuildCallback);
+
+ addRequireMethods(modRequire, context, relMap);
+ modRequire.isBrowser = isBrowser;
+
+ return modRequire;
+ }
+
+ handlers = {
+ 'require': function (mod) {
+ return makeRequire(mod);
+ },
+ 'exports': function (mod) {
+ mod.usingExports = true;
+ if (mod.map.isDefine) {
+ return (mod.exports = defined[mod.map.id] = {});
+ }
+ },
+ 'module': function (mod) {
+ return (mod.module = {
+ id: mod.map.id,
+ uri: mod.map.url,
+ config: function () {
+ return (config.config && config.config[mod.map.id]) || {};
+ },
+ exports: defined[mod.map.id]
+ });
+ }
+ };
+
+ function removeWaiting(id) {
+ //Clean up machinery used for waiting modules.
+ delete registry[id];
+
+ each(waitAry, function (mod, i) {
+ if (mod.map.id === id) {
+ waitAry.splice(i, 1);
+ if (!mod.defined) {
+ context.waitCount -= 1;
+ }
+ return true;
+ }
+ });
+ }
+
+ function findCycle(mod, traced) {
+ var id = mod.map.id,
+ depArray = mod.depMaps,
+ foundModule;
+
+ //Do not bother with unitialized modules or not yet enabled
+ //modules.
+ if (!mod.inited) {
+ return;
+ }
+
+ //Found the cycle.
+ if (traced[id]) {
+ return mod;
+ }
+
+ traced[id] = true;
+
+ //Trace through the dependencies.
+ each(depArray, function (depMap) {
+ var depId = depMap.id,
+ depMod = registry[depId];
+
+ if (!depMod) {
+ return;
+ }
+
+ if (!depMod.inited || !depMod.enabled) {
+ //Dependency is not inited, so this cannot
+ //be used to determine a cycle.
+ foundModule = null;
+ delete traced[id];
+ return true;
+ }
+
+ //mixin traced to a new object for each dependency, so that
+ //sibling dependencies in this object to not generate a
+ //false positive match on a cycle. Ideally an Object.create
+ //type of prototype delegation would be used here, but
+ //optimizing for file size vs. execution speed since hopefully
+ //the trees are small for circular dependency scans relative
+ //to the full app perf.
+ return (foundModule = findCycle(depMod, mixin({}, traced)));
+ });
+
+ return foundModule;
+ }
+
+ function forceExec(mod, traced, uninited) {
+ var id = mod.map.id,
+ depArray = mod.depMaps;
+
+ if (!mod.inited || !mod.map.isDefine) {
+ return;
+ }
+
+ if (traced[id]) {
+ return defined[id];
+ }
+
+ traced[id] = mod;
+
+ each(depArray, function (depMap) {
+ var depId = depMap.id,
+ depMod = registry[depId],
+ value;
+
+ if (handlers[depId]) {
+ return;
+ }
+
+ if (depMod) {
+ if (!depMod.inited || !depMod.enabled) {
+ //Dependency is not inited,
+ //so this module cannot be
+ //given a forced value yet.
+ uninited[id] = true;
+ return;
+ }
+
+ //Get the value for the current dependency
+ value = forceExec(depMod, traced, uninited);
+
+ //Even with forcing it may not be done,
+ //in particular if the module is waiting
+ //on a plugin resource.
+ if (!uninited[depId]) {
+ mod.defineDepById(depId, value);
+ }
+ }
+ });
+
+ mod.check(true);
+
+ return defined[id];
+ }
+
+ function modCheck(mod) {
+ mod.check();
+ }
+
+ function checkLoaded() {
+ var map, modId, err, usingPathFallback,
+ waitInterval = config.waitSeconds * 1000,
+ //It is possible to disable the wait interval by using waitSeconds of 0.
+ expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
+ noLoads = [],
+ stillLoading = false,
+ needCycleCheck = true;
+
+ //Do not bother if this call was a result of a cycle break.
+ if (inCheckLoaded) {
+ return;
+ }
+
+ inCheckLoaded = true;
+
+ //Figure out the state of all the modules.
+ eachProp(registry, function (mod) {
+ map = mod.map;
+ modId = map.id;
+
+ //Skip things that are not enabled or in error state.
+ if (!mod.enabled) {
+ return;
+ }
+
+ if (!mod.error) {
+ //If the module should be executed, and it has not
+ //been inited and time is up, remember it.
+ if (!mod.inited && expired) {
+ if (hasPathFallback(modId)) {
+ usingPathFallback = true;
+ stillLoading = true;
+ } else {
+ noLoads.push(modId);
+ removeScript(modId);
+ }
+ } else if (!mod.inited && mod.fetched && map.isDefine) {
+ stillLoading = true;
+ if (!map.prefix) {
+ //No reason to keep looking for unfinished
+ //loading. If the only stillLoading is a
+ //plugin resource though, keep going,
+ //because it may be that a plugin resource
+ //is waiting on a non-plugin cycle.
+ return (needCycleCheck = false);
+ }
+ }
+ }
+ });
+
+ if (expired && noLoads.length) {
+ //If wait time expired, throw error of unloaded modules.
+ err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
+ err.contextName = context.contextName;
+ return onError(err);
+ }
+
+ //Not expired, check for a cycle.
+ if (needCycleCheck) {
+
+ each(waitAry, function (mod) {
+ if (mod.defined) {
+ return;
+ }
+
+ var cycleMod = findCycle(mod, {}),
+ traced = {};
+
+ if (cycleMod) {
+ forceExec(cycleMod, traced, {});
+
+ //traced modules may have been
+ //removed from the registry, but
+ //their listeners still need to
+ //be called.
+ eachProp(traced, modCheck);
+ }
+ });
+
+ //Now that dependencies have
+ //been satisfied, trigger the
+ //completion check that then
+ //notifies listeners.
+ eachProp(registry, modCheck);
+ }
+
+ //If still waiting on loads, and the waiting load is something
+ //other than a plugin resource, or there are still outstanding
+ //scripts, then just try back later.
+ if ((!expired || usingPathFallback) && stillLoading) {
+ //Something is still waiting to load. Wait for it, but only
+ //if a timeout is not already in effect.
+ if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
+ checkLoadedTimeoutId = setTimeout(function () {
+ checkLoadedTimeoutId = 0;
+ checkLoaded();
+ }, 50);
+ }
+ }
+
+ inCheckLoaded = false;
+ }
+
+ Module = function (map) {
+ this.events = undefEvents[map.id] || {};
+ this.map = map;
+ this.shim = config.shim[map.id];
+ this.depExports = [];
+ this.depMaps = [];
+ this.depMatched = [];
+ this.pluginMaps = {};
+ this.depCount = 0;
+
+ /* this.exports this.factory
+ this.depMaps = [],
+ this.enabled, this.fetched
+ */
+ };
+
+ Module.prototype = {
+ init: function (depMaps, factory, errback, options) {
+ options = options || {};
+
+ //Do not do more inits if already done. Can happen if there
+ //are multiple define calls for the same module. That is not
+ //a normal, common case, but it is also not unexpected.
+ if (this.inited) {
+ return;
+ }
+
+ this.factory = factory;
+
+ if (errback) {
+ //Register for errors on this module.
+ this.on('error', errback);
+ } else if (this.events.error) {
+ //If no errback already, but there are error listeners
+ //on this module, set up an errback to pass to the deps.
+ errback = bind(this, function (err) {
+ this.emit('error', err);
+ });
+ }
+
+ //Do a copy of the dependency array, so that
+ //source inputs are not modified. For example
+ //"shim" deps are passed in here directly, and
+ //doing a direct modification of the depMaps array
+ //would affect that config.
+ this.depMaps = depMaps && depMaps.slice(0);
+ this.depMaps.rjsSkipMap = depMaps.rjsSkipMap;
+
+ this.errback = errback;
+
+ //Indicate this module has be initialized
+ this.inited = true;
+
+ this.ignore = options.ignore;
+
+ //Could have option to init this module in enabled mode,
+ //or could have been previously marked as enabled. However,
+ //the dependencies are not known until init is called. So
+ //if enabled previously, now trigger dependencies as enabled.
+ if (options.enabled || this.enabled) {
+ //Enable this module and dependencies.
+ //Will call this.check()
+ this.enable();
+ } else {
+ this.check();
+ }
+ },
+
+ defineDepById: function (id, depExports) {
+ var i;
+
+ //Find the index for this dependency.
+ each(this.depMaps, function (map, index) {
+ if (map.id === id) {
+ i = index;
+ return true;
+ }
+ });
+
+ return this.defineDep(i, depExports);
+ },
+
+ defineDep: function (i, depExports) {
+ //Because of cycles, defined callback for a given
+ //export can be called more than once.
+ if (!this.depMatched[i]) {
+ this.depMatched[i] = true;
+ this.depCount -= 1;
+ this.depExports[i] = depExports;
+ }
+ },
+
+ fetch: function () {
+ if (this.fetched) {
+ return;
+ }
+ this.fetched = true;
+
+ context.startTime = (new Date()).getTime();
+
+ var map = this.map;
+
+ //If the manager is for a plugin managed resource,
+ //ask the plugin to load it now.
+ if (this.shim) {
+ makeRequire(this, true)(this.shim.deps || [], bind(this, function () {
+ return map.prefix ? this.callPlugin() : this.load();
+ }));
+ } else {
+ //Regular dependency.
+ return map.prefix ? this.callPlugin() : this.load();
+ }
+ },
+
+ load: function () {
+ var url = this.map.url;
+
+ //Regular dependency.
+ if (!urlFetched[url]) {
+ urlFetched[url] = true;
+ context.load(this.map.id, url);
+ }
+ },
+
+ /**
+ * Checks is the module is ready to define itself, and if so,
+ * define it. If the silent argument is true, then it will just
+ * define, but not notify listeners, and not ask for a context-wide
+ * check of all loaded modules. That is useful for cycle breaking.
+ */
+ check: function (silent) {
+ if (!this.enabled || this.enabling) {
+ return;
+ }
+
+ var err, cjsModule,
+ id = this.map.id,
+ depExports = this.depExports,
+ exports = this.exports,
+ factory = this.factory;
+
+ if (!this.inited) {
+ this.fetch();
+ } else if (this.error) {
+ this.emit('error', this.error);
+ } else if (!this.defining) {
+ //The factory could trigger another require call
+ //that would result in checking this module to
+ //define itself again. If already in the process
+ //of doing that, skip this work.
+ this.defining = true;
+
+ if (this.depCount < 1 && !this.defined) {
+ if (isFunction(factory)) {
+ //If there is an error listener, favor passing
+ //to that instead of throwing an error.
+ if (this.events.error) {
+ try {
+ exports = context.execCb(id, factory, depExports, exports);
+ } catch (e) {
+ err = e;
+ }
+ } else {
+ exports = context.execCb(id, factory, depExports, exports);
+ }
+
+ if (this.map.isDefine) {
+ //If setting exports via 'module' is in play,
+ //favor that over return value and exports. After that,
+ //favor a non-undefined return value over exports use.
+ cjsModule = this.module;
+ if (cjsModule &&
+ cjsModule.exports !== undefined &&
+ //Make sure it is not already the exports value
+ cjsModule.exports !== this.exports) {
+ exports = cjsModule.exports;
+ } else if (exports === undefined && this.usingExports) {
+ //exports already set the defined value.
+ exports = this.exports;
+ }
+ }
+
+ if (err) {
+ err.requireMap = this.map;
+ err.requireModules = [this.map.id];
+ err.requireType = 'define';
+ return onError((this.error = err));
+ }
+
+ } else {
+ //Just a literal value
+ exports = factory;
+ }
+
+ this.exports = exports;
+
+ if (this.map.isDefine && !this.ignore) {
+ defined[id] = exports;
+
+ if (req.onResourceLoad) {
+ req.onResourceLoad(context, this.map, this.depMaps);
+ }
+ }
+
+ //Clean up
+ delete registry[id];
+
+ this.defined = true;
+ context.waitCount -= 1;
+ if (context.waitCount === 0) {
+ //Clear the wait array used for cycles.
+ waitAry = [];
+ }
+ }
+
+ //Finished the define stage. Allow calling check again
+ //to allow define notifications below in the case of a
+ //cycle.
+ this.defining = false;
+
+ if (!silent) {
+ if (this.defined && !this.defineEmitted) {
+ this.defineEmitted = true;
+ this.emit('defined', this.exports);
+ this.defineEmitComplete = true;
+ }
+ }
+ }
+ },
+
+ callPlugin: function () {
+ var map = this.map,
+ id = map.id,
+ pluginMap = makeModuleMap(map.prefix, null, false, true);
+
+ on(pluginMap, 'defined', bind(this, function (plugin) {
+ var load, normalizedMap, normalizedMod,
+ name = this.map.name,
+ parentName = this.map.parentMap ? this.map.parentMap.name : null;
+
+ //If current map is not normalized, wait for that
+ //normalized name to load instead of continuing.
+ if (this.map.unnormalized) {
+ //Normalize the ID if the plugin allows it.
+ if (plugin.normalize) {
+ name = plugin.normalize(name, function (name) {
+ return normalize(name, parentName, true);
+ }) || '';
+ }
+
+ normalizedMap = makeModuleMap(map.prefix + '!' + name,
+ this.map.parentMap,
+ false,
+ true);
+ on(normalizedMap,
+ 'defined', bind(this, function (value) {
+ this.init([], function () { return value; }, null, {
+ enabled: true,
+ ignore: true
+ });
+ }));
+ normalizedMod = registry[normalizedMap.id];
+ if (normalizedMod) {
+ if (this.events.error) {
+ normalizedMod.on('error', bind(this, function (err) {
+ this.emit('error', err);
+ }));
+ }
+ normalizedMod.enable();
+ }
+
+ return;
+ }
+
+ load = bind(this, function (value) {
+ this.init([], function () { return value; }, null, {
+ enabled: true
+ });
+ });
+
+ load.error = bind(this, function (err) {
+ this.inited = true;
+ this.error = err;
+ err.requireModules = [id];
+
+ //Remove temp unnormalized modules for this module,
+ //since they will never be resolved otherwise now.
+ eachProp(registry, function (mod) {
+ if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
+ removeWaiting(mod.map.id);
+ }
+ });
+
+ onError(err);
+ });
+
+ //Allow plugins to load other code without having to know the
+ //context or how to 'complete' the load.
+ load.fromText = function (moduleName, text) {
+ /*jslint evil: true */
+ var hasInteractive = useInteractive;
+
+ //Turn off interactive script matching for IE for any define
+ //calls in the text, then turn it back on at the end.
+ if (hasInteractive) {
+ useInteractive = false;
+ }
+
+ //Prime the system by creating a module instance for
+ //it.
+ getModule(makeModuleMap(moduleName));
+
+ req.exec(text);
+
+ if (hasInteractive) {
+ useInteractive = true;
+ }
+
+ //Support anonymous modules.
+ context.completeLoad(moduleName);
+ };
+
+ //Use parentName here since the plugin's name is not reliable,
+ //could be some weird string with no path that actually wants to
+ //reference the parentName's path.
+ plugin.load(map.name, makeRequire(map.parentMap, true, function (deps, cb, er) {
+ deps.rjsSkipMap = true;
+ return context.require(deps, cb, er);
+ }), load, config);
+ }));
+
+ context.enable(pluginMap, this);
+ this.pluginMaps[pluginMap.id] = pluginMap;
+ },
+
+ enable: function () {
+ this.enabled = true;
+
+ if (!this.waitPushed) {
+ waitAry.push(this);
+ context.waitCount += 1;
+ this.waitPushed = true;
+ }
+
+ //Set flag mentioning that the module is enabling,
+ //so that immediate calls to the defined callbacks
+ //for dependencies do not trigger inadvertent load
+ //with the depCount still being zero.
+ this.enabling = true;
+
+ //Enable each dependency
+ each(this.depMaps, bind(this, function (depMap, i) {
+ var id, mod, handler;
+
+ if (typeof depMap === 'string') {
+ //Dependency needs to be converted to a depMap
+ //and wired up to this module.
+ depMap = makeModuleMap(depMap,
+ (this.map.isDefine ? this.map : this.map.parentMap),
+ false,
+ !this.depMaps.rjsSkipMap);
+ this.depMaps[i] = depMap;
+
+ handler = handlers[depMap.id];
+
+ if (handler) {
+ this.depExports[i] = handler(this);
+ return;
+ }
+
+ this.depCount += 1;
+
+ on(depMap, 'defined', bind(this, function (depExports) {
+ this.defineDep(i, depExports);
+ this.check();
+ }));
+
+ if (this.errback) {
+ on(depMap, 'error', this.errback);
+ }
+ }
+
+ id = depMap.id;
+ mod = registry[id];
+
+ //Skip special modules like 'require', 'exports', 'module'
+ //Also, don't call enable if it is already enabled,
+ //important in circular dependency cases.
+ if (!handlers[id] && mod && !mod.enabled) {
+ context.enable(depMap, this);
+ }
+ }));
+
+ //Enable each plugin that is used in
+ //a dependency
+ eachProp(this.pluginMaps, bind(this, function (pluginMap) {
+ var mod = registry[pluginMap.id];
+ if (mod && !mod.enabled) {
+ context.enable(pluginMap, this);
+ }
+ }));
+
+ this.enabling = false;
+
+ this.check();
+ },
+
+ on: function (name, cb) {
+ var cbs = this.events[name];
+ if (!cbs) {
+ cbs = this.events[name] = [];
+ }
+ cbs.push(cb);
+ },
+
+ emit: function (name, evt) {
+ each(this.events[name], function (cb) {
+ cb(evt);
+ });
+ if (name === 'error') {
+ //Now that the error handler was triggered, remove
+ //the listeners, since this broken Module instance
+ //can stay around for a while in the registry/waitAry.
+ delete this.events[name];
+ }
+ }
+ };
+
+ function callGetModule(args) {
+ getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
+ }
+
+ function removeListener(node, func, name, ieName) {
+ //Favor detachEvent because of IE9
+ //issue, see attachEvent/addEventListener comment elsewhere
+ //in this file.
+ if (node.detachEvent && !isOpera) {
+ //Probably IE. If not it will throw an error, which will be
+ //useful to know.
+ if (ieName) {
+ node.detachEvent(ieName, func);
+ }
+ } else {
+ node.removeEventListener(name, func, false);
+ }
+ }
+
+ /**
+ * Given an event from a script node, get the requirejs info from it,
+ * and then removes the event listeners on the node.
+ * @param {Event} evt
+ * @returns {Object}
+ */
+ function getScriptData(evt) {
+ //Using currentTarget instead of target for Firefox 2.0's sake. Not
+ //all old browsers will be supported, but this one was easy enough
+ //to support and still makes sense.
+ var node = evt.currentTarget || evt.srcElement;
+
+ //Remove the listeners once here.
+ removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
+ removeListener(node, context.onScriptError, 'error');
+
+ return {
+ node: node,
+ id: node && node.getAttribute('data-requiremodule')
+ };
+ }
+
+ return (context = {
+ config: config,
+ contextName: contextName,
+ registry: registry,
+ defined: defined,
+ urlFetched: urlFetched,
+ waitCount: 0,
+ defQueue: defQueue,
+ Module: Module,
+ makeModuleMap: makeModuleMap,
+
+ /**
+ * Set a configuration for the context.
+ * @param {Object} cfg config object to integrate.
+ */
+ configure: function (cfg) {
+ //Make sure the baseUrl ends in a slash.
+ if (cfg.baseUrl) {
+ if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
+ cfg.baseUrl += '/';
+ }
+ }
+
+ //Save off the paths and packages since they require special processing,
+ //they are additive.
+ var pkgs = config.pkgs,
+ shim = config.shim,
+ paths = config.paths,
+ map = config.map;
+
+ //Mix in the config values, favoring the new values over
+ //existing ones in context.config.
+ mixin(config, cfg, true);
+
+ //Merge paths.
+ config.paths = mixin(paths, cfg.paths, true);
+
+ //Merge map
+ if (cfg.map) {
+ config.map = mixin(map || {}, cfg.map, true, true);
+ }
+
+ //Merge shim
+ if (cfg.shim) {
+ eachProp(cfg.shim, function (value, id) {
+ //Normalize the structure
+ if (isArray(value)) {
+ value = {
+ deps: value
+ };
+ }
+ if (value.exports && !value.exports.__buildReady) {
+ value.exports = context.makeShimExports(value.exports);
+ }
+ shim[id] = value;
+ });
+ config.shim = shim;
+ }
+
+ //Adjust packages if necessary.
+ if (cfg.packages) {
+ each(cfg.packages, function (pkgObj) {
+ var location;
+
+ pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
+ location = pkgObj.location;
+
+ //Create a brand new object on pkgs, since currentPackages can
+ //be passed in again, and config.pkgs is the internal transformed
+ //state for all package configs.
+ pkgs[pkgObj.name] = {
+ name: pkgObj.name,
+ location: location || pkgObj.name,
+ //Remove leading dot in main, so main paths are normalized,
+ //and remove any trailing .js, since different package
+ //envs have different conventions: some use a module name,
+ //some use a file name.
+ main: (pkgObj.main || 'main')
+ .replace(currDirRegExp, '')
+ .replace(jsSuffixRegExp, '')
+ };
+ });
+
+ //Done with modifications, assing packages back to context config
+ config.pkgs = pkgs;
+ }
+
+ //If there are any "waiting to execute" modules in the registry,
+ //update the maps for them, since their info, like URLs to load,
+ //may have changed.
+ eachProp(registry, function (mod, id) {
+ //If module already has init called, since it is too
+ //late to modify them, and ignore unnormalized ones
+ //since they are transient.
+ if (!mod.inited && !mod.map.unnormalized) {
+ mod.map = makeModuleMap(id);
+ }
+ });
+
+ //If a deps array or a config callback is specified, then call
+ //require with those args. This is useful when require is defined as a
+ //config object before require.js is loaded.
+ if (cfg.deps || cfg.callback) {
+ context.require(cfg.deps || [], cfg.callback);
+ }
+ },
+
+ makeShimExports: function (exports) {
+ var func;
+ if (typeof exports === 'string') {
+ func = function () {
+ return getGlobal(exports);
+ };
+ //Save the exports for use in nodefine checking.
+ func.exports = exports;
+ return func;
+ } else {
+ return function () {
+ return exports.apply(global, arguments);
+ };
+ }
+ },
+
+ requireDefined: function (id, relMap) {
+ return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
+ },
+
+ requireSpecified: function (id, relMap) {
+ id = makeModuleMap(id, relMap, false, true).id;
+ return hasProp(defined, id) || hasProp(registry, id);
+ },
+
+ require: function (deps, callback, errback, relMap) {
+ var moduleName, id, map, requireMod, args;
+ if (typeof deps === 'string') {
+ if (isFunction(callback)) {
+ //Invalid call
+ return onError(makeError('requireargs', 'Invalid require call'), errback);
+ }
+
+ //Synchronous access to one module. If require.get is
+ //available (as in the Node adapter), prefer that.
+ //In this case deps is the moduleName and callback is
+ //the relMap
+ if (req.get) {
+ return req.get(context, deps, callback);
+ }
+
+ //Just return the module wanted. In this scenario, the
+ //second arg (if passed) is just the relMap.
+ moduleName = deps;
+ relMap = callback;
+
+ //Normalize module name, if it contains . or ..
+ map = makeModuleMap(moduleName, relMap, false, true);
+ id = map.id;
+
+ if (!hasProp(defined, id)) {
+ return onError(makeError('notloaded', 'Module name "' +
+ id +
+ '" has not been loaded yet for context: ' +
+ contextName));
+ }
+ return defined[id];
+ }
+
+ //Callback require. Normalize args. if callback or errback is
+ //not a function, it means it is a relMap. Test errback first.
+ if (errback && !isFunction(errback)) {
+ relMap = errback;
+ errback = undefined;
+ }
+ if (callback && !isFunction(callback)) {
+ relMap = callback;
+ callback = undefined;
+ }
+
+ //Any defined modules in the global queue, intake them now.
+ takeGlobalQueue();
+
+ //Make sure any remaining defQueue items get properly processed.
+ while (defQueue.length) {
+ args = defQueue.shift();
+ if (args[0] === null) {
+ return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
+ } else {
+ //args are id, deps, factory. Should be normalized by the
+ //define() function.
+ callGetModule(args);
+ }
+ }
+
+ //Mark all the dependencies as needing to be loaded.
+ requireMod = getModule(makeModuleMap(null, relMap));
+
+ requireMod.init(deps, callback, errback, {
+ enabled: true
+ });
+
+ checkLoaded();
+
+ return context.require;
+ },
+
+ undef: function (id) {
+ //Bind any waiting define() calls to this context,
+ //fix for #408
+ takeGlobalQueue();
+
+ var map = makeModuleMap(id, null, true),
+ mod = registry[id];
+
+ delete defined[id];
+ delete urlFetched[map.url];
+ delete undefEvents[id];
+
+ if (mod) {
+ //Hold on to listeners in case the
+ //module will be attempted to be reloaded
+ //using a different config.
+ if (mod.events.defined) {
+ undefEvents[id] = mod.events;
+ }
+
+ removeWaiting(id);
+ }
+ },
+
+ /**
+ * Called to enable a module if it is still in the registry
+ * awaiting enablement. parent module is passed in for context,
+ * used by the optimizer.
+ */
+ enable: function (depMap, parent) {
+ var mod = registry[depMap.id];
+ if (mod) {
+ getModule(depMap).enable();
+ }
+ },
+
+ /**
+ * Internal method used by environment adapters to complete a load event.
+ * A load event could be a script load or just a load pass from a synchronous
+ * load call.
+ * @param {String} moduleName the name of the module to potentially complete.
+ */
+ completeLoad: function (moduleName) {
+ var found, args, mod,
+ shim = config.shim[moduleName] || {},
+ shExports = shim.exports && shim.exports.exports;
+
+ takeGlobalQueue();
+
+ while (defQueue.length) {
+ args = defQueue.shift();
+ if (args[0] === null) {
+ args[0] = moduleName;
+ //If already found an anonymous module and bound it
+ //to this name, then this is some other anon module
+ //waiting for its completeLoad to fire.
+ if (found) {
+ break;
+ }
+ found = true;
+ } else if (args[0] === moduleName) {
+ //Found matching define call for this script!
+ found = true;
+ }
+
+ callGetModule(args);
+ }
+
+ //Do this after the cycle of callGetModule in case the result
+ //of those calls/init calls changes the registry.
+ mod = registry[moduleName];
+
+ if (!found && !defined[moduleName] && mod && !mod.inited) {
+ if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
+ if (hasPathFallback(moduleName)) {
+ return;
+ } else {
+ return onError(makeError('nodefine',
+ 'No define call for ' + moduleName,
+ null,
+ [moduleName]));
+ }
+ } else {
+ //A script that does not call define(), so just simulate
+ //the call for it.
+ callGetModule([moduleName, (shim.deps || []), shim.exports]);
+ }
+ }
+
+ checkLoaded();
+ },
+
+ /**
+ * Converts a module name + .extension into an URL path.
+ * *Requires* the use of a module name. It does not support using
+ * plain URLs like nameToUrl.
+ */
+ toUrl: function (moduleNamePlusExt, relModuleMap) {
+ var index = moduleNamePlusExt.lastIndexOf('.'),
+ ext = null;
+
+ if (index !== -1) {
+ ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
+ moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
+ }
+
+ return context.nameToUrl(normalize(moduleNamePlusExt, relModuleMap && relModuleMap.id, true),
+ ext);
+ },
+
+ /**
+ * Converts a module name to a file path. Supports cases where
+ * moduleName may actually be just an URL.
+ * Note that it **does not** call normalize on the moduleName,
+ * it is assumed to have already been normalized. This is an
+ * internal API, not a public one. Use toUrl for the public API.
+ */
+ nameToUrl: function (moduleName, ext) {
+ var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
+ parentPath;
+
+ //If a colon is in the URL, it indicates a protocol is used and it is just
+ //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
+ //or ends with .js, then assume the user meant to use an url and not a module id.
+ //The slash is important for protocol-less URLs as well as full paths.
+ if (req.jsExtRegExp.test(moduleName)) {
+ //Just a plain path, not module name lookup, so just return it.
+ //Add extension if it is included. This is a bit wonky, only non-.js things pass
+ //an extension, this method probably needs to be reworked.
+ url = moduleName + (ext || '');
+ } else {
+ //A module that needs to be converted to a path.
+ paths = config.paths;
+ pkgs = config.pkgs;
+
+ syms = moduleName.split('/');
+ //For each module name segment, see if there is a path
+ //registered for it. Start with most specific name
+ //and work up from it.
+ for (i = syms.length; i > 0; i -= 1) {
+ parentModule = syms.slice(0, i).join('/');
+ pkg = pkgs[parentModule];
+ parentPath = paths[parentModule];
+ if (parentPath) {
+ //If an array, it means there are a few choices,
+ //Choose the one that is desired
+ if (isArray(parentPath)) {
+ parentPath = parentPath[0];
+ }
+ syms.splice(0, i, parentPath);
+ break;
+ } else if (pkg) {
+ //If module name is just the package name, then looking
+ //for the main module.
+ if (moduleName === pkg.name) {
+ pkgPath = pkg.location + '/' + pkg.main;
+ } else {
+ pkgPath = pkg.location;
+ }
+ syms.splice(0, i, pkgPath);
+ break;
+ }
+ }
+
+ //Join the path parts together, then figure out if baseUrl is needed.
+ url = syms.join('/');
+ url += (ext || (/\?/.test(url) ? '' : '.js'));
+ url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
+ }
+
+ return config.urlArgs ? url +
+ ((url.indexOf('?') === -1 ? '?' : '&') +
+ config.urlArgs) : url;
+ },
+
+ //Delegates to req.load. Broken out as a separate function to
+ //allow overriding in the optimizer.
+ load: function (id, url) {
+ req.load(context, id, url);
+ },
+
+ /**
+ * Executes a module callack function. Broken out as a separate function
+ * solely to allow the build system to sequence the files in the built
+ * layer in the right sequence.
+ *
+ * @private
+ */
+ execCb: function (name, callback, args, exports) {
+ return callback.apply(exports, args);
+ },
+
+ /**
+ * callback for script loads, used to check status of loading.
+ *
+ * @param {Event} evt the event from the browser for the script
+ * that was loaded.
+ */
+ onScriptLoad: function (evt) {
+ //Using currentTarget instead of target for Firefox 2.0's sake. Not
+ //all old browsers will be supported, but this one was easy enough
+ //to support and still makes sense.
+ if (evt.type === 'load' ||
+ (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
+ //Reset interactive script so a script node is not held onto for
+ //to long.
+ interactiveScript = null;
+
+ //Pull out the name of the module and the context.
+ var data = getScriptData(evt);
+ context.completeLoad(data.id);
+ }
+ },
+
+ /**
+ * Callback for script errors.
+ */
+ onScriptError: function (evt) {
+ var data = getScriptData(evt);
+ if (!hasPathFallback(data.id)) {
+ return onError(makeError('scripterror', 'Script error', evt, [data.id]));
+ }
+ }
+ });
+ }
+
+ /**
+ * Main entry point.
+ *
+ * If the only argument to require is a string, then the module that
+ * is represented by that string is fetched for the appropriate context.
+ *
+ * If the first argument is an array, then it will be treated as an array
+ * of dependency string names to fetch. An optional function callback can
+ * be specified to execute when all of those dependencies are available.
+ *
+ * Make a local req variable to help Caja compliance (it assumes things
+ * on a require that are not standardized), and to give a short
+ * name for minification/local scope use.
+ */
+ req = requirejs = function (deps, callback, errback, optional) {
+
+ //Find the right context, use default
+ var context, config,
+ contextName = defContextName;
+
+ // Determine if have config object in the call.
+ if (!isArray(deps) && typeof deps !== 'string') {
+ // deps is a config object
+ config = deps;
+ if (isArray(callback)) {
+ // Adjust args if there are dependencies
+ deps = callback;
+ callback = errback;
+ errback = optional;
+ } else {
+ deps = [];
+ }
+ }
+
+ if (config && config.context) {
+ contextName = config.context;
+ }
+
+ context = contexts[contextName];
+ if (!context) {
+ context = contexts[contextName] = req.s.newContext(contextName);
+ }
+
+ if (config) {
+ context.configure(config);
+ }
+
+ return context.require(deps, callback, errback);
+ };
+
+ /**
+ * Support require.config() to make it easier to cooperate with other
+ * AMD loaders on globally agreed names.
+ */
+ req.config = function (config) {
+ return req(config);
+ };
+
+ /**
+ * Export require as a global, but only if it does not already exist.
+ */
+ if (!require) {
+ require = req;
+ }
+
+ req.version = version;
+
+ //Used to filter out dependencies that are already paths.
+ req.jsExtRegExp = /^\/|:|\?|\.js$/;
+ req.isBrowser = isBrowser;
+ s = req.s = {
+ contexts: contexts,
+ newContext: newContext
+ };
+
+ //Create default context.
+ req({});
+
+ //Exports some context-sensitive methods on global require, using
+ //default context if no context specified.
+ addRequireMethods(req);
+
+ if (isBrowser) {
+ head = s.head = document.getElementsByTagName('head')[0];
+ //If BASE tag is in play, using appendChild is a problem for IE6.
+ //When that browser dies, this can be removed. Details in this jQuery bug:
+ //http://dev.jquery.com/ticket/2709
+ baseElement = document.getElementsByTagName('base')[0];
+ if (baseElement) {
+ head = s.head = baseElement.parentNode;
+ }
+ }
+
+ /**
+ * Any errors that require explicitly generates will be passed to this
+ * function. Intercept/override it if you want custom error handling.
+ * @param {Error} err the error object.
+ */
+ req.onError = function (err) {
+ throw err;
+ };
+
+ /**
+ * Does the request to load a module for the browser case.
+ * Make this a separate function to allow other environments
+ * to override it.
+ *
+ * @param {Object} context the require context to find state.
+ * @param {String} moduleName the name of the module.
+ * @param {Object} url the URL to the module.
+ */
+ req.load = function (context, moduleName, url) {
+ var config = (context && context.config) || {},
+ node;
+ if (isBrowser) {
+ //In the browser so use a script tag
+ node = config.xhtml ?
+ document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
+ document.createElement('script');
+ node.type = config.scriptType || 'text/javascript';
+ node.charset = 'utf-8';
+ node.async = true;
+
+ node.setAttribute('data-requirecontext', context.contextName);
+ node.setAttribute('data-requiremodule', moduleName);
+
+ //Set up load listener. Test attachEvent first because IE9 has
+ //a subtle issue in its addEventListener and script onload firings
+ //that do not match the behavior of all other browsers with
+ //addEventListener support, which fire the onload event for a
+ //script right after the script execution. See:
+ //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
+ //UNFORTUNATELY Opera implements attachEvent but does not follow the script
+ //script execution mode.
+ if (node.attachEvent &&
+ //Check if node.attachEvent is artificially added by custom script or
+ //natively supported by browser
+ //read https://github.com/jrburke/requirejs/issues/187
+ //if we can NOT find [native code] then it must NOT natively supported.
+ //in IE8, node.attachEvent does not have toString()
+ //Note the test for "[native code" with no closing brace, see:
+ //https://github.com/jrburke/requirejs/issues/273
+ !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
+ !isOpera) {
+ //Probably IE. IE (at least 6-8) do not fire
+ //script onload right after executing the script, so
+ //we cannot tie the anonymous define call to a name.
+ //However, IE reports the script as being in 'interactive'
+ //readyState at the time of the define call.
+ useInteractive = true;
+
+ node.attachEvent('onreadystatechange', context.onScriptLoad);
+ //It would be great to add an error handler here to catch
+ //404s in IE9+. However, onreadystatechange will fire before
+ //the error handler, so that does not help. If addEvenListener
+ //is used, then IE will fire error before load, but we cannot
+ //use that pathway given the connect.microsoft.com issue
+ //mentioned above about not doing the 'script execute,
+ //then fire the script load event listener before execute
+ //next script' that other browsers do.
+ //Best hope: IE10 fixes the issues,
+ //and then destroys all installs of IE 6-9.
+ //node.attachEvent('onerror', context.onScriptError);
+ } else {
+ node.addEventListener('load', context.onScriptLoad, false);
+ node.addEventListener('error', context.onScriptError, false);
+ }
+ node.src = url;
+
+ //For some cache cases in IE 6-8, the script executes before the end
+ //of the appendChild execution, so to tie an anonymous define
+ //call to the module name (which is stored on the node), hold on
+ //to a reference to this node, but clear after the DOM insertion.
+ currentlyAddingScript = node;
+ if (baseElement) {
+ head.insertBefore(node, baseElement);
+ } else {
+ head.appendChild(node);
+ }
+ currentlyAddingScript = null;
+
+ return node;
+ } else if (isWebWorker) {
+ //In a web worker, use importScripts. This is not a very
+ //efficient use of importScripts, importScripts will block until
+ //its script is downloaded and evaluated. However, if web workers
+ //are in play, the expectation that a build has been done so that
+ //only one script needs to be loaded anyway. This may need to be
+ //reevaluated if other use cases become common.
+ importScripts(url);
+
+ //Account for anonymous modules
+ context.completeLoad(moduleName);
+ }
+ };
+
+ function getInteractiveScript() {
+ if (interactiveScript && interactiveScript.readyState === 'interactive') {
+ return interactiveScript;
+ }
+
+ eachReverse(scripts(), function (script) {
+ if (script.readyState === 'interactive') {
+ return (interactiveScript = script);
+ }
+ });
+ return interactiveScript;
+ }
+
+ //Look for a data-main script attribute, which could also adjust the baseUrl.
+ if (isBrowser) {
+ //Figure out baseUrl. Get it from the script tag with require.js in it.
+ eachReverse(scripts(), function (script) {
+ //Set the 'head' where we can append children by
+ //using the script's parent.
+ if (!head) {
+ head = script.parentNode;
+ }
+
+ //Look for a data-main attribute to set main script for the page
+ //to load. If it is there, the path to data main becomes the
+ //baseUrl, if it is not already set.
+ dataMain = script.getAttribute('data-main');
+ if (dataMain) {
+ //Set final baseUrl if there is not already an explicit one.
+ if (!cfg.baseUrl) {
+ //Pull off the directory of data-main for use as the
+ //baseUrl.
+ src = dataMain.split('/');
+ mainScript = src.pop();
+ subPath = src.length ? src.join('/') + '/' : './';
+
+ cfg.baseUrl = subPath;
+ dataMain = mainScript;
+ }
+
+ //Strip off any trailing .js since dataMain is now
+ //like a module name.
+ dataMain = dataMain.replace(jsSuffixRegExp, '');
+
+ //Put the data-main script in the files to load.
+ cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
+
+ return true;
+ }
+ });
+ }
+
+ /**
+ * The function that handles definitions of modules. Differs from
+ * require() in that a string for the module should be the first argument,
+ * and the function to execute after dependencies are loaded should
+ * return a value to define the module corresponding to the first argument's
+ * name.
+ */
+ define = function (name, deps, callback) {
+ var node, context;
+
+ //Allow for anonymous functions
+ if (typeof name !== 'string') {
+ //Adjust args appropriately
+ callback = deps;
+ deps = name;
+ name = null;
+ }
+
+ //This module may not have dependencies
+ if (!isArray(deps)) {
+ callback = deps;
+ deps = [];
+ }
+
+ //If no name, and callback is a function, then figure out if it a
+ //CommonJS thing with dependencies.
+ if (!deps.length && isFunction(callback)) {
+ //Remove comments from the callback string,
+ //look for require calls, and pull them into the dependencies,
+ //but only if there are function args.
+ if (callback.length) {
+ callback
+ .toString()
+ .replace(commentRegExp, '')
+ .replace(cjsRequireRegExp, function (match, dep) {
+ deps.push(dep);
+ });
+
+ //May be a CommonJS thing even without require calls, but still
+ //could use exports, and module. Avoid doing exports and module
+ //work though if it just needs require.
+ //REQUIRES the function to expect the CommonJS variables in the
+ //order listed below.
+ deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
+ }
+ }
+
+ //If in IE 6-8 and hit an anonymous define() call, do the interactive
+ //work.
+ if (useInteractive) {
+ node = currentlyAddingScript || getInteractiveScript();
+ if (node) {
+ if (!name) {
+ name = node.getAttribute('data-requiremodule');
+ }
+ context = contexts[node.getAttribute('data-requirecontext')];
+ }
+ }
+
+ //Always save off evaluating the def call until the script onload handler.
+ //This allows multiple modules to be in a file without prematurely
+ //tracing dependencies, and allows for anonymous module support,
+ //where the module name is not known until the script onload event
+ //occurs. If no context, use the global queue, and get it processed
+ //in the onscript load callback.
+ (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
+ };
+
+ define.amd = {
+ jQuery: true
+ };
+
+
+ /**
+ * Executes the text. Normally just uses eval, but can be modified
+ * to use a better, environment-specific call. Only used for transpiling
+ * loader plugins, not for plain JS modules.
+ * @param {String} text the text to execute/evaluate.
+ */
+ req.exec = function (text) {
+ /*jslint evil: true */
+ return eval(text);
+ };
+
+ //Set up with config info.
+ req(cfg);
+}(this));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/LICENSE.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/LICENSE.md
new file mode 100644
index 0000000..cb518af7
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/LICENSE.md
@@ -0,0 +1,2 @@
+This software is dedicated to the public domain. No warranty is expressed or implied.
+Use this software at your own risk.
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/README.md b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/README.md
new file mode 100644
index 0000000..d6ada56
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/README.md
@@ -0,0 +1,7 @@
+classList.js is a cross-browser JavaScript shim that fully implements `element.classList`. Refer to [the MDN page on `element.classList`][1] for more information.
+
+
+
+
+
+ [1]: https://developer.mozilla.org/en/DOM/element.classList "MDN / DOM / element.classList"
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.js
new file mode 100644
index 0000000..0cf197c
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.js
@@ -0,0 +1,138 @@
+
+/*
+ * classList.js: Cross-browser full element.classList implementation.
+ * 2011-06-15
+ *
+ * By Eli Grey, http://eligrey.com
+ * Public Domain.
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+ */
+
+/*global self, document, DOMException */
+
+/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
+
+if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
+
+(function (view) {
+
+"use strict";
+
+var
+ classListProp = "classList"
+ , protoProp = "prototype"
+ , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
+ , objCtr = Object
+ , strTrim = String[protoProp].trim || function () {
+ return this.replace(/^\s+|\s+$/g, "");
+ }
+ , arrIndexOf = Array[protoProp].indexOf || function (item) {
+ var
+ i = 0
+ , len = this.length
+ ;
+ for (; i < len; i++) {
+ if (i in this && this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ // Vendors: please allow content code to instantiate DOMExceptions
+ , DOMEx = function (type, message) {
+ this.name = type;
+ this.code = DOMException[type];
+ this.message = message;
+ }
+ , checkTokenAndGetIndex = function (classList, token) {
+ if (token === "") {
+ throw new DOMEx(
+ "SYNTAX_ERR"
+ , "An invalid or illegal string was specified"
+ );
+ }
+ if (/\s/.test(token)) {
+ throw new DOMEx(
+ "INVALID_CHARACTER_ERR"
+ , "String contains an invalid character"
+ );
+ }
+ return arrIndexOf.call(classList, token);
+ }
+ , ClassList = function (elem) {
+ var
+ trimmedClasses = strTrim.call(elem.className)
+ , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
+ , i = 0
+ , len = classes.length
+ ;
+ for (; i < len; i++) {
+ this.push(classes[i]);
+ }
+ this._updateClassName = function () {
+ elem.className = this.toString();
+ };
+ }
+ , classListProto = ClassList[protoProp] = []
+ , classListGetter = function () {
+ return new ClassList(this);
+ }
+;
+// Most DOMException implementations don't allow calling DOMException's toString()
+// on non-DOMExceptions. Error's toString() is sufficient here.
+DOMEx[protoProp] = Error[protoProp];
+classListProto.item = function (i) {
+ return this[i] || null;
+};
+classListProto.contains = function (token) {
+ token += "";
+ return checkTokenAndGetIndex(this, token) !== -1;
+};
+classListProto.add = function (token) {
+ token += "";
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.push(token);
+ this._updateClassName();
+ }
+};
+classListProto.remove = function (token) {
+ token += "";
+ var index = checkTokenAndGetIndex(this, token);
+ if (index !== -1) {
+ this.splice(index, 1);
+ this._updateClassName();
+ }
+};
+classListProto.toggle = function (token) {
+ token += "";
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.add(token);
+ } else {
+ this.remove(token);
+ }
+};
+classListProto.toString = function () {
+ return this.join(" ");
+};
+
+if (objCtr.defineProperty) {
+ var classListPropDesc = {
+ get: classListGetter
+ , enumerable: true
+ , configurable: true
+ };
+ try {
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ } catch (ex) { // IE 8 doesn't support enumerable:true
+ if (ex.number === -0x7FF5EC54) {
+ classListPropDesc.enumerable = false;
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ }
+ }
+} else if (objCtr[protoProp].__defineGetter__) {
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
+}
+
+}(self));
+
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.min.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.min.js
new file mode 100644
index 0000000..44f2b4c
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/classList.js/classList.min.js
@@ -0,0 +1,2 @@
+/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
+if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p<o;p++){if(p in this&&this[p]===q){return p}}return -1},n=function(o,p){this.name=o;this.code=DOMException[o];this.message=p},g=function(p,o){if(o===""){throw new n("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(o)){throw new n("INVALID_CHARACTER_ERR","String contains an invalid character")}return c.call(p,o)},d=function(s){var r=k.call(s.className),q=r?r.split(/\s+/):[],p=0,o=q.length;for(;p<o;p++){this.push(q[p])}this._updateClassName=function(){s.className=this.toString()}},e=d[f]=[],i=function(){return new d(this)};n[f]=Error[f];e.item=function(o){return this[o]||null};e.contains=function(o){o+="";return g(this,o)!==-1};e.add=function(o){o+="";if(g(this,o)===-1){this.push(o);this._updateClassName()}};e.remove=function(p){p+="";var o=g(this,p);if(o!==-1){this.splice(o,1);this._updateClassName()}};e.toggle=function(o){o+="";if(g(this,o)===-1){this.add(o)}else{this.remove(o)}};e.toString=function(){return this.join(" ")};if(b.defineProperty){var l={get:i,enumerable:true,configurable:true};try{b.defineProperty(m,a,l)}catch(h){if(h.number===-2146823252){l.enumerable=false;b.defineProperty(m,a,l)}}}else{if(b[f].__defineGetter__){m.__defineGetter__(a,i)}}}(self))};
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-core.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-core.js
new file mode 100644
index 0000000..8549d10
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-core.js
@@ -0,0 +1,94 @@
+/*
+ Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Copyright 2012, Tim Down
+ Licensed under the MIT license.
+ Version: 1.2.3
+ Build date: 26 February 2012
+*/
+window.rangy=function(){function l(p,u){var w=typeof p[u];return w=="function"||!!(w=="object"&&p[u])||w=="unknown"}function K(p,u){return!!(typeof p[u]=="object"&&p[u])}function H(p,u){return typeof p[u]!="undefined"}function I(p){return function(u,w){for(var B=w.length;B--;)if(!p(u,w[B]))return false;return true}}function z(p){return p&&A(p,x)&&v(p,t)}function C(p){window.alert("Rangy not supported in your browser. Reason: "+p);c.initialized=true;c.supported=false}function N(){if(!c.initialized){var p,
+u=false,w=false;if(l(document,"createRange")){p=document.createRange();if(A(p,n)&&v(p,i))u=true;p.detach()}if((p=K(document,"body")?document.body:document.getElementsByTagName("body")[0])&&l(p,"createTextRange")){p=p.createTextRange();if(z(p))w=true}!u&&!w&&C("Neither Range nor TextRange are implemented");c.initialized=true;c.features={implementsDomRange:u,implementsTextRange:w};u=k.concat(f);w=0;for(p=u.length;w<p;++w)try{u[w](c)}catch(B){K(window,"console")&&l(window.console,"log")&&window.console.log("Init listener threw an exception. Continuing.",
+B)}}}function O(p){this.name=p;this.supported=this.initialized=false}var i=["startContainer","startOffset","endContainer","endOffset","collapsed","commonAncestorContainer","START_TO_START","START_TO_END","END_TO_START","END_TO_END"],n=["setStart","setStartBefore","setStartAfter","setEnd","setEndBefore","setEndAfter","collapse","selectNode","selectNodeContents","compareBoundaryPoints","deleteContents","extractContents","cloneContents","insertNode","surroundContents","cloneRange","toString","detach"],
+t=["boundingHeight","boundingLeft","boundingTop","boundingWidth","htmlText","text"],x=["collapse","compareEndPoints","duplicate","getBookmark","moveToBookmark","moveToElementText","parentElement","pasteHTML","select","setEndPoint","getBoundingClientRect"],A=I(l),q=I(K),v=I(H),c={version:"1.2.3",initialized:false,supported:true,util:{isHostMethod:l,isHostObject:K,isHostProperty:H,areHostMethods:A,areHostObjects:q,areHostProperties:v,isTextRange:z},features:{},modules:{},config:{alertOnWarn:false,preferTextRange:false}};
+c.fail=C;c.warn=function(p){p="Rangy warning: "+p;if(c.config.alertOnWarn)window.alert(p);else typeof window.console!="undefined"&&typeof window.console.log!="undefined"&&window.console.log(p)};if({}.hasOwnProperty)c.util.extend=function(p,u){for(var w in u)if(u.hasOwnProperty(w))p[w]=u[w]};else C("hasOwnProperty not supported");var f=[],k=[];c.init=N;c.addInitListener=function(p){c.initialized?p(c):f.push(p)};var r=[];c.addCreateMissingNativeApiListener=function(p){r.push(p)};c.createMissingNativeApi=
+function(p){p=p||window;N();for(var u=0,w=r.length;u<w;++u)r[u](p)};O.prototype.fail=function(p){this.initialized=true;this.supported=false;throw Error("Module '"+this.name+"' failed to load: "+p);};O.prototype.warn=function(p){c.warn("Module "+this.name+": "+p)};O.prototype.createError=function(p){return Error("Error in Rangy "+this.name+" module: "+p)};c.createModule=function(p,u){var w=new O(p);c.modules[p]=w;k.push(function(B){u(B,w);w.initialized=true;w.supported=true})};c.requireModules=function(p){for(var u=
+0,w=p.length,B,V;u<w;++u){V=p[u];B=c.modules[V];if(!B||!(B instanceof O))throw Error("Module '"+V+"' not found");if(!B.supported)throw Error("Module '"+V+"' not supported");}};var L=false;q=function(){if(!L){L=true;c.initialized||N()}};if(typeof window=="undefined")C("No window found");else if(typeof document=="undefined")C("No document found");else{l(document,"addEventListener")&&document.addEventListener("DOMContentLoaded",q,false);if(l(window,"addEventListener"))window.addEventListener("load",
+q,false);else l(window,"attachEvent")?window.attachEvent("onload",q):C("Window does not have required addEventListener or attachEvent method");return c}}();
+rangy.createModule("DomUtil",function(l,K){function H(c){for(var f=0;c=c.previousSibling;)f++;return f}function I(c,f){var k=[],r;for(r=c;r;r=r.parentNode)k.push(r);for(r=f;r;r=r.parentNode)if(v(k,r))return r;return null}function z(c,f,k){for(k=k?c:c.parentNode;k;){c=k.parentNode;if(c===f)return k;k=c}return null}function C(c){c=c.nodeType;return c==3||c==4||c==8}function N(c,f){var k=f.nextSibling,r=f.parentNode;k?r.insertBefore(c,k):r.appendChild(c);return c}function O(c){if(c.nodeType==9)return c;
+else if(typeof c.ownerDocument!="undefined")return c.ownerDocument;else if(typeof c.document!="undefined")return c.document;else if(c.parentNode)return O(c.parentNode);else throw Error("getDocument: no document found for node");}function i(c){if(!c)return"[No node]";return C(c)?'"'+c.data+'"':c.nodeType==1?"<"+c.nodeName+(c.id?' id="'+c.id+'"':"")+">["+c.childNodes.length+"]":c.nodeName}function n(c){this._next=this.root=c}function t(c,f){this.node=c;this.offset=f}function x(c){this.code=this[c];
+this.codeName=c;this.message="DOMException: "+this.codeName}var A=l.util;A.areHostMethods(document,["createDocumentFragment","createElement","createTextNode"])||K.fail("document missing a Node creation method");A.isHostMethod(document,"getElementsByTagName")||K.fail("document missing getElementsByTagName method");var q=document.createElement("div");A.areHostMethods(q,["insertBefore","appendChild","cloneNode"])||K.fail("Incomplete Element implementation");A.isHostProperty(q,"innerHTML")||K.fail("Element is missing innerHTML property");
+q=document.createTextNode("test");A.areHostMethods(q,["splitText","deleteData","insertData","appendData","cloneNode"])||K.fail("Incomplete Text Node implementation");var v=function(c,f){for(var k=c.length;k--;)if(c[k]===f)return true;return false};n.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){var c=this._current=this._next,f;if(this._current)if(f=c.firstChild)this._next=f;else{for(f=null;c!==this.root&&!(f=c.nextSibling);)c=c.parentNode;this._next=f}return this._current},
+detach:function(){this._current=this._next=this.root=null}};t.prototype={equals:function(c){return this.node===c.node&this.offset==c.offset},inspect:function(){return"[DomPosition("+i(this.node)+":"+this.offset+")]"}};x.prototype={INDEX_SIZE_ERR:1,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INVALID_STATE_ERR:11};x.prototype.toString=function(){return this.message};l.dom={arrayContains:v,isHtmlNamespace:function(c){var f;return typeof c.namespaceURI==
+"undefined"||(f=c.namespaceURI)===null||f=="http://www.w3.org/1999/xhtml"},parentElement:function(c){c=c.parentNode;return c.nodeType==1?c:null},getNodeIndex:H,getNodeLength:function(c){var f;return C(c)?c.length:(f=c.childNodes)?f.length:0},getCommonAncestor:I,isAncestorOf:function(c,f,k){for(f=k?f:f.parentNode;f;)if(f===c)return true;else f=f.parentNode;return false},getClosestAncestorIn:z,isCharacterDataNode:C,insertAfter:N,splitDataNode:function(c,f){var k=c.cloneNode(false);k.deleteData(0,f);
+c.deleteData(f,c.length-f);N(k,c);return k},getDocument:O,getWindow:function(c){c=O(c);if(typeof c.defaultView!="undefined")return c.defaultView;else if(typeof c.parentWindow!="undefined")return c.parentWindow;else throw Error("Cannot get a window object for node");},getIframeWindow:function(c){if(typeof c.contentWindow!="undefined")return c.contentWindow;else if(typeof c.contentDocument!="undefined")return c.contentDocument.defaultView;else throw Error("getIframeWindow: No Window object found for iframe element");
+},getIframeDocument:function(c){if(typeof c.contentDocument!="undefined")return c.contentDocument;else if(typeof c.contentWindow!="undefined")return c.contentWindow.document;else throw Error("getIframeWindow: No Document object found for iframe element");},getBody:function(c){return A.isHostObject(c,"body")?c.body:c.getElementsByTagName("body")[0]},getRootContainer:function(c){for(var f;f=c.parentNode;)c=f;return c},comparePoints:function(c,f,k,r){var L;if(c==k)return f===r?0:f<r?-1:1;else if(L=z(k,
+c,true))return f<=H(L)?-1:1;else if(L=z(c,k,true))return H(L)<r?-1:1;else{f=I(c,k);c=c===f?f:z(c,f,true);k=k===f?f:z(k,f,true);if(c===k)throw Error("comparePoints got to case 4 and childA and childB are the same!");else{for(f=f.firstChild;f;){if(f===c)return-1;else if(f===k)return 1;f=f.nextSibling}throw Error("Should not be here!");}}},inspectNode:i,fragmentFromNodeChildren:function(c){for(var f=O(c).createDocumentFragment(),k;k=c.firstChild;)f.appendChild(k);return f},createIterator:function(c){return new n(c)},
+DomPosition:t};l.DOMException=x});
+rangy.createModule("DomRange",function(l){function K(a,e){return a.nodeType!=3&&(g.isAncestorOf(a,e.startContainer,true)||g.isAncestorOf(a,e.endContainer,true))}function H(a){return g.getDocument(a.startContainer)}function I(a,e,j){if(e=a._listeners[e])for(var o=0,E=e.length;o<E;++o)e[o].call(a,{target:a,args:j})}function z(a){return new Z(a.parentNode,g.getNodeIndex(a))}function C(a){return new Z(a.parentNode,g.getNodeIndex(a)+1)}function N(a,e,j){var o=a.nodeType==11?a.firstChild:a;if(g.isCharacterDataNode(e))j==
+e.length?g.insertAfter(a,e):e.parentNode.insertBefore(a,j==0?e:g.splitDataNode(e,j));else j>=e.childNodes.length?e.appendChild(a):e.insertBefore(a,e.childNodes[j]);return o}function O(a){for(var e,j,o=H(a.range).createDocumentFragment();j=a.next();){e=a.isPartiallySelectedSubtree();j=j.cloneNode(!e);if(e){e=a.getSubtreeIterator();j.appendChild(O(e));e.detach(true)}if(j.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");o.appendChild(j)}return o}function i(a,e,j){var o,E;for(j=j||{stop:false};o=a.next();)if(a.isPartiallySelectedSubtree())if(e(o)===
+false){j.stop=true;return}else{o=a.getSubtreeIterator();i(o,e,j);o.detach(true);if(j.stop)return}else for(o=g.createIterator(o);E=o.next();)if(e(E)===false){j.stop=true;return}}function n(a){for(var e;a.next();)if(a.isPartiallySelectedSubtree()){e=a.getSubtreeIterator();n(e);e.detach(true)}else a.remove()}function t(a){for(var e,j=H(a.range).createDocumentFragment(),o;e=a.next();){if(a.isPartiallySelectedSubtree()){e=e.cloneNode(false);o=a.getSubtreeIterator();e.appendChild(t(o));o.detach(true)}else a.remove();
+if(e.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");j.appendChild(e)}return j}function x(a,e,j){var o=!!(e&&e.length),E,T=!!j;if(o)E=RegExp("^("+e.join("|")+")$");var m=[];i(new q(a,false),function(s){if((!o||E.test(s.nodeType))&&(!T||j(s)))m.push(s)});return m}function A(a){return"["+(typeof a.getName=="undefined"?"Range":a.getName())+"("+g.inspectNode(a.startContainer)+":"+a.startOffset+", "+g.inspectNode(a.endContainer)+":"+a.endOffset+")]"}function q(a,e){this.range=a;this.clonePartiallySelectedTextNodes=
+e;if(!a.collapsed){this.sc=a.startContainer;this.so=a.startOffset;this.ec=a.endContainer;this.eo=a.endOffset;var j=a.commonAncestorContainer;if(this.sc===this.ec&&g.isCharacterDataNode(this.sc)){this.isSingleCharacterDataNode=true;this._first=this._last=this._next=this.sc}else{this._first=this._next=this.sc===j&&!g.isCharacterDataNode(this.sc)?this.sc.childNodes[this.so]:g.getClosestAncestorIn(this.sc,j,true);this._last=this.ec===j&&!g.isCharacterDataNode(this.ec)?this.ec.childNodes[this.eo-1]:g.getClosestAncestorIn(this.ec,
+j,true)}}}function v(a){this.code=this[a];this.codeName=a;this.message="RangeException: "+this.codeName}function c(a,e,j){this.nodes=x(a,e,j);this._next=this.nodes[0];this._position=0}function f(a){return function(e,j){for(var o,E=j?e:e.parentNode;E;){o=E.nodeType;if(g.arrayContains(a,o))return E;E=E.parentNode}return null}}function k(a,e){if(G(a,e))throw new v("INVALID_NODE_TYPE_ERR");}function r(a){if(!a.startContainer)throw new S("INVALID_STATE_ERR");}function L(a,e){if(!g.arrayContains(e,a.nodeType))throw new v("INVALID_NODE_TYPE_ERR");
+}function p(a,e){if(e<0||e>(g.isCharacterDataNode(a)?a.length:a.childNodes.length))throw new S("INDEX_SIZE_ERR");}function u(a,e){if(h(a,true)!==h(e,true))throw new S("WRONG_DOCUMENT_ERR");}function w(a){if(D(a,true))throw new S("NO_MODIFICATION_ALLOWED_ERR");}function B(a,e){if(!a)throw new S(e);}function V(a){return!!a.startContainer&&!!a.endContainer&&!(!g.arrayContains(ba,a.startContainer.nodeType)&&!h(a.startContainer,true))&&!(!g.arrayContains(ba,a.endContainer.nodeType)&&!h(a.endContainer,
+true))&&a.startOffset<=(g.isCharacterDataNode(a.startContainer)?a.startContainer.length:a.startContainer.childNodes.length)&&a.endOffset<=(g.isCharacterDataNode(a.endContainer)?a.endContainer.length:a.endContainer.childNodes.length)}function J(a){r(a);if(!V(a))throw Error("Range error: Range is no longer valid after DOM mutation ("+a.inspect()+")");}function ca(){}function Y(a){a.START_TO_START=ia;a.START_TO_END=la;a.END_TO_END=ra;a.END_TO_START=ma;a.NODE_BEFORE=na;a.NODE_AFTER=oa;a.NODE_BEFORE_AND_AFTER=
+pa;a.NODE_INSIDE=ja}function W(a){Y(a);Y(a.prototype)}function da(a,e){return function(){J(this);var j=this.startContainer,o=this.startOffset,E=this.commonAncestorContainer,T=new q(this,true);if(j!==E){j=g.getClosestAncestorIn(j,E,true);o=C(j);j=o.node;o=o.offset}i(T,w);T.reset();E=a(T);T.detach();e(this,j,o,j,o);return E}}function fa(a,e,j){function o(m,s){return function(y){r(this);L(y,$);L(d(y),ba);y=(m?z:C)(y);(s?E:T)(this,y.node,y.offset)}}function E(m,s,y){var F=m.endContainer,Q=m.endOffset;
+if(s!==m.startContainer||y!==m.startOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==1){F=s;Q=y}e(m,s,y,F,Q)}}function T(m,s,y){var F=m.startContainer,Q=m.startOffset;if(s!==m.endContainer||y!==m.endOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==-1){F=s;Q=y}e(m,F,Q,s,y)}}a.prototype=new ca;l.util.extend(a.prototype,{setStart:function(m,s){r(this);k(m,true);p(m,s);E(this,m,s)},setEnd:function(m,s){r(this);k(m,true);p(m,s);T(this,m,s)},setStartBefore:o(true,true),setStartAfter:o(false,true),setEndBefore:o(true,
+false),setEndAfter:o(false,false),collapse:function(m){J(this);m?e(this,this.startContainer,this.startOffset,this.startContainer,this.startOffset):e(this,this.endContainer,this.endOffset,this.endContainer,this.endOffset)},selectNodeContents:function(m){r(this);k(m,true);e(this,m,0,m,g.getNodeLength(m))},selectNode:function(m){r(this);k(m,false);L(m,$);var s=z(m);m=C(m);e(this,s.node,s.offset,m.node,m.offset)},extractContents:da(t,e),deleteContents:da(n,e),canSurroundContents:function(){J(this);w(this.startContainer);
+w(this.endContainer);var m=new q(this,true),s=m._first&&K(m._first,this)||m._last&&K(m._last,this);m.detach();return!s},detach:function(){j(this)},splitBoundaries:function(){J(this);var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=m===y;g.isCharacterDataNode(y)&&F>0&&F<y.length&&g.splitDataNode(y,F);if(g.isCharacterDataNode(m)&&s>0&&s<m.length){m=g.splitDataNode(m,s);if(Q){F-=s;y=m}else y==m.parentNode&&F>=g.getNodeIndex(m)&&F++;s=0}e(this,m,s,y,F)},normalizeBoundaries:function(){J(this);
+var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=function(U){var R=U.nextSibling;if(R&&R.nodeType==U.nodeType){y=U;F=U.length;U.appendData(R.data);R.parentNode.removeChild(R)}},qa=function(U){var R=U.previousSibling;if(R&&R.nodeType==U.nodeType){m=U;var sa=U.length;s=R.length;U.insertData(0,R.data);R.parentNode.removeChild(R);if(m==y){F+=s;y=m}else if(y==U.parentNode){R=g.getNodeIndex(U);if(F==R){y=U;F=sa}else F>R&&F--}}},ga=true;if(g.isCharacterDataNode(y))y.length==
+F&&Q(y);else{if(F>0)(ga=y.childNodes[F-1])&&g.isCharacterDataNode(ga)&&Q(ga);ga=!this.collapsed}if(ga)if(g.isCharacterDataNode(m))s==0&&qa(m);else{if(s<m.childNodes.length)(Q=m.childNodes[s])&&g.isCharacterDataNode(Q)&&qa(Q)}else{m=y;s=F}e(this,m,s,y,F)},collapseToPoint:function(m,s){r(this);k(m,true);p(m,s);if(m!==this.startContainer||s!==this.startOffset||m!==this.endContainer||s!==this.endOffset)e(this,m,s,m,s)}});W(a)}function ea(a){a.collapsed=a.startContainer===a.endContainer&&a.startOffset===
+a.endOffset;a.commonAncestorContainer=a.collapsed?a.startContainer:g.getCommonAncestor(a.startContainer,a.endContainer)}function ha(a,e,j,o,E){var T=a.startContainer!==e||a.startOffset!==j,m=a.endContainer!==o||a.endOffset!==E;a.startContainer=e;a.startOffset=j;a.endContainer=o;a.endOffset=E;ea(a);I(a,"boundarychange",{startMoved:T,endMoved:m})}function M(a){this.startContainer=a;this.startOffset=0;this.endContainer=a;this.endOffset=0;this._listeners={boundarychange:[],detach:[]};ea(this)}l.requireModules(["DomUtil"]);
+var g=l.dom,Z=g.DomPosition,S=l.DOMException;q.prototype={_current:null,_next:null,_first:null,_last:null,isSingleCharacterDataNode:false,reset:function(){this._current=null;this._next=this._first},hasNext:function(){return!!this._next},next:function(){var a=this._current=this._next;if(a){this._next=a!==this._last?a.nextSibling:null;if(g.isCharacterDataNode(a)&&this.clonePartiallySelectedTextNodes){if(a===this.ec)(a=a.cloneNode(true)).deleteData(this.eo,a.length-this.eo);if(this._current===this.sc)(a=
+a.cloneNode(true)).deleteData(0,this.so)}}return a},remove:function(){var a=this._current,e,j;if(g.isCharacterDataNode(a)&&(a===this.sc||a===this.ec)){e=a===this.sc?this.so:0;j=a===this.ec?this.eo:a.length;e!=j&&a.deleteData(e,j-e)}else a.parentNode&&a.parentNode.removeChild(a)},isPartiallySelectedSubtree:function(){return K(this._current,this.range)},getSubtreeIterator:function(){var a;if(this.isSingleCharacterDataNode){a=this.range.cloneRange();a.collapse()}else{a=new M(H(this.range));var e=this._current,
+j=e,o=0,E=e,T=g.getNodeLength(e);if(g.isAncestorOf(e,this.sc,true)){j=this.sc;o=this.so}if(g.isAncestorOf(e,this.ec,true)){E=this.ec;T=this.eo}ha(a,j,o,E,T)}return new q(a,this.clonePartiallySelectedTextNodes)},detach:function(a){a&&this.range.detach();this.range=this._current=this._next=this._first=this._last=this.sc=this.so=this.ec=this.eo=null}};v.prototype={BAD_BOUNDARYPOINTS_ERR:1,INVALID_NODE_TYPE_ERR:2};v.prototype.toString=function(){return this.message};c.prototype={_current:null,hasNext:function(){return!!this._next},
+next:function(){this._current=this._next;this._next=this.nodes[++this._position];return this._current},detach:function(){this._current=this._next=this.nodes=null}};var $=[1,3,4,5,7,8,10],ba=[2,9,11],aa=[1,3,4,5,7,8,10,11],b=[1,3,4,5,7,8],d=g.getRootContainer,h=f([9,11]),D=f([5,6,10,12]),G=f([6,10,12]),P=document.createElement("style"),X=false;try{P.innerHTML="<b>x</b>";X=P.firstChild.nodeType==3}catch(ta){}l.features.htmlParsingConforms=X;var ka=["startContainer","startOffset","endContainer","endOffset",
+"collapsed","commonAncestorContainer"],ia=0,la=1,ra=2,ma=3,na=0,oa=1,pa=2,ja=3;ca.prototype={attachListener:function(a,e){this._listeners[a].push(e)},compareBoundaryPoints:function(a,e){J(this);u(this.startContainer,e.startContainer);var j=a==ma||a==ia?"start":"end",o=a==la||a==ia?"start":"end";return g.comparePoints(this[j+"Container"],this[j+"Offset"],e[o+"Container"],e[o+"Offset"])},insertNode:function(a){J(this);L(a,aa);w(this.startContainer);if(g.isAncestorOf(a,this.startContainer,true))throw new S("HIERARCHY_REQUEST_ERR");
+this.setStartBefore(N(a,this.startContainer,this.startOffset))},cloneContents:function(){J(this);var a,e;if(this.collapsed)return H(this).createDocumentFragment();else{if(this.startContainer===this.endContainer&&g.isCharacterDataNode(this.startContainer)){a=this.startContainer.cloneNode(true);a.data=a.data.slice(this.startOffset,this.endOffset);e=H(this).createDocumentFragment();e.appendChild(a);return e}else{e=new q(this,true);a=O(e);e.detach()}return a}},canSurroundContents:function(){J(this);w(this.startContainer);
+w(this.endContainer);var a=new q(this,true),e=a._first&&K(a._first,this)||a._last&&K(a._last,this);a.detach();return!e},surroundContents:function(a){L(a,b);if(!this.canSurroundContents())throw new v("BAD_BOUNDARYPOINTS_ERR");var e=this.extractContents();if(a.hasChildNodes())for(;a.lastChild;)a.removeChild(a.lastChild);N(a,this.startContainer,this.startOffset);a.appendChild(e);this.selectNode(a)},cloneRange:function(){J(this);for(var a=new M(H(this)),e=ka.length,j;e--;){j=ka[e];a[j]=this[j]}return a},
+toString:function(){J(this);var a=this.startContainer;if(a===this.endContainer&&g.isCharacterDataNode(a))return a.nodeType==3||a.nodeType==4?a.data.slice(this.startOffset,this.endOffset):"";else{var e=[];a=new q(this,true);i(a,function(j){if(j.nodeType==3||j.nodeType==4)e.push(j.data)});a.detach();return e.join("")}},compareNode:function(a){J(this);var e=a.parentNode,j=g.getNodeIndex(a);if(!e)throw new S("NOT_FOUND_ERR");a=this.comparePoint(e,j);e=this.comparePoint(e,j+1);return a<0?e>0?pa:na:e>0?
+oa:ja},comparePoint:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);if(g.comparePoints(a,e,this.startContainer,this.startOffset)<0)return-1;else if(g.comparePoints(a,e,this.endContainer,this.endOffset)>0)return 1;return 0},createContextualFragment:X?function(a){var e=this.startContainer,j=g.getDocument(e);if(!e)throw new S("INVALID_STATE_ERR");var o=null;if(e.nodeType==1)o=e;else if(g.isCharacterDataNode(e))o=g.parentElement(e);o=o===null||o.nodeName=="HTML"&&g.isHtmlNamespace(g.getDocument(o).documentElement)&&
+g.isHtmlNamespace(o)?j.createElement("body"):o.cloneNode(false);o.innerHTML=a;return g.fragmentFromNodeChildren(o)}:function(a){r(this);var e=H(this).createElement("body");e.innerHTML=a;return g.fragmentFromNodeChildren(e)},toHtml:function(){J(this);var a=H(this).createElement("div");a.appendChild(this.cloneContents());return a.innerHTML},intersectsNode:function(a,e){J(this);B(a,"NOT_FOUND_ERR");if(g.getDocument(a)!==H(this))return false;var j=a.parentNode,o=g.getNodeIndex(a);B(j,"NOT_FOUND_ERR");
+var E=g.comparePoints(j,o,this.endContainer,this.endOffset);j=g.comparePoints(j,o+1,this.startContainer,this.startOffset);return e?E<=0&&j>=0:E<0&&j>0},isPointInRange:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);return g.comparePoints(a,e,this.startContainer,this.startOffset)>=0&&g.comparePoints(a,e,this.endContainer,this.endOffset)<=0},intersectsRange:function(a,e){J(this);if(H(a)!=H(this))throw new S("WRONG_DOCUMENT_ERR");var j=g.comparePoints(this.startContainer,
+this.startOffset,a.endContainer,a.endOffset),o=g.comparePoints(this.endContainer,this.endOffset,a.startContainer,a.startOffset);return e?j<=0&&o>=0:j<0&&o>0},intersection:function(a){if(this.intersectsRange(a)){var e=g.comparePoints(this.startContainer,this.startOffset,a.startContainer,a.startOffset),j=g.comparePoints(this.endContainer,this.endOffset,a.endContainer,a.endOffset),o=this.cloneRange();e==-1&&o.setStart(a.startContainer,a.startOffset);j==1&&o.setEnd(a.endContainer,a.endOffset);return o}return null},
+union:function(a){if(this.intersectsRange(a,true)){var e=this.cloneRange();g.comparePoints(a.startContainer,a.startOffset,this.startContainer,this.startOffset)==-1&&e.setStart(a.startContainer,a.startOffset);g.comparePoints(a.endContainer,a.endOffset,this.endContainer,this.endOffset)==1&&e.setEnd(a.endContainer,a.endOffset);return e}else throw new v("Ranges do not intersect");},containsNode:function(a,e){return e?this.intersectsNode(a,false):this.compareNode(a)==ja},containsNodeContents:function(a){return this.comparePoint(a,
+0)>=0&&this.comparePoint(a,g.getNodeLength(a))<=0},containsRange:function(a){return this.intersection(a).equals(a)},containsNodeText:function(a){var e=this.cloneRange();e.selectNode(a);var j=e.getNodes([3]);if(j.length>0){e.setStart(j[0],0);a=j.pop();e.setEnd(a,a.length);a=this.containsRange(e);e.detach();return a}else return this.containsNodeContents(a)},createNodeIterator:function(a,e){J(this);return new c(this,a,e)},getNodes:function(a,e){J(this);return x(this,a,e)},getDocument:function(){return H(this)},
+collapseBefore:function(a){r(this);this.setEndBefore(a);this.collapse(false)},collapseAfter:function(a){r(this);this.setStartAfter(a);this.collapse(true)},getName:function(){return"DomRange"},equals:function(a){return M.rangesEqual(this,a)},isValid:function(){return V(this)},inspect:function(){return A(this)}};fa(M,ha,function(a){r(a);a.startContainer=a.startOffset=a.endContainer=a.endOffset=null;a.collapsed=a.commonAncestorContainer=null;I(a,"detach",null);a._listeners=null});l.rangePrototype=ca.prototype;
+M.rangeProperties=ka;M.RangeIterator=q;M.copyComparisonConstants=W;M.createPrototypeRange=fa;M.inspect=A;M.getRangeDocument=H;M.rangesEqual=function(a,e){return a.startContainer===e.startContainer&&a.startOffset===e.startOffset&&a.endContainer===e.endContainer&&a.endOffset===e.endOffset};l.DomRange=M;l.RangeException=v});
+rangy.createModule("WrappedRange",function(l){function K(i,n,t,x){var A=i.duplicate();A.collapse(t);var q=A.parentElement();z.isAncestorOf(n,q,true)||(q=n);if(!q.canHaveHTML)return new C(q.parentNode,z.getNodeIndex(q));n=z.getDocument(q).createElement("span");var v,c=t?"StartToStart":"StartToEnd";do{q.insertBefore(n,n.previousSibling);A.moveToElementText(n)}while((v=A.compareEndPoints(c,i))>0&&n.previousSibling);c=n.nextSibling;if(v==-1&&c&&z.isCharacterDataNode(c)){A.setEndPoint(t?"EndToStart":"EndToEnd",
+i);if(/[\r\n]/.test(c.data)){q=A.duplicate();t=q.text.replace(/\r\n/g,"\r").length;for(t=q.moveStart("character",t);q.compareEndPoints("StartToEnd",q)==-1;){t++;q.moveStart("character",1)}}else t=A.text.length;q=new C(c,t)}else{c=(x||!t)&&n.previousSibling;q=(t=(x||t)&&n.nextSibling)&&z.isCharacterDataNode(t)?new C(t,0):c&&z.isCharacterDataNode(c)?new C(c,c.length):new C(q,z.getNodeIndex(n))}n.parentNode.removeChild(n);return q}function H(i,n){var t,x,A=i.offset,q=z.getDocument(i.node),v=q.body.createTextRange(),
+c=z.isCharacterDataNode(i.node);if(c){t=i.node;x=t.parentNode}else{t=i.node.childNodes;t=A<t.length?t[A]:null;x=i.node}q=q.createElement("span");q.innerHTML="&#feff;";t?x.insertBefore(q,t):x.appendChild(q);v.moveToElementText(q);v.collapse(!n);x.removeChild(q);if(c)v[n?"moveStart":"moveEnd"]("character",A);return v}l.requireModules(["DomUtil","DomRange"]);var I,z=l.dom,C=z.DomPosition,N=l.DomRange;if(l.features.implementsDomRange&&(!l.features.implementsTextRange||!l.config.preferTextRange)){(function(){function i(f){for(var k=
+t.length,r;k--;){r=t[k];f[r]=f.nativeRange[r]}}var n,t=N.rangeProperties,x,A;I=function(f){if(!f)throw Error("Range must be specified");this.nativeRange=f;i(this)};N.createPrototypeRange(I,function(f,k,r,L,p){var u=f.endContainer!==L||f.endOffset!=p;if(f.startContainer!==k||f.startOffset!=r||u){f.setEnd(L,p);f.setStart(k,r)}},function(f){f.nativeRange.detach();f.detached=true;for(var k=t.length,r;k--;){r=t[k];f[r]=null}});n=I.prototype;n.selectNode=function(f){this.nativeRange.selectNode(f);i(this)};
+n.deleteContents=function(){this.nativeRange.deleteContents();i(this)};n.extractContents=function(){var f=this.nativeRange.extractContents();i(this);return f};n.cloneContents=function(){return this.nativeRange.cloneContents()};n.surroundContents=function(f){this.nativeRange.surroundContents(f);i(this)};n.collapse=function(f){this.nativeRange.collapse(f);i(this)};n.cloneRange=function(){return new I(this.nativeRange.cloneRange())};n.refresh=function(){i(this)};n.toString=function(){return this.nativeRange.toString()};
+var q=document.createTextNode("test");z.getBody(document).appendChild(q);var v=document.createRange();v.setStart(q,0);v.setEnd(q,0);try{v.setStart(q,1);x=true;n.setStart=function(f,k){this.nativeRange.setStart(f,k);i(this)};n.setEnd=function(f,k){this.nativeRange.setEnd(f,k);i(this)};A=function(f){return function(k){this.nativeRange[f](k);i(this)}}}catch(c){x=false;n.setStart=function(f,k){try{this.nativeRange.setStart(f,k)}catch(r){this.nativeRange.setEnd(f,k);this.nativeRange.setStart(f,k)}i(this)};
+n.setEnd=function(f,k){try{this.nativeRange.setEnd(f,k)}catch(r){this.nativeRange.setStart(f,k);this.nativeRange.setEnd(f,k)}i(this)};A=function(f,k){return function(r){try{this.nativeRange[f](r)}catch(L){this.nativeRange[k](r);this.nativeRange[f](r)}i(this)}}}n.setStartBefore=A("setStartBefore","setEndBefore");n.setStartAfter=A("setStartAfter","setEndAfter");n.setEndBefore=A("setEndBefore","setStartBefore");n.setEndAfter=A("setEndAfter","setStartAfter");v.selectNodeContents(q);n.selectNodeContents=
+v.startContainer==q&&v.endContainer==q&&v.startOffset==0&&v.endOffset==q.length?function(f){this.nativeRange.selectNodeContents(f);i(this)}:function(f){this.setStart(f,0);this.setEnd(f,N.getEndOffset(f))};v.selectNodeContents(q);v.setEnd(q,3);x=document.createRange();x.selectNodeContents(q);x.setEnd(q,4);x.setStart(q,2);n.compareBoundaryPoints=v.compareBoundaryPoints(v.START_TO_END,x)==-1&v.compareBoundaryPoints(v.END_TO_START,x)==1?function(f,k){k=k.nativeRange||k;if(f==k.START_TO_END)f=k.END_TO_START;
+else if(f==k.END_TO_START)f=k.START_TO_END;return this.nativeRange.compareBoundaryPoints(f,k)}:function(f,k){return this.nativeRange.compareBoundaryPoints(f,k.nativeRange||k)};if(l.util.isHostMethod(v,"createContextualFragment"))n.createContextualFragment=function(f){return this.nativeRange.createContextualFragment(f)};z.getBody(document).removeChild(q);v.detach();x.detach()})();l.createNativeRange=function(i){i=i||document;return i.createRange()}}else if(l.features.implementsTextRange){I=function(i){this.textRange=
+i;this.refresh()};I.prototype=new N(document);I.prototype.refresh=function(){var i,n,t=this.textRange;i=t.parentElement();var x=t.duplicate();x.collapse(true);n=x.parentElement();x=t.duplicate();x.collapse(false);t=x.parentElement();n=n==t?n:z.getCommonAncestor(n,t);n=n==i?n:z.getCommonAncestor(i,n);if(this.textRange.compareEndPoints("StartToEnd",this.textRange)==0)n=i=K(this.textRange,n,true,true);else{i=K(this.textRange,n,true,false);n=K(this.textRange,n,false,false)}this.setStart(i.node,i.offset);
+this.setEnd(n.node,n.offset)};N.copyComparisonConstants(I);var O=function(){return this}();if(typeof O.Range=="undefined")O.Range=I;l.createNativeRange=function(i){i=i||document;return i.body.createTextRange()}}if(l.features.implementsTextRange)I.rangeToTextRange=function(i){if(i.collapsed)return H(new C(i.startContainer,i.startOffset),true);else{var n=H(new C(i.startContainer,i.startOffset),true),t=H(new C(i.endContainer,i.endOffset),false);i=z.getDocument(i.startContainer).body.createTextRange();
+i.setEndPoint("StartToStart",n);i.setEndPoint("EndToEnd",t);return i}};I.prototype.getName=function(){return"WrappedRange"};l.WrappedRange=I;l.createRange=function(i){i=i||document;return new I(l.createNativeRange(i))};l.createRangyRange=function(i){i=i||document;return new N(i)};l.createIframeRange=function(i){return l.createRange(z.getIframeDocument(i))};l.createIframeRangyRange=function(i){return l.createRangyRange(z.getIframeDocument(i))};l.addCreateMissingNativeApiListener(function(i){i=i.document;
+if(typeof i.createRange=="undefined")i.createRange=function(){return l.createRange(this)};i=i=null})});
+rangy.createModule("WrappedSelection",function(l,K){function H(b){return(b||window).getSelection()}function I(b){return(b||window).document.selection}function z(b,d,h){var D=h?"end":"start";h=h?"start":"end";b.anchorNode=d[D+"Container"];b.anchorOffset=d[D+"Offset"];b.focusNode=d[h+"Container"];b.focusOffset=d[h+"Offset"]}function C(b){b.anchorNode=b.focusNode=null;b.anchorOffset=b.focusOffset=0;b.rangeCount=0;b.isCollapsed=true;b._ranges.length=0}function N(b){var d;if(b instanceof k){d=b._selectionNativeRange;
+if(!d){d=l.createNativeRange(c.getDocument(b.startContainer));d.setEnd(b.endContainer,b.endOffset);d.setStart(b.startContainer,b.startOffset);b._selectionNativeRange=d;b.attachListener("detach",function(){this._selectionNativeRange=null})}}else if(b instanceof r)d=b.nativeRange;else if(l.features.implementsDomRange&&b instanceof c.getWindow(b.startContainer).Range)d=b;return d}function O(b){var d=b.getNodes(),h;a:if(!d.length||d[0].nodeType!=1)h=false;else{h=1;for(var D=d.length;h<D;++h)if(!c.isAncestorOf(d[0],
+d[h])){h=false;break a}h=true}if(!h)throw Error("getSingleElementFromRange: range "+b.inspect()+" did not consist of a single element");return d[0]}function i(b,d){var h=new r(d);b._ranges=[h];z(b,h,false);b.rangeCount=1;b.isCollapsed=h.collapsed}function n(b){b._ranges.length=0;if(b.docSelection.type=="None")C(b);else{var d=b.docSelection.createRange();if(d&&typeof d.text!="undefined")i(b,d);else{b.rangeCount=d.length;for(var h,D=c.getDocument(d.item(0)),G=0;G<b.rangeCount;++G){h=l.createRange(D);
+h.selectNode(d.item(G));b._ranges.push(h)}b.isCollapsed=b.rangeCount==1&&b._ranges[0].collapsed;z(b,b._ranges[b.rangeCount-1],false)}}}function t(b,d){var h=b.docSelection.createRange(),D=O(d),G=c.getDocument(h.item(0));G=c.getBody(G).createControlRange();for(var P=0,X=h.length;P<X;++P)G.add(h.item(P));try{G.add(D)}catch(ta){throw Error("addRange(): Element within the specified Range could not be added to control selection (does it have layout?)");}G.select();n(b)}function x(b,d,h){this.nativeSelection=
+b;this.docSelection=d;this._ranges=[];this.win=h;this.refresh()}function A(b,d){var h=c.getDocument(d[0].startContainer);h=c.getBody(h).createControlRange();for(var D=0,G;D<rangeCount;++D){G=O(d[D]);try{h.add(G)}catch(P){throw Error("setRanges(): Element within the one of the specified Ranges could not be added to control selection (does it have layout?)");}}h.select();n(b)}function q(b,d){if(b.anchorNode&&c.getDocument(b.anchorNode)!==c.getDocument(d))throw new L("WRONG_DOCUMENT_ERR");}function v(b){var d=
+[],h=new p(b.anchorNode,b.anchorOffset),D=new p(b.focusNode,b.focusOffset),G=typeof b.getName=="function"?b.getName():"Selection";if(typeof b.rangeCount!="undefined")for(var P=0,X=b.rangeCount;P<X;++P)d[P]=k.inspect(b.getRangeAt(P));return"["+G+"(Ranges: "+d.join(", ")+")(anchor: "+h.inspect()+", focus: "+D.inspect()+"]"}l.requireModules(["DomUtil","DomRange","WrappedRange"]);l.config.checkSelectionRanges=true;var c=l.dom,f=l.util,k=l.DomRange,r=l.WrappedRange,L=l.DOMException,p=c.DomPosition,u,w,
+B=l.util.isHostMethod(window,"getSelection"),V=l.util.isHostObject(document,"selection"),J=V&&(!B||l.config.preferTextRange);if(J){u=I;l.isSelectionValid=function(b){b=(b||window).document;var d=b.selection;return d.type!="None"||c.getDocument(d.createRange().parentElement())==b}}else if(B){u=H;l.isSelectionValid=function(){return true}}else K.fail("Neither document.selection or window.getSelection() detected.");l.getNativeSelection=u;B=u();var ca=l.createNativeRange(document),Y=c.getBody(document),
+W=f.areHostObjects(B,f.areHostProperties(B,["anchorOffset","focusOffset"]));l.features.selectionHasAnchorAndFocus=W;var da=f.isHostMethod(B,"extend");l.features.selectionHasExtend=da;var fa=typeof B.rangeCount=="number";l.features.selectionHasRangeCount=fa;var ea=false,ha=true;f.areHostMethods(B,["addRange","getRangeAt","removeAllRanges"])&&typeof B.rangeCount=="number"&&l.features.implementsDomRange&&function(){var b=document.createElement("iframe");b.frameBorder=0;b.style.position="absolute";b.style.left=
+"-10000px";Y.appendChild(b);var d=c.getIframeDocument(b);d.open();d.write("<html><head></head><body>12</body></html>");d.close();var h=c.getIframeWindow(b).getSelection(),D=d.documentElement.lastChild.firstChild;d=d.createRange();d.setStart(D,1);d.collapse(true);h.addRange(d);ha=h.rangeCount==1;h.removeAllRanges();var G=d.cloneRange();d.setStart(D,0);G.setEnd(D,2);h.addRange(d);h.addRange(G);ea=h.rangeCount==2;d.detach();G.detach();Y.removeChild(b)}();l.features.selectionSupportsMultipleRanges=ea;
+l.features.collapsedNonEditableSelectionsSupported=ha;var M=false,g;if(Y&&f.isHostMethod(Y,"createControlRange")){g=Y.createControlRange();if(f.areHostProperties(g,["item","add"]))M=true}l.features.implementsControlRange=M;w=W?function(b){return b.anchorNode===b.focusNode&&b.anchorOffset===b.focusOffset}:function(b){return b.rangeCount?b.getRangeAt(b.rangeCount-1).collapsed:false};var Z;if(f.isHostMethod(B,"getRangeAt"))Z=function(b,d){try{return b.getRangeAt(d)}catch(h){return null}};else if(W)Z=
+function(b){var d=c.getDocument(b.anchorNode);d=l.createRange(d);d.setStart(b.anchorNode,b.anchorOffset);d.setEnd(b.focusNode,b.focusOffset);if(d.collapsed!==this.isCollapsed){d.setStart(b.focusNode,b.focusOffset);d.setEnd(b.anchorNode,b.anchorOffset)}return d};l.getSelection=function(b){b=b||window;var d=b._rangySelection,h=u(b),D=V?I(b):null;if(d){d.nativeSelection=h;d.docSelection=D;d.refresh(b)}else{d=new x(h,D,b);b._rangySelection=d}return d};l.getIframeSelection=function(b){return l.getSelection(c.getIframeWindow(b))};
+g=x.prototype;if(!J&&W&&f.areHostMethods(B,["removeAllRanges","addRange"])){g.removeAllRanges=function(){this.nativeSelection.removeAllRanges();C(this)};var S=function(b,d){var h=k.getRangeDocument(d);h=l.createRange(h);h.collapseToPoint(d.endContainer,d.endOffset);b.nativeSelection.addRange(N(h));b.nativeSelection.extend(d.startContainer,d.startOffset);b.refresh()};g.addRange=fa?function(b,d){if(M&&V&&this.docSelection.type=="Control")t(this,b);else if(d&&da)S(this,b);else{var h;if(ea)h=this.rangeCount;
+else{this.removeAllRanges();h=0}this.nativeSelection.addRange(N(b));this.rangeCount=this.nativeSelection.rangeCount;if(this.rangeCount==h+1){if(l.config.checkSelectionRanges)if((h=Z(this.nativeSelection,this.rangeCount-1))&&!k.rangesEqual(h,b))b=new r(h);this._ranges[this.rangeCount-1]=b;z(this,b,aa(this.nativeSelection));this.isCollapsed=w(this)}else this.refresh()}}:function(b,d){if(d&&da)S(this,b);else{this.nativeSelection.addRange(N(b));this.refresh()}};g.setRanges=function(b){if(M&&b.length>
+1)A(this,b);else{this.removeAllRanges();for(var d=0,h=b.length;d<h;++d)this.addRange(b[d])}}}else if(f.isHostMethod(B,"empty")&&f.isHostMethod(ca,"select")&&M&&J){g.removeAllRanges=function(){try{this.docSelection.empty();if(this.docSelection.type!="None"){var b;if(this.anchorNode)b=c.getDocument(this.anchorNode);else if(this.docSelection.type=="Control"){var d=this.docSelection.createRange();if(d.length)b=c.getDocument(d.item(0)).body.createTextRange()}if(b){b.body.createTextRange().select();this.docSelection.empty()}}}catch(h){}C(this)};
+g.addRange=function(b){if(this.docSelection.type=="Control")t(this,b);else{r.rangeToTextRange(b).select();this._ranges[0]=b;this.rangeCount=1;this.isCollapsed=this._ranges[0].collapsed;z(this,b,false)}};g.setRanges=function(b){this.removeAllRanges();var d=b.length;if(d>1)A(this,b);else d&&this.addRange(b[0])}}else{K.fail("No means of selecting a Range or TextRange was found");return false}g.getRangeAt=function(b){if(b<0||b>=this.rangeCount)throw new L("INDEX_SIZE_ERR");else return this._ranges[b]};
+var $;if(J)$=function(b){var d;if(l.isSelectionValid(b.win))d=b.docSelection.createRange();else{d=c.getBody(b.win.document).createTextRange();d.collapse(true)}if(b.docSelection.type=="Control")n(b);else d&&typeof d.text!="undefined"?i(b,d):C(b)};else if(f.isHostMethod(B,"getRangeAt")&&typeof B.rangeCount=="number")$=function(b){if(M&&V&&b.docSelection.type=="Control")n(b);else{b._ranges.length=b.rangeCount=b.nativeSelection.rangeCount;if(b.rangeCount){for(var d=0,h=b.rangeCount;d<h;++d)b._ranges[d]=
+new l.WrappedRange(b.nativeSelection.getRangeAt(d));z(b,b._ranges[b.rangeCount-1],aa(b.nativeSelection));b.isCollapsed=w(b)}else C(b)}};else if(W&&typeof B.isCollapsed=="boolean"&&typeof ca.collapsed=="boolean"&&l.features.implementsDomRange)$=function(b){var d;d=b.nativeSelection;if(d.anchorNode){d=Z(d,0);b._ranges=[d];b.rangeCount=1;d=b.nativeSelection;b.anchorNode=d.anchorNode;b.anchorOffset=d.anchorOffset;b.focusNode=d.focusNode;b.focusOffset=d.focusOffset;b.isCollapsed=w(b)}else C(b)};else{K.fail("No means of obtaining a Range or TextRange from the user's selection was found");
+return false}g.refresh=function(b){var d=b?this._ranges.slice(0):null;$(this);if(b){b=d.length;if(b!=this._ranges.length)return false;for(;b--;)if(!k.rangesEqual(d[b],this._ranges[b]))return false;return true}};var ba=function(b,d){var h=b.getAllRanges(),D=false;b.removeAllRanges();for(var G=0,P=h.length;G<P;++G)if(D||d!==h[G])b.addRange(h[G]);else D=true;b.rangeCount||C(b)};g.removeRange=M?function(b){if(this.docSelection.type=="Control"){var d=this.docSelection.createRange();b=O(b);var h=c.getDocument(d.item(0));
+h=c.getBody(h).createControlRange();for(var D,G=false,P=0,X=d.length;P<X;++P){D=d.item(P);if(D!==b||G)h.add(d.item(P));else G=true}h.select();n(this)}else ba(this,b)}:function(b){ba(this,b)};var aa;if(!J&&W&&l.features.implementsDomRange){aa=function(b){var d=false;if(b.anchorNode)d=c.comparePoints(b.anchorNode,b.anchorOffset,b.focusNode,b.focusOffset)==1;return d};g.isBackwards=function(){return aa(this)}}else aa=g.isBackwards=function(){return false};g.toString=function(){for(var b=[],d=0,h=this.rangeCount;d<
+h;++d)b[d]=""+this._ranges[d];return b.join("")};g.collapse=function(b,d){q(this,b);var h=l.createRange(c.getDocument(b));h.collapseToPoint(b,d);this.removeAllRanges();this.addRange(h);this.isCollapsed=true};g.collapseToStart=function(){if(this.rangeCount){var b=this._ranges[0];this.collapse(b.startContainer,b.startOffset)}else throw new L("INVALID_STATE_ERR");};g.collapseToEnd=function(){if(this.rangeCount){var b=this._ranges[this.rangeCount-1];this.collapse(b.endContainer,b.endOffset)}else throw new L("INVALID_STATE_ERR");
+};g.selectAllChildren=function(b){q(this,b);var d=l.createRange(c.getDocument(b));d.selectNodeContents(b);this.removeAllRanges();this.addRange(d)};g.deleteFromDocument=function(){if(M&&V&&this.docSelection.type=="Control"){for(var b=this.docSelection.createRange(),d;b.length;){d=b.item(0);b.remove(d);d.parentNode.removeChild(d)}this.refresh()}else if(this.rangeCount){b=this.getAllRanges();this.removeAllRanges();d=0;for(var h=b.length;d<h;++d)b[d].deleteContents();this.addRange(b[h-1])}};g.getAllRanges=
+function(){return this._ranges.slice(0)};g.setSingleRange=function(b){this.setRanges([b])};g.containsNode=function(b,d){for(var h=0,D=this._ranges.length;h<D;++h)if(this._ranges[h].containsNode(b,d))return true;return false};g.toHtml=function(){var b="";if(this.rangeCount){b=k.getRangeDocument(this._ranges[0]).createElement("div");for(var d=0,h=this._ranges.length;d<h;++d)b.appendChild(this._ranges[d].cloneContents());b=b.innerHTML}return b};g.getName=function(){return"WrappedSelection"};g.inspect=
+function(){return v(this)};g.detach=function(){this.win=this.anchorNode=this.focusNode=this.win._rangySelection=null};x.inspect=v;l.Selection=x;l.selectionPrototype=g;l.addCreateMissingNativeApiListener(function(b){if(typeof b.getSelection=="undefined")b.getSelection=function(){return l.getSelection(this)};b=null})});
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-cssclassapplier.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-cssclassapplier.js
new file mode 100644
index 0000000..60064a3
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-cssclassapplier.js
@@ -0,0 +1,32 @@
+/*
+ CSS Class Applier module for Rangy.
+ Adds, removes and toggles CSS classes on Ranges and Selections
+
+ Part of Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Depends on Rangy core.
+
+ Copyright 2012, Tim Down
+ Licensed under the MIT license.
+ Version: 1.2.3
+ Build date: 26 February 2012
+*/
+rangy.createModule("CssClassApplier",function(i,v){function r(a,b){return a.className&&RegExp("(?:^|\\s)"+b+"(?:\\s|$)").test(a.className)}function s(a,b){if(a.className)r(a,b)||(a.className+=" "+b);else a.className=b}function o(a){return a.split(/\s+/).sort().join(" ")}function w(a,b){return o(a.className)==o(b.className)}function x(a){for(var b=a.parentNode;a.hasChildNodes();)b.insertBefore(a.firstChild,a);b.removeChild(a)}function y(a,b){var c=a.cloneRange();c.selectNodeContents(b);var d=c.intersection(a);
+d=d?d.toString():"";c.detach();return d!=""}function z(a){return a.getNodes([3],function(b){return y(a,b)})}function A(a,b){if(a.attributes.length!=b.attributes.length)return false;for(var c=0,d=a.attributes.length,e,f;c<d;++c){e=a.attributes[c];f=e.name;if(f!="class"){f=b.attributes.getNamedItem(f);if(e.specified!=f.specified)return false;if(e.specified&&e.nodeValue!==f.nodeValue)return false}}return true}function B(a,b){for(var c=0,d=a.attributes.length,e;c<d;++c){e=a.attributes[c].name;if(!(b&&
+h.arrayContains(b,e))&&a.attributes[c].specified&&e!="class")return true}return false}function C(a){var b;return a&&a.nodeType==1&&((b=a.parentNode)&&b.nodeType==9&&b.designMode=="on"||k(a)&&!k(a.parentNode))}function D(a){return(k(a)||a.nodeType!=1&&k(a.parentNode))&&!C(a)}function E(a){return a&&a.nodeType==1&&!M.test(p(a,"display"))}function N(a){if(a.data.length==0)return true;if(O.test(a.data))return false;switch(p(a.parentNode,"whiteSpace")){case "pre":case "pre-wrap":case "-moz-pre-wrap":return false;
+case "pre-line":if(/[\r\n]/.test(a.data))return false}return E(a.previousSibling)||E(a.nextSibling)}function m(a,b,c,d){var e,f=c==0;if(h.isAncestorOf(b,a))return a;if(h.isCharacterDataNode(b))if(c==0){c=h.getNodeIndex(b);b=b.parentNode}else if(c==b.length){c=h.getNodeIndex(b)+1;b=b.parentNode}else throw v.createError("splitNodeAt should not be called with offset in the middle of a data node ("+c+" in "+b.data);var g;g=b;var j=c;g=h.isCharacterDataNode(g)?j==0?!!g.previousSibling:j==g.length?!!g.nextSibling:
+true:j>0&&j<g.childNodes.length;if(g){if(!e){e=b.cloneNode(false);for(e.id&&e.removeAttribute("id");f=b.childNodes[c];)e.appendChild(f);h.insertAfter(e,b)}return b==a?e:m(a,e.parentNode,h.getNodeIndex(e),d)}else if(a!=b){e=b.parentNode;b=h.getNodeIndex(b);f||b++;return m(a,e,b,d)}return a}function F(a){var b=a?"nextSibling":"previousSibling";return function(c,d){var e=c.parentNode,f=c[b];if(f){if(f&&f.nodeType==3)return f}else if(d)if((f=e[b])&&f.nodeType==1&&e.tagName==f.tagName&&w(e,f)&&A(e,f))return f[a?
+"firstChild":"lastChild"];return null}}function t(a){this.firstTextNode=(this.isElementMerge=a.nodeType==1)?a.lastChild:a;this.textNodes=[this.firstTextNode]}function q(a,b,c){this.cssClass=a;var d,e,f=null;if(typeof b=="object"&&b!==null){c=b.tagNames;f=b.elementProperties;for(d=0;e=P[d++];)if(b.hasOwnProperty(e))this[e]=b[e];d=b.normalize}else d=b;this.normalize=typeof d=="undefined"?true:d;this.attrExceptions=[];d=document.createElement(this.elementTagName);this.elementProperties={};for(var g in f)if(f.hasOwnProperty(g)){if(G.hasOwnProperty(g))g=
+G[g];d[g]=f[g];this.elementProperties[g]=d[g];this.attrExceptions.push(g)}this.elementSortedClassName=this.elementProperties.hasOwnProperty("className")?o(this.elementProperties.className+" "+a):a;this.applyToAnyTagName=false;a=typeof c;if(a=="string")if(c=="*")this.applyToAnyTagName=true;else this.tagNames=c.toLowerCase().replace(/^\s\s*/,"").replace(/\s\s*$/,"").split(/\s*,\s*/);else if(a=="object"&&typeof c.length=="number"){this.tagNames=[];d=0;for(a=c.length;d<a;++d)if(c[d]=="*")this.applyToAnyTagName=
+true;else this.tagNames.push(c[d].toLowerCase())}else this.tagNames=[this.elementTagName]}i.requireModules(["WrappedSelection","WrappedRange"]);var h=i.dom,H=function(){function a(b,c,d){return c&&d?" ":""}return function(b,c){if(b.className)b.className=b.className.replace(RegExp("(?:^|\\s)"+c+"(?:\\s|$)"),a)}}(),p;if(typeof window.getComputedStyle!="undefined")p=function(a,b){return h.getWindow(a).getComputedStyle(a,null)[b]};else if(typeof document.documentElement.currentStyle!="undefined")p=function(a,
+b){return a.currentStyle[b]};else v.fail("No means of obtaining computed style properties found");var k;(function(){k=typeof document.createElement("div").isContentEditable=="boolean"?function(a){return a&&a.nodeType==1&&a.isContentEditable}:function(a){if(!a||a.nodeType!=1||a.contentEditable=="false")return false;return a.contentEditable=="true"||k(a.parentNode)}})();var M=/^inline(-block|-table)?$/i,O=/[^\r\n\t\f \u200B]/,Q=F(false),R=F(true);t.prototype={doMerge:function(){for(var a=[],b,c,d=0,
+e=this.textNodes.length;d<e;++d){b=this.textNodes[d];c=b.parentNode;a[d]=b.data;if(d){c.removeChild(b);c.hasChildNodes()||c.parentNode.removeChild(c)}}return this.firstTextNode.data=a=a.join("")},getLength:function(){for(var a=this.textNodes.length,b=0;a--;)b+=this.textNodes[a].length;return b},toString:function(){for(var a=[],b=0,c=this.textNodes.length;b<c;++b)a[b]="'"+this.textNodes[b].data+"'";return"[Merge("+a.join(",")+")]"}};var P=["elementTagName","ignoreWhiteSpace","applyToEditableOnly"],
+G={"class":"className"};q.prototype={elementTagName:"span",elementProperties:{},ignoreWhiteSpace:true,applyToEditableOnly:false,hasClass:function(a){return a.nodeType==1&&h.arrayContains(this.tagNames,a.tagName.toLowerCase())&&r(a,this.cssClass)},getSelfOrAncestorWithClass:function(a){for(;a;){if(this.hasClass(a,this.cssClass))return a;a=a.parentNode}return null},isModifiable:function(a){return!this.applyToEditableOnly||D(a)},isIgnorableWhiteSpaceNode:function(a){return this.ignoreWhiteSpace&&a&&
+a.nodeType==3&&N(a)},postApply:function(a,b,c){for(var d=a[0],e=a[a.length-1],f=[],g,j=d,I=e,J=0,K=e.length,n,L,l=0,u=a.length;l<u;++l){n=a[l];if(L=Q(n,!c)){if(!g){g=new t(L);f.push(g)}g.textNodes.push(n);if(n===d){j=g.firstTextNode;J=j.length}if(n===e){I=g.firstTextNode;K=g.getLength()}}else g=null}if(a=R(e,!c)){if(!g){g=new t(e);f.push(g)}g.textNodes.push(a)}if(f.length){l=0;for(u=f.length;l<u;++l)f[l].doMerge();b.setStart(j,J);b.setEnd(I,K)}},createContainer:function(a){a=a.createElement(this.elementTagName);
+i.util.extend(a,this.elementProperties);s(a,this.cssClass);return a},applyToTextNode:function(a){var b=a.parentNode;if(b.childNodes.length==1&&h.arrayContains(this.tagNames,b.tagName.toLowerCase()))s(b,this.cssClass);else{b=this.createContainer(h.getDocument(a));a.parentNode.insertBefore(b,a);b.appendChild(a)}},isRemovable:function(a){var b;if(b=a.tagName.toLowerCase()==this.elementTagName){if(b=o(a.className)==this.elementSortedClassName){var c;a:{b=this.elementProperties;for(c in b)if(b.hasOwnProperty(c)&&
+a[c]!==b[c]){c=false;break a}c=true}b=c&&!B(a,this.attrExceptions)&&this.isModifiable(a)}b=b}return b},undoToTextNode:function(a,b,c){if(!b.containsNode(c)){a=b.cloneRange();a.selectNode(c);if(a.isPointInRange(b.endContainer,b.endOffset)){m(c,b.endContainer,b.endOffset,[b]);b.setEndAfter(c)}if(a.isPointInRange(b.startContainer,b.startOffset))c=m(c,b.startContainer,b.startOffset,[b])}this.isRemovable(c)?x(c):H(c,this.cssClass)},applyToRange:function(a){a.splitBoundaries();var b=z(a);if(b.length){for(var c,
+d=0,e=b.length;d<e;++d){c=b[d];!this.isIgnorableWhiteSpaceNode(c)&&!this.getSelfOrAncestorWithClass(c)&&this.isModifiable(c)&&this.applyToTextNode(c)}a.setStart(b[0],0);c=b[b.length-1];a.setEnd(c,c.length);this.normalize&&this.postApply(b,a,false)}},applyToSelection:function(a){a=a||window;a=i.getSelection(a);var b,c=a.getAllRanges();a.removeAllRanges();for(var d=c.length;d--;){b=c[d];this.applyToRange(b);a.addRange(b)}},undoToRange:function(a){a.splitBoundaries();var b=z(a),c,d,e=b[b.length-1];if(b.length){for(var f=
+0,g=b.length;f<g;++f){c=b[f];(d=this.getSelfOrAncestorWithClass(c))&&this.isModifiable(c)&&this.undoToTextNode(c,a,d);a.setStart(b[0],0);a.setEnd(e,e.length)}this.normalize&&this.postApply(b,a,true)}},undoToSelection:function(a){a=a||window;a=i.getSelection(a);var b=a.getAllRanges(),c;a.removeAllRanges();for(var d=0,e=b.length;d<e;++d){c=b[d];this.undoToRange(c);a.addRange(c)}},getTextSelectedByRange:function(a,b){var c=b.cloneRange();c.selectNodeContents(a);var d=c.intersection(b);d=d?d.toString():
+"";c.detach();return d},isAppliedToRange:function(a){if(a.collapsed)return!!this.getSelfOrAncestorWithClass(a.commonAncestorContainer);else{for(var b=a.getNodes([3]),c=0,d;d=b[c++];)if(!this.isIgnorableWhiteSpaceNode(d)&&y(a,d)&&this.isModifiable(d)&&!this.getSelfOrAncestorWithClass(d))return false;return true}},isAppliedToSelection:function(a){a=a||window;a=i.getSelection(a).getAllRanges();for(var b=a.length;b--;)if(!this.isAppliedToRange(a[b]))return false;return true},toggleRange:function(a){this.isAppliedToRange(a)?
+this.undoToRange(a):this.applyToRange(a)},toggleSelection:function(a){this.isAppliedToSelection(a)?this.undoToSelection(a):this.applyToSelection(a)},detach:function(){}};q.util={hasClass:r,addClass:s,removeClass:H,hasSameClasses:w,replaceWithOwnChildren:x,elementsHaveSameNonClassAttributes:A,elementHasNonClassAttributes:B,splitNodeAt:m,isEditableElement:k,isEditingHost:C,isEditable:D};i.CssClassApplier=q;i.createCssClassApplier=function(a,b,c){return new q(a,b,c)}});
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-selectionsaverestore.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-selectionsaverestore.js
new file mode 100644
index 0000000..73e3070
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-selectionsaverestore.js
@@ -0,0 +1,19 @@
+/*
+ Selection save and restore module for Rangy.
+ Saves and restores user selections using marker invisible elements in the DOM.
+
+ Part of Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Depends on Rangy core.
+
+ Copyright 2012, Tim Down
+ Licensed under the MIT license.
+ Version: 1.2.3
+ Build date: 26 February 2012
+*/
+rangy.createModule("SaveRestore",function(h,m){function n(a,g){var e="selectionBoundary_"+ +new Date+"_"+(""+Math.random()).slice(2),c,f=p.getDocument(a.startContainer),d=a.cloneRange();d.collapse(g);c=f.createElement("span");c.id=e;c.style.lineHeight="0";c.style.display="none";c.className="rangySelectionBoundary";c.appendChild(f.createTextNode(q));d.insertNode(c);d.detach();return c}function o(a,g,e,c){if(a=(a||document).getElementById(e)){g[c?"setStartBefore":"setEndBefore"](a);a.parentNode.removeChild(a)}else m.warn("Marker element has been removed. Cannot restore selection.")}
+function r(a,g){return g.compareBoundaryPoints(a.START_TO_START,a)}function k(a,g){var e=(a||document).getElementById(g);e&&e.parentNode.removeChild(e)}h.requireModules(["DomUtil","DomRange","WrappedRange"]);var p=h.dom,q="\ufeff";h.saveSelection=function(a){a=a||window;var g=a.document;if(h.isSelectionValid(a)){var e=h.getSelection(a),c=e.getAllRanges(),f=[],d,j;c.sort(r);for(var b=0,i=c.length;b<i;++b){d=c[b];if(d.collapsed){j=n(d,false);f.push({markerId:j.id,collapsed:true})}else{j=n(d,false);
+d=n(d,true);f[b]={startMarkerId:d.id,endMarkerId:j.id,collapsed:false,backwards:c.length==1&&e.isBackwards()}}}for(b=i-1;b>=0;--b){d=c[b];if(d.collapsed)d.collapseBefore((g||document).getElementById(f[b].markerId));else{d.setEndBefore((g||document).getElementById(f[b].endMarkerId));d.setStartAfter((g||document).getElementById(f[b].startMarkerId))}}e.setRanges(c);return{win:a,doc:g,rangeInfos:f,restored:false}}else m.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.")};
+h.restoreSelection=function(a,g){if(!a.restored){for(var e=a.rangeInfos,c=h.getSelection(a.win),f=[],d=e.length,j=d-1,b,i;j>=0;--j){b=e[j];i=h.createRange(a.doc);if(b.collapsed)if(b=(a.doc||document).getElementById(b.markerId)){b.style.display="inline";var l=b.previousSibling;if(l&&l.nodeType==3){b.parentNode.removeChild(b);i.collapseToPoint(l,l.length)}else{i.collapseBefore(b);b.parentNode.removeChild(b)}}else m.warn("Marker element has been removed. Cannot restore selection.");else{o(a.doc,i,b.startMarkerId,
+true);o(a.doc,i,b.endMarkerId,false)}d==1&&i.normalizeBoundaries();f[j]=i}if(d==1&&g&&h.features.selectionHasExtend&&e[0].backwards){c.removeAllRanges();c.addRange(f[0],true)}else c.setRanges(f);a.restored=true}};h.removeMarkerElement=k;h.removeMarkers=function(a){for(var g=a.rangeInfos,e=0,c=g.length,f;e<c;++e){f=g[e];if(f.collapsed)k(a.doc,f.markerId);else{k(a.doc,f.startMarkerId);k(a.doc,f.endMarkerId)}}}});
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-serializer.js b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-serializer.js
new file mode 100644
index 0000000..72f19f6
--- /dev/null
+++ b/suite/pts/hostTests/browser/browserlauncher/assets/robohornet/third_party/rangy/rangy-serializer.js
@@ -0,0 +1,23 @@
+/*
+ Serializer module for Rangy.
+ Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
+ cookie or local storage and restore it on the user's next visit to the same page.
+
+ Part of Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Depends on Rangy core.
+
+ Copyright 2012, Tim Down
+ Licensed under the MIT license.
+ Version: 1.2.3
+ Build date: 26 February 2012
+*/
+rangy.createModule("Serializer",function(g,n){function o(c,a){a=a||[];var b=c.nodeType,e=c.childNodes,d=e.length,f=[b,c.nodeName,d].join(":"),h="",k="";switch(b){case 3:h=c.nodeValue.replace(/</g,"<").replace(/>/g,">");break;case 8:h="<!--"+c.nodeValue.replace(/</g,"<").replace(/>/g,">")+"--\>";break;default:h="<"+f+">";k="</>";break}h&&a.push(h);for(b=0;b<d;++b)o(e[b],a);k&&a.push(k);return a}function j(c){c=o(c).join("");return u(c).toString(16)}function l(c,a,b){var e=[],d=c;for(b=
+b||i.getDocument(c).documentElement;d&&d!=b;){e.push(i.getNodeIndex(d,true));d=d.parentNode}return e.join("/")+":"+a}function m(c,a,b){if(a)b||i.getDocument(a);else{b=b||document;a=b.documentElement}c=c.split(":");a=a;b=c[0]?c[0].split("/"):[];for(var e=b.length,d;e--;){d=parseInt(b[e],10);if(d<a.childNodes.length)a=a.childNodes[parseInt(b[e],10)];else throw n.createError("deserializePosition failed: node "+i.inspectNode(a)+" has no child with index "+d+", "+e);}return new i.DomPosition(a,parseInt(c[1],
+10))}function p(c,a,b){b=b||g.DomRange.getRangeDocument(c).documentElement;if(!i.isAncestorOf(b,c.commonAncestorContainer,true))throw Error("serializeRange: range is not wholly contained within specified root node");c=l(c.startContainer,c.startOffset,b)+","+l(c.endContainer,c.endOffset,b);a||(c+="{"+j(b)+"}");return c}function q(c,a,b){if(a)b=b||i.getDocument(a);else{b=b||document;a=b.documentElement}c=/^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(c);var e=c[4],d=j(a);if(e&&e!==j(a))throw Error("deserializeRange: checksums of serialized range root node ("+
+e+") and target root node ("+d+") do not match");e=m(c[1],a,b);a=m(c[2],a,b);b=g.createRange(b);b.setStart(e.node,e.offset);b.setEnd(a.node,a.offset);return b}function r(c,a,b){if(a)b||i.getDocument(a);else{b=b||document;a=b.documentElement}c=/^([^,]+),([^,]+)({([^}]+)})?$/.exec(c)[3];return!c||c===j(a)}function s(c,a,b){c=c||g.getSelection();c=c.getAllRanges();for(var e=[],d=0,f=c.length;d<f;++d)e[d]=p(c[d],a,b);return e.join("|")}function t(c,a,b){if(a)b=b||i.getWindow(a);else{b=b||window;a=b.document.documentElement}c=
+c.split("|");for(var e=g.getSelection(b),d=[],f=0,h=c.length;f<h;++f)d[f]=q(c[f],a,b.document);e.setRanges(d);return e}g.requireModules(["WrappedSelection","WrappedRange"]);if(typeof encodeURIComponent=="undefined"||typeof decodeURIComponent=="undefined")n.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");var u=function(){var c=null;return function(a){for(var b=[],e=0,d=a.length,f;e<d;++e){f=a.charCodeAt(e);if(f<128)b.push(f);else f<2048?b.push(f>>6|192,f&63|128):
+b.push(f>>12|224,f>>6&63|128,f&63|128)}a=-1;if(!c){e=[];d=0;for(var h;d<256;++d){h=d;for(f=8;f--;)if((h&1)==1)h=h>>>1^3988292384;else h>>>=1;e[d]=h>>>0}c=e}e=c;d=0;for(f=b.length;d<f;++d){h=(a^b[d])&255;a=a>>>8^e[h]}return(a^-1)>>>0}}(),i=g.dom;g.serializePosition=l;g.deserializePosition=m;g.serializeRange=p;g.deserializeRange=q;g.canDeserializeRange=r;g.serializeSelection=s;g.deserializeSelection=t;g.canDeserializeSelection=function(c,a,b){var e;if(a)e=b?b.document:i.getDocument(a);else{b=b||window;
+a=b.document.documentElement}c=c.split("|");b=0;for(var d=c.length;b<d;++b)if(!r(c[b],a,e))return false;return true};g.restoreSelectionFromCookie=function(c){c=c||window;var a;a:{a=c.document.cookie.split(/[;,]/);for(var b=0,e=a.length,d;b<e;++b){d=a[b].split("=");if(d[0].replace(/^\s+/,"")=="rangySerializedSelection")if(d=d[1]){a=decodeURIComponent(d.replace(/\s+$/,""));break a}}a=null}a&&t(a,c.doc)};g.saveSelectionCookie=function(c,a){c=c||window;a=typeof a=="object"?a:{};var b=a.expires?";expires="+
+a.expires.toUTCString():"",e=a.path?";path="+a.path:"",d=a.domain?";domain="+a.domain:"",f=a.secure?";secure":"",h=s(g.getSelection(c));c.document.cookie=encodeURIComponent("rangySerializedSelection")+"="+encodeURIComponent(h)+b+e+d+f};g.getElementChecksum=j});
\ No newline at end of file
diff --git a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java b/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
index a9e48f3..4667903 100644
--- a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
+++ b/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
@@ -35,8 +35,10 @@
public class LaunchBrowserTest extends PtsAndroidTestCase {
private static final String OCTANE_START_FILE = "octane/index.html";
+ private static final String ROBOHORNET_START_FILE = "robohornet/robohornet.html";
private static final String HOST_COMPLETION_BROADCAST = "com.android.pts.browser.completion";
- private static long BROWSER_COMPLETION_TIMEOUT = 10 * 60;
+ // host-side will time-out anyway. So make it long enough.
+ private static long BROWSER_COMPLETION_TIMEOUT = 60 * 60;
private CtsTestServer mWebServer;
private HostBroadcastReceiver mReceiver;
@@ -65,6 +67,15 @@
mReceiver.waitForBroadcast(BROWSER_COMPLETION_TIMEOUT);
}
+ public void testRoboHornet() throws InterruptedException {
+ String url = mWebServer.getAssetUrl(ROBOHORNET_START_FILE);
+ Uri uri = Uri.parse(url);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ mReceiver.waitForBroadcast(BROWSER_COMPLETION_TIMEOUT);
+ }
+
class HostBroadcastReceiver extends BroadcastReceiver {
private final Semaphore mSemaphore = new Semaphore(0);
diff --git a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
index 54f2eb0..5865eba 100644
--- a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
+++ b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
@@ -22,7 +22,8 @@
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.pts.ptsutil.LogcatLineReceiver;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.tradefed.build.IBuildInfo;
@@ -50,9 +51,10 @@
private static final String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
private static final String PACKAGE = "com.android.pts.browser";
private static final String APK = "PtsDeviceBrowserLauncher.apk";
- private static final long LOGCAT_TIMEOUT_IN_SEC = 10 * 60L;
private static final String LOGCAT_FILTER = " browser:D chromium:D *:S";
private static final long REBOOT_WAIT_TIME_IN_MS = 2 * 60 * 1000L;
+ private static final int OCTANE_TIMEOUT_IN_MS = 10 * 60 * 1000;
+ private static final int ROBOHORNET_TIMEOUT_IN_MS = 40 * 60 * 1000;
private CtsBuildHelper mBuild;
private ITestDevice mDevice;
@@ -60,6 +62,7 @@
private ReportLog mReport;
private volatile boolean mIgnoreLine = true;
+ private volatile boolean mResultReceived = false;
private double mResult;
@Override
@@ -88,29 +91,64 @@
public void testOctane() throws Exception {
String resultPattern = "([A-Z][\\d\\w]+): ([\\d]+)";
String summaryPattern = "(Octane Score.*): ([\\d]+)";
- int numberRepeat = 5;
+ final int numberRepeat = 5;
double[] results = new double[numberRepeat];
for (int i = 0; i < numberRepeat; i++) {
Log.i(TAG, i + "-th round");
// browser will not refresh if the page is already loaded.
mDevice.reboot();
Thread.sleep(REBOOT_WAIT_TIME_IN_MS);
- results[i] = runBenchmarking("testOctane", resultPattern, summaryPattern);
+ results[i] = runBenchmarking("testOctane", resultPattern, summaryPattern,
+ OCTANE_TIMEOUT_IN_MS,
+ ResultType.HIGHER_BETTER, ResultUnit.SCORE,
+ ResultType.HIGHER_BETTER, ResultUnit.SCORE);
}
- mReport.printArray("scores", results, true);
+ mReport.printArray("scores", results, ResultType.HIGHER_BETTER,
+ ResultUnit.SCORE);
Stat.StatResult stat = Stat.getStat(results);
// Octane score is higher-better
- mReport.printSummary("Score", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
+ mReport.printSummary("Octane score", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.SCORE);
+ }
+
+ @TimeoutReq(minutes = 40)
+ public void testRoboHornet() throws Exception {
+ String resultPattern = "([A-Z][\\d\\w]+):([\\d]+[\\.]?[\\d]*)ms";
+ String summaryPattern = "(RoboHornet Score):([\\d]+[\\.]?[\\d]*)";
+ // currently robohornet takes too long to repeat
+ final int numberRepeat = 1;
+ double[] results = new double[numberRepeat];
+ for (int i = 0; i < numberRepeat; i++) {
+ Log.i(TAG, i + "-th round");
+ // browser will not refresh if the page is already loaded.
+ mDevice.reboot();
+ Thread.sleep(REBOOT_WAIT_TIME_IN_MS);
+ results[i] = runBenchmarking("testRoboHornet", resultPattern, summaryPattern,
+ ROBOHORNET_TIMEOUT_IN_MS,
+ ResultType.LOWER_BETTER, ResultUnit.MS,
+ ResultType.HIGHER_BETTER, ResultUnit.SCORE);
+ }
+ mReport.printArray("time for each test", results, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ Stat.StatResult stat = Stat.getStat(results);
+ mReport.printSummary("Robohornet Score", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.SCORE);
}
private double runBenchmarking(String testMethodName, String resultPattern,
- String summaryPattern) throws DeviceNotAvailableException, InterruptedException {
+ String summaryPattern, int timeOutInMs,
+ ResultType resultType, ResultUnit resultUnit,
+ ResultType summaryType, ResultUnit summaryUnit)
+ throws DeviceNotAvailableException, InterruptedException {
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(PACKAGE, CTS_RUNNER,
mDevice.getIDevice());
+ testRunner.setMaxtimeToOutputResponse(timeOutInMs);
testRunner.setMethodName("com.android.pts.browser.LaunchBrowserTest", testMethodName);
CollectingTestListener listener = new CollectingTestListener();
mIgnoreLine = true;
- startLogMonitoring(resultPattern, summaryPattern);
+ mResultReceived = false;
+ startLogMonitoring(resultPattern, summaryPattern, timeOutInMs, resultType, resultUnit,
+ summaryType, summaryUnit);
// hack to ignore already captured logcat as the monitor will get it again.
// Checking time and skipping may be a better logic, but simply throwing away also works.
Thread.sleep(5000);
@@ -125,14 +163,17 @@
fail("maybe timeout");
}
stopLogMonitoring();
+ assertTrue("No result found", mResultReceived);
return mResult;
}
- void startLogMonitoring(String resultPattern, String summaryPattern)
+ void startLogMonitoring(String resultPattern, String summaryPattern, int timeOutInMs,
+ final ResultType resultType, final ResultUnit resultUnit,
+ final ResultType summaryType, final ResultUnit summaryUnit)
throws InterruptedException, DeviceNotAvailableException {
final Pattern result = Pattern.compile(resultPattern);
final Pattern summary = Pattern.compile(summaryPattern);
- mReceiver = new LogcatLineReceiver(mDevice, LOGCAT_FILTER, LOGCAT_TIMEOUT_IN_SEC) {
+ mReceiver = new LogcatLineReceiver(mDevice, LOGCAT_FILTER, timeOutInMs / 1000) {
@Override
public void processALine(String line) throws DeviceNotAvailableException {
Log.i(TAG, "processALine " + line + " ignore " + mIgnoreLine);
@@ -142,13 +183,16 @@
Matcher matchResult = result.matcher(line);
if (matchResult.find()) {
mReport.printValue(matchResult.group(1),
- Double.parseDouble(matchResult.group(2)));
+ Double.parseDouble(matchResult.group(2)),
+ resultType, resultUnit);
}
Matcher matchSummary = summary.matcher(line);
if (matchSummary.find()) {
mResult = Double.parseDouble(matchSummary.group(2));
- mReport.printValue(matchSummary.group(1), mResult);
+ mReport.printValue(matchSummary.group(1), mResult,
+ summaryType, summaryUnit);
mDevice.executeShellCommand(BROADCAST_CMD);
+ mResultReceived = true;
}
}
};
diff --git a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
index da9d8e2..5147eaf 100644
--- a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
+++ b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
@@ -27,7 +27,8 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.Stat;
@@ -82,10 +83,11 @@
}
}
});
- getReportLog().printArray("ms", results, false);
+ getReportLog().printArray("taskswitching time", results, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
- stat.mStddev);
+ getReportLog().printSummary("taskswitching time", stat.mAverage,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
}
private void startActivity(String packageName, String activityName) {
diff --git a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
index dedf19c..056e0f4 100644
--- a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
+++ b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
@@ -16,38 +16,26 @@
package com.android.pts.uihost;
-import android.cts.util.TimeoutReq;
-
import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PerfResultType;
-import com.android.pts.util.PtsException;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.Stat.StatResult;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceOptions;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestResult;
-import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import java.io.File;
-import java.util.Map;
+
/**
* Test to measure installation time of a APK.
*/
public class InstallTimeTest extends DeviceTestCase implements IBuildReceiver {
- private static final String TAG = "InstallTimeTest";
- private final static String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
private CtsBuildHelper mBuild;
private ITestDevice mDevice;
private ReportLog mReport = null;
@@ -90,10 +78,11 @@
device.installPackage(app, false);
}
});
- mReport.printArray("time in ms", result, false);
+ mReport.printArray("install time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER,
- stat.mStddev);
+ mReport.printSummary("install time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
deleted file mode 100644
index 0d0f13d..0000000
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.pts.util;
-
-/**
- * Enum for distinguish performance results.
- * HIGHER_BETTER: better performance with higher value
- * LOWER_BETTER: better performance with lower value
- *
- */
-public enum PerfResultType {
- LOWER_BETTER,
- HIGHER_BETTER;
-}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
index 721a196..eff8508 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
@@ -25,11 +25,9 @@
* For now, throws know exception with message.
*
* Format:
- * LOG_SEPARATOR : separates each log
- * Message = log [LOG_SEPARATOR log]*
- * log for single value = classMethodName:line_number|header|d|value
- * log for array = classMethodName:line_number|header|da|values|
- * average average_value min|max value stddev value
+ * Message = summary log SUMMARY_SEPARATOR [LOG_SEPARATOR log]*
+ * summary = message|unit|type|value
+ * log for array = classMethodName:line_number|message|unit|type|space separated values
*/
public class ReportLog {
private static final String LOG_SEPARATOR = "+++";
@@ -41,65 +39,49 @@
protected static int mDepth = 3;
/**
- * print given value to the report
- * @param header string to explain the contents. It can be unit for the value.
- * @param val
+ * print array of values to output log
*/
- public void printValue(String header, double val) {
- String message = getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
- LOG_ELEM_SEPARATOR + "d" + LOG_ELEM_SEPARATOR + val;
- mMessages.add(message);
- printLog(message);
+ public void printArray(String message, double[] values, ResultType type,
+ ResultUnit unit) {
+ doPrintArray(message, values, type, unit);
}
/**
- * array version of printValue
- * @param header
- * @param val
- * @param addMin add minimum to the result. If false, add maximum to the result
+ * Print a value to output log
*/
- public void printArray(String header, double[] val, boolean addMin) {
+ public void printValue(String message, double value, ResultType type,
+ ResultUnit unit) {
+ double[] vals = { value };
+ doPrintArray(message, vals, type, unit);
+ }
+
+ private void doPrintArray(String message, double[] values, ResultType type,
+ ResultUnit unit) {
StringBuilder builder = new StringBuilder();
- builder.append(getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
- LOG_ELEM_SEPARATOR + "da" + LOG_ELEM_SEPARATOR);
- for (double v : val) {
+ // note mDepth + 1 as this function will be called by printVaue or printArray
+ // and we need caller of printValue / printArray
+ builder.append(getClassMethodNames(mDepth + 1, true) + LOG_ELEM_SEPARATOR + message +
+ LOG_ELEM_SEPARATOR + type.getXmlString() + LOG_ELEM_SEPARATOR +
+ unit.getXmlString() + LOG_ELEM_SEPARATOR);
+ for (double v : values) {
builder.append(v);
builder.append(" ");
}
- Stat.StatResult stat = Stat.getStat(val);
- builder.append(LOG_ELEM_SEPARATOR + "average " + stat.mAverage +
- (addMin ? (" min " + stat.mMin) : (" max " + stat.mMax)) + " stddev " + stat.mStddev);
mMessages.add(builder.toString());
printLog(builder.toString());
}
/**
* For standard report summary with average and stddev
- * @param header
- * @param average
+ * @param messsage
+ * @param value
* @param type type of average value. stddev does not need type.
- * @param stddev
+ * @param unit unit of the data
*/
- public void printSummary(String header, double average, PerfResultType type, double stddev) {
- mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + " " + type.ordinal() +
- LOG_ELEM_SEPARATOR + "stddev " + stddev + " " +
- PerfResultType.LOWER_BETTER.ordinal();
- }
-
- /**
- * For a report with two representative values with custom key strings
- * @param header
- * @param key1 String key for val1
- * @param val1
- * @param val1type
- * @param key2 String key for val2
- * @param val2
- * @param val2type
- */
- public void printSummaryFull(String header, String key1, double val1, PerfResultType val1type,
- String key2, double val2, PerfResultType val2type) {
- mSummary = header + LOG_ELEM_SEPARATOR + key1 + " " + val1 + " " + val1type.ordinal() +
- LOG_ELEM_SEPARATOR + key2 + " " + val2 + " " + val2type.ordinal();
+ public void printSummary(String message, double value, ResultType type,
+ ResultUnit unit) {
+ mSummary = message + LOG_ELEM_SEPARATOR + type.getXmlString() + LOG_ELEM_SEPARATOR +
+ unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
}
public void throwReportToHost() throws PtsException {
@@ -139,9 +121,6 @@
/**
* array version of calcRatePerSecArray
- * @param change
- * @param timeInMSec
- * @return
*/
public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
double[] result = new double[timeInMSec.length];
@@ -159,9 +138,6 @@
/**
* copy array from src to dst with given offset in dst.
* dst should be big enough to hold src
- * @param src
- * @param dst
- * @param dstOffset
*/
public static void copyArray(double[] src, double[] dst, int dstOffset) {
for (int i = 0; i < src.length; i++) {
@@ -171,8 +147,6 @@
/**
* get classname.methodname from call stack of the current thread
- *
- * @return
*/
public static String getClassMethodNames() {
return getClassMethodNames(mDepth, false);
@@ -180,14 +154,13 @@
private static String getClassMethodNames(int depth, boolean addLineNumber) {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
- String names = elements[depth].getClassName() + "." + elements[depth].getMethodName() +
+ String names = elements[depth].getClassName() + "#" + elements[depth].getMethodName() +
(addLineNumber ? ":" + elements[depth].getLineNumber() : "");
return names;
}
/**
* to be overridden by child to print message to be passed
- * @param msg
*/
protected void printLog(String msg) {
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
new file mode 100644
index 0000000..1cbb522
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.util;
+
+/**
+ * Enum for distinguishing performance results.
+ */
+public enum ResultType {
+ /** lower score shows better performance */
+ LOWER_BETTER("lowerBetter"),
+ /** higher score shows better performance */
+ HIGHER_BETTER("higherBetter"),
+ /** This value is not directly correlated with performance. */
+ NEUTRAL("neutral");
+
+ final private String mXmlString;
+ ResultType(String xmlString) {
+ mXmlString = xmlString;
+ }
+
+ /**
+ * Return string used in CTS XML report
+ */
+ public String getXmlString() {
+ return mXmlString;
+ }
+}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
new file mode 100644
index 0000000..b234cb5
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.util;
+
+/**
+ * Enum for representing the unit of performance results.
+ *
+ */
+public enum ResultUnit {
+ /** for value with no unit */
+ NONE,
+ /** milli-seconds */
+ MS,
+ /** frames per second */
+ FPS,
+ /** operations per second */
+ OPS,
+ /** kilo-bytes-per-second, not bits-per-second */
+ KBPS,
+ /** mega-bytes-per-second */
+ MBPS,
+ /** amount of data, bytes */
+ BYTE,
+ /** tell how many times it did happen. */
+ COUNT,
+ /** unit for benchmarking with generic score. */
+ SCORE;
+
+ /**
+ * Return string used in CTS XML report
+ */
+ public String getXmlString() {
+ return name().toLowerCase();
+ }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/EncoderTest.java b/tests/tests/media/src/android/media/cts/EncoderTest.java
new file mode 100644
index 0000000..e9d0b5f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EncoderTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+public class EncoderTest extends AndroidTestCase {
+ private static final String TAG = "EncoderTest";
+ private static final boolean VERBOSE = false;
+
+ private static final int kNumInputBytes = 256 * 1024;
+ private static final long kTimeoutUs = 10000;
+
+ @Override
+ public void setContext(Context context) {
+ super.setContext(context);
+ }
+
+ public void testAMRNBEncoders() {
+ LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
+
+ final int kBitRates[] =
+ { 4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200 };
+
+ for (int j = 0; j < kBitRates.length; ++j) {
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, "audio/3gpp");
+ format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
+ formats.push(format);
+ }
+
+ testEncoderWithFormats("audio/3gpp", formats);
+ }
+
+ public void testAMRWBEncoders() {
+ LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
+
+ final int kBitRates[] =
+ { 6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850 };
+
+ for (int j = 0; j < kBitRates.length; ++j) {
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, "audio/amr-wb");
+ format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 16000);
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
+ formats.push(format);
+ }
+
+ testEncoderWithFormats("audio/amr-wb", formats);
+ }
+
+ public void testAACEncoders() {
+ LinkedList<MediaFormat> formats = new LinkedList<MediaFormat>();
+
+ final int kAACProfiles[] = {
+ 2 /* OMX_AUDIO_AACObjectLC */,
+ 5 /* OMX_AUDIO_AACObjectHE */,
+ 39 /* OMX_AUDIO_AACObjectELD */
+ };
+
+ final int kSampleRates[] = { 8000, 11025, 22050, 44100, 48000 };
+ final int kBitRates[] = { 64000, 128000 };
+
+ for (int k = 0; k < kAACProfiles.length; ++k) {
+ for (int i = 0; i < kSampleRates.length; ++i) {
+ if (kAACProfiles[k] == 5 && kSampleRates[i] < 22050) {
+ // Is this right? HE does not support sample rates < 22050Hz?
+ continue;
+ }
+ for (int j = 0; j < kBitRates.length; ++j) {
+ for (int ch = 1; ch <= 2; ++ch) {
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
+
+ format.setInteger(
+ MediaFormat.KEY_AAC_PROFILE, kAACProfiles[k]);
+
+ format.setInteger(
+ MediaFormat.KEY_SAMPLE_RATE, kSampleRates[i]);
+
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, ch);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates[j]);
+ formats.push(format);
+ }
+ }
+ }
+ }
+
+ testEncoderWithFormats("audio/mp4a-latm", formats);
+ }
+
+ private void testEncoderWithFormats(
+ String mime, List<MediaFormat> formats) {
+ List<String> componentNames = getEncoderNamesForType(mime);
+
+ for (String componentName : componentNames) {
+ Log.d(TAG, "testing component '" + componentName + "'");
+ for (MediaFormat format : formats) {
+ Log.d(TAG, " testing format '" + format + "'");
+ assertEquals(mime, format.getString(MediaFormat.KEY_MIME));
+ testEncoder(componentName, format);
+ }
+ }
+ }
+
+ private List<String> getEncoderNamesForType(String mime) {
+ LinkedList<String> names = new LinkedList<String>();
+
+ int n = MediaCodecList.getCodecCount();
+ for (int i = 0; i < n; ++i) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+
+ if (!info.isEncoder()) {
+ continue;
+ }
+
+ if (!info.getName().startsWith("OMX.")) {
+ // Unfortunately for legacy reasons, "AACEncoder", a
+ // non OMX component had to be in this list for the video
+ // editor code to work... but it cannot actually be instantiated
+ // using MediaCodec.
+ Log.d(TAG, "skipping '" + info.getName() + "'.");
+ continue;
+ }
+
+ String[] supportedTypes = info.getSupportedTypes();
+
+ for (int j = 0; j < supportedTypes.length; ++j) {
+ if (supportedTypes[j].equalsIgnoreCase(mime)) {
+ names.push(info.getName());
+ break;
+ }
+ }
+ }
+
+ return names;
+ }
+
+ private int queueInputBuffer(
+ MediaCodec codec, ByteBuffer[] inputBuffers, int index) {
+ ByteBuffer buffer = inputBuffers[index];
+ buffer.clear();
+
+ int size = buffer.limit();
+
+ byte[] zeroes = new byte[size];
+ buffer.put(zeroes);
+
+ codec.queueInputBuffer(index, 0 /* offset */, size, 0 /* timeUs */, 0);
+
+ return size;
+ }
+
+ private void dequeueOutputBuffer(
+ MediaCodec codec, ByteBuffer[] outputBuffers,
+ int index, MediaCodec.BufferInfo info) {
+ codec.releaseOutputBuffer(index, false /* render */);
+ }
+
+ private void testEncoder(String componentName, MediaFormat format) {
+ MediaCodec codec = MediaCodec.createByCodecName(componentName);
+
+ try {
+ codec.configure(
+ format,
+ null /* surface */,
+ null /* crypto */,
+ MediaCodec.CONFIGURE_FLAG_ENCODE);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "codec '" + componentName + "' failed configuration.");
+
+ assertTrue("codec '" + componentName + "' failed configuration.", false);
+ }
+
+ codec.start();
+ ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+ ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+ int numBytesSubmitted = 0;
+ boolean doneSubmittingInput = false;
+ int numBytesDequeued = 0;
+
+ while (true) {
+ int index;
+
+ if (!doneSubmittingInput) {
+ index = codec.dequeueInputBuffer(kTimeoutUs /* timeoutUs */);
+
+ if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (numBytesSubmitted >= kNumInputBytes) {
+ codec.queueInputBuffer(
+ index,
+ 0 /* offset */,
+ 0 /* size */,
+ 0 /* timeUs */,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+
+ if (VERBOSE) {
+ Log.d(TAG, "queued input EOS.");
+ }
+
+ doneSubmittingInput = true;
+ } else {
+ int size = queueInputBuffer(
+ codec, codecInputBuffers, index);
+
+ numBytesSubmitted += size;
+
+ if (VERBOSE) {
+ Log.d(TAG, "queued " + size + " bytes of input data.");
+ }
+ }
+ }
+ }
+
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ index = codec.dequeueOutputBuffer(info, kTimeoutUs /* timeoutUs */);
+
+ if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+ } else {
+ dequeueOutputBuffer(codec, codecOutputBuffers, index, info);
+
+ numBytesDequeued += info.size;
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) {
+ Log.d(TAG, "dequeued output EOS.");
+ }
+ break;
+ }
+
+ if (VERBOSE) {
+ Log.d(TAG, "dequeued " + info.size + " bytes of output data.");
+ }
+ }
+ }
+
+ if (VERBOSE) {
+ Log.d(TAG, "queued a total of " + numBytesSubmitted + "bytes, "
+ + "dequeued " + numBytesDequeued + " bytes.");
+ }
+
+ int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ int inBitrate = sampleRate * channelCount * 16; // bit/sec
+ int outBitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
+
+ float desiredRatio = (float)outBitrate / (float)inBitrate;
+ float actualRatio = (float)numBytesDequeued / (float)numBytesSubmitted;
+
+ if (actualRatio < 0.9 * desiredRatio || actualRatio > 1.1 * desiredRatio) {
+ Log.w(TAG, "desiredRatio = " + desiredRatio
+ + ", actualRatio = " + actualRatio);
+ }
+
+ codec.release();
+ codec = null;
+ }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index e6abe31..0973c1a 100755
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -17,10 +17,11 @@
package android.media.cts;
-import android.media.MediaCodecList;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -53,6 +54,63 @@
assertTrue("/etc/media_codecs.xml does not exist", file.exists());
}
+ // Each component advertised by MediaCodecList should at least be
+ // instantiate-able.
+ public void testComponentInstantiation() {
+ Log.d(TAG, "testComponentInstantiation");
+
+ int codecCount = MediaCodecList.getCodecCount();
+ for (int i = 0; i < codecCount; ++i) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+
+ Log.d(TAG, (i + 1) + ": " + info.getName());
+ Log.d(TAG, " isEncoder = " + info.isEncoder());
+
+ if (!info.getName().startsWith("OMX.")) {
+ // Unfortunately for legacy reasons, "AACEncoder", a
+ // non OMX component had to be in this list for the video
+ // editor code to work... but it cannot actually be instantiated
+ // using MediaCodec.
+ Log.d(TAG, " skipping...");
+ continue;
+ }
+
+ MediaCodec codec = MediaCodec.createByCodecName(info.getName());
+
+ codec.release();
+ codec = null;
+ }
+ }
+
+ // For each type advertised by any of the components we should be able
+ // to get capabilities.
+ public void testGetCapabilities() {
+ Log.d(TAG, "testGetCapabilities");
+
+ int codecCount = MediaCodecList.getCodecCount();
+ for (int i = 0; i < codecCount; ++i) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+
+ Log.d(TAG, (i + 1) + ": " + info.getName());
+ Log.d(TAG, " isEncoder = " + info.isEncoder());
+
+ if (!info.getName().startsWith("OMX.")) {
+ // Unfortunately for legacy reasons, "AACEncoder", a
+ // non OMX component had to be in this list for the video
+ // editor code to work... but it cannot actually be instantiated
+ // using MediaCodec.
+ Log.d(TAG, " skipping...");
+ continue;
+ }
+
+ String[] types = info.getSupportedTypes();
+ for (int j = 0; j < types.length; ++j) {
+ Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
+ CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+ }
+ }
+ }
+
public void testRequiredMediaCodecList() {
List<CodecType> requiredList = getRequiredCodecTypes();
List<CodecType> supportedList = getSupportedCodecTypes();
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
index f714a07..c13f041 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
@@ -22,6 +22,12 @@
import android.os.Environment;
import android.test.AndroidTestCase;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
public class MediaScannerNotificationTest extends AndroidTestCase {
public void testMediaScannerNotification() throws Exception {
@@ -38,10 +44,81 @@
mContext.registerReceiver(startedReceiver, startedIntentFilter);
mContext.registerReceiver(finishedReceiver, finshedIntentFilter);
+ String [] temps = new String[] { "avi", "gif", "jpg", "dat", "mp3", "mp4", "txt" };
+ String tmpPath = createTempFiles(temps);
+
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
startedReceiver.waitForBroadcast();
finishedReceiver.waitForBroadcast();
+
+ checkTempFiles(tmpPath, temps);
+
+ // add .nomedia file and scan again
+ File noMedia = new File(tmpPath, ".nomedia");
+ try {
+ noMedia.createNewFile();
+ } catch (IOException e) {
+ fail("couldn't create .nomedia file");
+ }
+ startedReceiver.reset();
+ finishedReceiver.reset();
+ mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
+ + Environment.getExternalStorageDirectory())));
+ startedReceiver.waitForBroadcast();
+ finishedReceiver.waitForBroadcast();
+
+ checkTempFiles(tmpPath, temps);
+ assertTrue(noMedia.delete());
+ deleteTempFiles(tmpPath, temps);
+
+ // scan one more time just to clean everything up nicely
+ startedReceiver.reset();
+ finishedReceiver.reset();
+ mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
+ + Environment.getExternalStorageDirectory())));
+ startedReceiver.waitForBroadcast();
+ finishedReceiver.waitForBroadcast();
+
+ }
+
+ String createTempFiles(String [] extensions) {
+ String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ File tmpDir = new File(externalPath, "" + System.nanoTime());
+ String tmpPath = tmpDir.getAbsolutePath();
+ assertFalse(tmpPath + " already exists", tmpDir.exists());
+ assertTrue("failed to create " + tmpDir, tmpDir.mkdirs());
+
+ for (int i = 0; i < extensions.length; i++) {
+ File foo = new File(tmpPath, "foobar." + extensions[i]);
+ try {
+ // create a non-empty file
+ foo.createNewFile();
+ FileOutputStream out = new FileOutputStream(foo);
+ out.write(0x12);
+ out.flush();
+ out.close();
+ assertTrue(foo.length() != 0);
+ } catch (IOException e) {
+ fail("Error creating " + foo.getAbsolutePath() + ": " + e);
+ }
+ }
+ return tmpPath;
+ }
+
+ void checkTempFiles(String tmpPath, String [] extensions) {
+ for (int i = 0; i < extensions.length; i++) {
+ File foo = new File(tmpPath, "foobar." + extensions[i]);
+ assertTrue(foo.getAbsolutePath() + " no longer exists or was truncated",
+ foo.length() != 0);
+ }
+ }
+
+ void deleteTempFiles(String tmpPath, String [] extensions) {
+ for (int i = 0; i < extensions.length; i++) {
+ assertTrue(new File(tmpPath, "foobar." + extensions[i]).delete());
+ }
+ assertTrue(new File(tmpPath).delete());
}
}
diff --git a/tests/tests/media/src/android/media/cts/ScannerNotificationReceiver.java b/tests/tests/media/src/android/media/cts/ScannerNotificationReceiver.java
index 4357bee..9d91671 100644
--- a/tests/tests/media/src/android/media/cts/ScannerNotificationReceiver.java
+++ b/tests/tests/media/src/android/media/cts/ScannerNotificationReceiver.java
@@ -30,7 +30,7 @@
private static final int TIMEOUT_MS = 4 * 60 * 1000;
private final String mAction;
- private final CountDownLatch mLatch = new CountDownLatch(1);
+ private CountDownLatch mLatch = new CountDownLatch(1);
ScannerNotificationReceiver(String action) {
mAction = action;
@@ -51,6 +51,10 @@
}
}
+ void reset() {
+ mLatch = new CountDownLatch(1);
+ }
+
private int countFiles(File dir) {
int count = 0;
File[] files = dir.listFiles();
@@ -65,4 +69,4 @@
}
return count;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 42991d8..4010d41 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -435,13 +435,6 @@
assertEquals(WebSettings.LayoutAlgorithm.SINGLE_COLUMN, mSettings.getLayoutAlgorithm());
}
- public void testAccessLightTouchEnabled() {
- assertFalse(mSettings.getLightTouchEnabled());
-
- mSettings.setLightTouchEnabled(true);
- assertTrue(mSettings.getLightTouchEnabled());
- }
-
public void testAccessMinimumFontSize() {
assertEquals(8, mSettings.getMinimumFontSize());
diff --git a/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java b/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
index e583dce..5426ae9 100644
--- a/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
@@ -81,6 +81,21 @@
HeaderViewFullAdapter fullAdapter = new HeaderViewFullAdapter();
headerViewListAdapter = new HeaderViewListAdapter(null, null, fullAdapter);
assertFalse(headerViewListAdapter.isEmpty());
+
+ ListView lv = new ListView(getContext());
+ ArrayList<ListView.FixedViewInfo> header = new ArrayList<ListView.FixedViewInfo>(4);
+ header.add(lv.new FixedViewInfo());
+ headerViewListAdapter = new HeaderViewListAdapter(header, null, null);
+ assertEquals(1, headerViewListAdapter.getHeadersCount());
+ assertFalse(headerViewListAdapter.isEmpty());
+
+ lv = new ListView(getContext());
+ ArrayList<ListView.FixedViewInfo> footer = new ArrayList<ListView.FixedViewInfo>(4);
+ footer.add(lv.new FixedViewInfo());
+ headerViewListAdapter = new HeaderViewListAdapter(null, footer, null);
+ assertEquals(1, headerViewListAdapter.getFootersCount());
+ assertFalse(headerViewListAdapter.isEmpty());
+
}
public void testRemoveHeader() {
diff --git a/tests/uiautomator/Android.mk b/tests/uiautomator/Android.mk
index b5bfae3..68b1dc2 100644
--- a/tests/uiautomator/Android.mk
+++ b/tests/uiautomator/Android.mk
@@ -22,7 +22,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := CtsUiAutomatorTests
-LOCAL_JAVA_LIBRARIES := uiautomator_sdk_v16
+LOCAL_JAVA_LIBRARIES := uiautomator_sdk_v17
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_CTS_TEST_APK := CtsUiAutomatorApp
LOCAL_CTS_TEST_APP_PACKAGE := com.android.cts.uiautomator
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 1595e93..5de9805 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -16,6 +16,7 @@
package com.android.cts.uiautomatortest;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
@@ -29,6 +30,7 @@
import com.android.uiautomator.testrunner.UiAutomatorTestCase;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
/**
@@ -37,7 +39,7 @@
public class CtsUiAutomatorTest extends UiAutomatorTestCase {
private static final String LOG_TAG = CtsUiAutomatorTest.class.getSimpleName();
private static final String[] LIST_SCROLL_TESTS = new String[] {
- "Test 17", "Test 11", "Test 30", "Test 29"
+ "Test 17", "Test 11", "Test 20"
};
private static final String LAUNCH_APP = "am start -a android.intent.action.MAIN"
+ " -n com.android.cts.uiautomator/.MainActivity -W";
@@ -46,6 +48,8 @@
// Maximum wait for key object to become visible
private static final int WAIT_EXIST_TIMEOUT = 5 * 1000;
+ private static final String SCREEN_SHOT_FILE_PATH_NAME = "/data/local/tmp/ctsScreenShot";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -85,6 +89,13 @@
* scroll is required to reach each item at each of the far ends.
*/
public void testListScrollAndSelect() throws UiObjectNotFoundException {
+ UiScrollable listView = new UiScrollable(
+ new UiSelector().className(android.widget.ListView.class.getName()));
+
+ // on single fragment display
+ if (!listView.exists())
+ UiDevice.getInstance().pressBack();
+
for (String test : LIST_SCROLL_TESTS) {
openTest(test);
verifyTestDetailsExists(test);
@@ -230,6 +241,7 @@
assertFalse("Wait for exist must return false after press back", result.exists());
// Home button test
+ openTest("Test 5");
String pkgName = device.getCurrentPackageName();
assertTrue("CTS test app must be running", pkgName.equals(PKG_NAME));
device.pressHome();
@@ -363,6 +375,7 @@
*
* @throws UiObjectNotFoundException
*/
+ /*// broken in MR1
public void testWebViewTextTraversal() throws UiObjectNotFoundException {
openTest("Test 6");
UiObject webView = new UiObject(new UiSelector().className(android.webkit.WebView.class
@@ -390,7 +403,7 @@
device.pressDPadDown();
text = device.getLastTraversedText();
assertTrue("h4 text", text.contains("h4"));
- }
+ }*/
/**
* Test when an object does not exist, an exception is thrown
@@ -608,6 +621,166 @@
}
/**
+ * Test Orientation APIs by causing rotations and verifying current state
+ *
+ * @throws RemoteException
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testRotation() throws RemoteException, UiObjectNotFoundException {
+ openTest("Test 5");
+ UiDevice device = UiDevice.getInstance();
+
+ device.setOrientationLeft();
+ device.waitForIdle(); // isNaturalOrientation is not waiting for idle
+ SystemClock.sleep(1000);
+ assertFalse("Device orientation should not be natural", device.isNaturalOrientation());
+
+ device.setOrientationNatural();
+ device.waitForIdle(); // isNaturalOrientation is not waiting for idle
+ SystemClock.sleep(1000);
+ assertTrue("Device orientation should be natural", device.isNaturalOrientation());
+
+ device.setOrientationRight();
+ device.waitForIdle(); // isNaturalOrientation is not waiting for idle
+ SystemClock.sleep(1000);
+ assertFalse("Device orientation should not be natural", device.isNaturalOrientation());
+
+ device.setOrientationNatural();
+ }
+
+ /**
+ * Reads the current device's product name. Since it is not possible to predetermine the
+ * would be value, the check verifies that the value is not null and not empty.
+ *
+ * @since API Level 17
+ */
+ public void testGetProductName() {
+ String name = UiDevice.getInstance().getProductName();
+ assertFalse("Product name check returned empty string", name.isEmpty());
+ }
+
+ /**
+ * Select each of the buttons by using only regex text
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectByTextMatch() throws UiObjectNotFoundException {
+ openTest("Test 2");
+ getObjectByTextMatch(".*n\\s1$").click();
+ verifyDialogActionResults("Button 1");
+ getObjectByTextMatch(".*n\\s2$").click();
+ verifyDialogActionResults("Button 2");
+ getObjectByTextMatch(".*n\\s3$").click();
+ verifyDialogActionResults("Button 3");
+ }
+
+ /**
+ * Select each of the buttons by using only regex content-description
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectByDescriptionMatch() throws UiObjectNotFoundException {
+ openTest("Test 2");
+ getObjectByDescriptionMatch(".*n\\s1$").click();
+ verifyDialogActionResults("Button 1");
+ getObjectByDescriptionMatch(".*n\\s2$").click();
+ verifyDialogActionResults("Button 2");
+ getObjectByDescriptionMatch(".*n\\s3$").click();
+ verifyDialogActionResults("Button 3");
+ }
+
+ /**
+ * Select each of the buttons by using only regex class name
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectByClassMatch() throws UiObjectNotFoundException {
+ openTest("Test 5");
+ UiObject tgl = getObjectByClassMatch(".*ToggleButton$", 0);
+ String tglValue = tgl.getText();
+ tgl.click();
+
+ assertFalse("Matching class by Regex failed", tglValue.equals(tgl.getText()));
+ }
+
+ /**
+ * Select each of the buttons by using only class type
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectByClassType() throws UiObjectNotFoundException {
+ openTest("Test 5");
+ UiObject tgl = getObjectByClass(android.widget.ToggleButton.class, 0);
+ String tglValue = tgl.getText();
+ tgl.click();
+
+ assertFalse("Matching class by class type failed", tglValue.equals(tgl.getText()));
+ }
+
+ /**
+ * Test the coordinates of 3 buttons side by side verifying vertical and
+ * horizontal coordinates.
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testGetVisibleBounds() throws UiObjectNotFoundException {
+ openTest("Test 2");
+ Rect rect1 = getObjectByText("Button 1").getVisibleBounds();
+ Rect rect2 = getObjectByText("Button 2").getVisibleBounds();
+ Rect rect3 = getObjectByText("Button 3").getVisibleBounds();
+
+ assertTrue("X coordinate check failed",
+ rect1.left < rect2.left && rect2.right < rect3.right);
+ assertTrue("Y coordinate check failed",
+ rect1.top == rect2.top && rect2.bottom == rect3.bottom);
+ }
+
+ /**
+ * Tests the LongClick functionality in the API
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectorLongClickable() throws UiObjectNotFoundException {
+ openTest("Test 2");
+ getObjectByText("Button 1").longClick();
+ verifyDialogActionResults("Longclick Button 1");
+ }
+
+ /**
+ * Test the UiSelector's long-clickable property
+ *
+ * @throws UiObjectNotFoundException
+ * @since API Level 17
+ */
+ public void testSelectorLongClickableProperty() throws UiObjectNotFoundException {
+ UiObject button3 = new UiObject(new UiSelector().className(
+ android.widget.Button.class).longClickable(true).instance(2));
+ button3.longClick();
+ verifyDialogActionResults("Longclick Button 3");
+ }
+
+ /**
+ * Takes a screen shot of the current display and checks if the file is
+ * created and is not zero size.
+ *
+ * @since API Level 17
+ */
+ public void testTakeScreenShots() {
+ File storePath = new File(SCREEN_SHOT_FILE_PATH_NAME);
+ getUiDevice().takeScreenshot(storePath);
+
+ assertTrue("Screenshot file not detected in store", storePath.exists());
+ assertTrue("Zero size for screenshot file", storePath.length() > 0);
+ }
+
+ /**
* Private helper to open test views. Also covers UiScrollable tests
*
* @param name
@@ -636,10 +809,26 @@
return new UiObject(new UiSelector().text(txt));
}
+ private UiObject getObjectByTextMatch(String regex) {
+ return new UiObject(new UiSelector().textMatches(regex));
+ }
+
+ private UiObject getObjectByDescriptionMatch(String regex) {
+ return new UiObject(new UiSelector().descriptionMatches(regex));
+ }
+
private UiObject getObjectByDescription(String txt) {
return new UiObject(new UiSelector().description(txt));
}
+ private UiObject getObjectByClassMatch(String regex, int instance) {
+ return new UiObject(new UiSelector().classNameMatches(regex).instance(instance));
+ }
+
+ private <T> UiObject getObjectByClass(Class<T> type, int instance) {
+ return new UiObject(new UiSelector().className(type).instance(instance));
+ }
+
private UiObject getObjectByIndex(String className, int index) {
return new UiObject(new UiSelector().className(className).index(index));
}
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
index e3689f7..db2450a 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
@@ -31,8 +31,11 @@
<string name="test2_description_2">This tests a Button text changing dynamically after a click. The test should attempt to find the button after it has changed using its new text.</string>
<string name="test3_description">The tests should read the clock, then write the clock back into the EditText then press submit. A dialog will display the time it took from reading the clock until submit is press. This can be used to get an idea of performance differences.</string>
<string name="button1">Button 1</string>
+ <string name="button1long">Longclick Button 1</string>
<string name="button2">Button 2</string>
+ <string name="button2long">Longclick Button 2</string>
<string name="button3">Button 3</string>
+ <string name="button3long">Longclick Button 3</string>
<string name="buttonBefore">Before</string>
<string name="buttonAfter">After</string>
<string name="dialog_title_result">Action results</string>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/Test2DetailFragment.java b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/Test2DetailFragment.java
index cec98b5..4eade4b 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/Test2DetailFragment.java
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/Test2DetailFragment.java
@@ -78,6 +78,19 @@
}
});
+ mButton1.setOnLongClickListener(new Button.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.dialog_title_result);
+ builder.setPositiveButton(R.string.OK, null);
+ builder.setMessage(R.string.button1long);
+ AlertDialog diag = builder.create();
+ diag.show();
+ return true;
+ }
+ });
+
mButton2.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
@@ -90,6 +103,19 @@
}
});
+ mButton2.setOnLongClickListener(new Button.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.dialog_title_result);
+ builder.setPositiveButton(R.string.OK, null);
+ builder.setMessage(R.string.button2long);
+ AlertDialog diag = builder.create();
+ diag.show();
+ return true;
+ }
+ });
+
mButton3.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
@@ -102,6 +128,19 @@
}
});
+ mButton3.setOnLongClickListener(new Button.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.dialog_title_result);
+ builder.setPositiveButton(R.string.OK, null);
+ builder.setMessage(R.string.button3long);
+ AlertDialog diag = builder.create();
+ diag.show();
+ return true;
+ }
+ });
+
mDynaButton.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/tools/tradefed-host/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 74eec37..826fbc5 100644
--- a/tools/tradefed-host/res/report/cts_result.xsd
+++ b/tools/tradefed-host/res/report/cts_result.xsd
@@ -16,8 +16,8 @@
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
- targetNamespace="http://compatibility.android.com/cts_result/1.14"
- xmlns="http://compatibility.android.com/cts_result/1.14"
+ targetNamespace="http://compatibility.android.com/cts_result/1.15"
+ xmlns="http://compatibility.android.com/cts_result/1.15"
elementFormDefault="qualified">
<xs:element name="TestResult">
@@ -196,6 +196,28 @@
<xs:attribute name="priority" type="xs:string"/>
</xs:complexType>
+<xs:simpleType name="unitType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="ms"/>
+ <xs:enumeration value="fps"/>
+ <xs:enumeration value="ops"/>
+ <xs:enumeration value="kbps"/>
+ <xs:enumeration value="mbps"/>
+ <xs:enumeration value="byte"/>
+ <xs:enumeration value="count"/>
+ <xs:enumeration value="score"/>
+ </xs:restriction>
+</xs:simpleType>
+
+<xs:simpleType name="scoreTypeType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="higherBetter"/>
+ <xs:enumeration value="lowerBetter"/>
+ <xs:enumeration value="neutral"/>
+ </xs:restriction>
+</xs:simpleType>
+
<xs:complexType name="testType">
<xs:sequence>
<xs:element name="FailedScene" minOccurs="0" maxOccurs="1">
@@ -206,6 +228,34 @@
<xs:attribute name="message" type="xs:string"/>
</xs:complexType>
</xs:element>
+ <xs:element name="Summary" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:decimal">
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Details" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="ValueArray" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Value" type="xs:decimal" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="source" type="xs:string" use="required" />
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="result" type="resultType" use="required"/>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
index dd4f31f..ab88908 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
@@ -15,6 +15,7 @@
*/
package com.android.cts.tradefed.result;
+import com.android.ddmlib.Log;
import com.android.tradefed.result.TestResult;
import org.kxml2.io.KXmlSerializer;
@@ -27,7 +28,6 @@
* Data structure that represents a "Test" result XML element.
*/
class Test extends AbstractXmlPullParser {
-
static final String TAG = "Test";
private static final String NAME_ATTR = "name";
private static final String MESSAGE_ATTR = "message";
@@ -36,7 +36,16 @@
private static final String RESULT_ATTR = "result";
private static final String SCENE_TAG = "FailedScene";
private static final String STACK_TAG = "StackTrace";
+ private static final String SUMMARY_TAG = "Summary";
private static final String DETAILS_TAG = "Details";
+ private static final String VALUEARRAY_TAG = "ValueArray";
+ private static final String VALUE_TAG = "Value";
+ private static final String SCORETYPE_ATTR = "scoreType";
+ private static final String UNIT_ATTR = "unit";
+ private static final String SOURCE_ATTR = "source";
+ // separators for the message from PTS
+ private static final String LOG_SEPARATOR = "\\+\\+\\+";
+ private static final String LOG_ELEM_SEPARATOR = "\\|";
private String mName;
private CtsTestStatus mResult;
@@ -44,7 +53,8 @@
private String mEndTime;
private String mMessage;
private String mStackTrace;
- // details passed from pts
+ // summary and details passed from pts
+ private String mSummary;
private String mDetails;
/**
@@ -109,6 +119,14 @@
mMessage = getFailureMessageFromStackTrace(mStackTrace);
}
+ public String getSummary() {
+ return mSummary;
+ }
+
+ public void setSummary(String summary) {
+ mSummary = summary;
+ }
+
public String getDetails() {
return mDetails;
}
@@ -147,17 +165,110 @@
serializer.text(mStackTrace);
serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG);
}
- if (mDetails != null) {
- serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
- serializer.text(mDetails);
- serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
- }
serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
}
+ if (mSummary != null) {
+ // <Summary message = "screen copies per sec" scoretype="higherBetter" unit="fps">
+ // 23938.82978723404</Summary>
+ PerfResultSummary summary = parseSummary(mSummary);
+ if (summary != null) {
+ serializer.startTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+ serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, summary.mMessage);
+ serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, summary.mType);
+ serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, summary.mUnit);
+ serializer.text(summary.mValue);
+ serializer.endTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+ // add details only if summary is present
+ // <Details>
+ // <ValueArray source=”com.android.pts.dram.BandwidthTest#doRunMemcpy:98”
+ // message=”measure1” unit="ms" scoretype="higherBetter">
+ // <Value>0.0</Value>
+ // <Value>0.1</Value>
+ // </ValueArray>
+ // </Details>
+ if (mDetails != null) {
+ PerfResultDetail[] ds = parseDetails(mDetails);
+ serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+ for (PerfResultDetail d : ds) {
+ if (d == null) {
+ continue;
+ }
+ serializer.startTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+ serializer.attribute(CtsXmlResultReporter.ns, SOURCE_ATTR, d.mSource);
+ serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR,
+ d.mMessage);
+ serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, d.mType);
+ serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, d.mUnit);
+ for (String v : d.mValues) {
+ if (v == null) {
+ continue;
+ }
+ serializer.startTag(CtsXmlResultReporter.ns, VALUE_TAG);
+ serializer.text(v);
+ serializer.endTag(CtsXmlResultReporter.ns, VALUE_TAG);
+ }
+ serializer.endTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+ }
+ serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+ }
+ }
+ }
serializer.endTag(CtsXmlResultReporter.ns, TAG);
}
/**
+ * class containing performance result.
+ */
+ public static class PerfResultCommon {
+ public String mMessage;
+ public String mType;
+ public String mUnit;
+ }
+
+ private class PerfResultSummary extends PerfResultCommon {
+ public String mValue;
+ }
+
+ private class PerfResultDetail extends PerfResultCommon {
+ public String mSource;
+ public String[] mValues;
+ }
+
+ private PerfResultSummary parseSummary(String summary) {
+ String[] elems = summary.split(LOG_ELEM_SEPARATOR);
+ PerfResultSummary r = new PerfResultSummary();
+ if (elems.length < 4) {
+ Log.w(TAG, "wrong message " + summary);
+ return null;
+ }
+ r.mMessage = elems[0];
+ r.mType = elems[1];
+ r.mUnit = elems[2];
+ r.mValue = elems[3];
+ return r;
+ }
+
+ private PerfResultDetail[] parseDetails(String details) {
+ String[] arrays = details.split(LOG_SEPARATOR);
+ PerfResultDetail[] rs = new PerfResultDetail[arrays.length];
+ for (int i = 0; i < arrays.length; i++) {
+ String[] elems = arrays[i].split(LOG_ELEM_SEPARATOR);
+ if (elems.length < 5) {
+ Log.w(TAG, "wrong message " + arrays[i]);
+ continue;
+ }
+ PerfResultDetail r = new PerfResultDetail();
+ r.mSource = elems[0];
+ r.mMessage = elems[1];
+ r.mType = elems[2];
+ r.mUnit = elems[3];
+ r.mValues = elems[4].split(" ");
+ rs[i] = r;
+ }
+ return rs;
+ }
+
+ /**
* Strip out any invalid XML characters that might cause the report to be unviewable.
* http://www.w3.org/TR/REC-xml/#dt-character
*/
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index ce1c2f3..015a7ae 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -283,7 +283,7 @@
public void reportPerformanceResult(TestIdentifier test, CtsTestStatus status, String summary, String details) {
Test result = findTest(test);
result.setResultStatus(status);
- result.setMessage(summary);
+ result.setSummary(summary);
result.setDetails(details);
}