mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-11 19:37:49 +00:00
Add json3.js, remove json2 and jslitmus from vendors.
Former-commit-id: d801c48ec81ea2f5c5638910190da461facf98cf
This commit is contained in:
@@ -8,9 +8,6 @@
|
|||||||
body > #qunit-header {
|
body > #qunit-header {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#jslitmus {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -21,9 +18,8 @@
|
|||||||
<h1>Test</h1>
|
<h1>Test</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="../vendor/backbone/test/vendor/json2.js"></script>
|
<script src="../vendor/json3/lib/json3.js"></script>
|
||||||
<script src="../vendor/backbone/test/vendor/jquery-1.7.1.js"></script>
|
<script src="../vendor/backbone/test/vendor/jquery-1.7.1.js"></script>
|
||||||
<script src="../vendor/backbone/test/vendor/jslitmus.js"></script>
|
|
||||||
<script src="../vendor/platform.js/platform.js"></script>
|
<script src="../vendor/platform.js/platform.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// avoid syntax errors for `QUnit.throws` in older Firefoxes
|
// avoid syntax errors for `QUnit.throws` in older Firefoxes
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<title>Underscore Test Suite</title>
|
<title>Underscore Test Suite</title>
|
||||||
<link rel="stylesheet" href="../vendor/underscore/test/vendor/qunit.css">
|
<link rel="stylesheet" href="../vendor/underscore/test/vendor/qunit.css">
|
||||||
<style>
|
<style>
|
||||||
#jslitmus, iframe {
|
iframe, img {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
<div id="id1"></div>
|
<div id="id1"></div>
|
||||||
<div id="id2"></div>
|
<div id="id2"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<img id="chart_image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
|
||||||
</div>
|
</div>
|
||||||
<script src="../vendor/backbone/test/vendor/json2.js"></script>
|
<script src="../vendor/json3/lib/json3.js"></script>
|
||||||
<script src="../vendor/underscore/test/vendor/jquery.js"></script>
|
<script src="../vendor/underscore/test/vendor/jquery.js"></script>
|
||||||
<script src="../vendor/underscore/test/vendor/jslitmus.js"></script>
|
|
||||||
<script src="../vendor/platform.js/platform.js"></script>
|
<script src="../vendor/platform.js/platform.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// avoid syntax errors for `QUnit.throws` in older Firefoxes
|
// avoid syntax errors for `QUnit.throws` in older Firefoxes
|
||||||
|
|||||||
649
vendor/backbone/test/vendor/jslitmus.js
vendored
649
vendor/backbone/test/vendor/jslitmus.js
vendored
@@ -1,649 +0,0 @@
|
|||||||
// JSLitmus.js
|
|
||||||
//
|
|
||||||
// Copyright (c) 2010, Robert Kieffer, http://broofa.com
|
|
||||||
// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
// Private methods and state
|
|
||||||
|
|
||||||
// Get platform info but don't go crazy trying to recognize everything
|
|
||||||
// that's out there. This is just for the major platforms and OSes.
|
|
||||||
var platform = 'unknown platform', ua = navigator.userAgent;
|
|
||||||
|
|
||||||
// Detect OS
|
|
||||||
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
|
|
||||||
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
|
|
||||||
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
|
|
||||||
|
|
||||||
// Detect browser
|
|
||||||
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
|
|
||||||
|
|
||||||
// Detect version
|
|
||||||
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
|
|
||||||
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
|
|
||||||
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A smattering of methods that are needed to implement the JSLitmus testbed.
|
|
||||||
*/
|
|
||||||
var jsl = {
|
|
||||||
/**
|
|
||||||
* Enhanced version of escape()
|
|
||||||
*/
|
|
||||||
escape: function(s) {
|
|
||||||
s = s.replace(/,/g, '\\,');
|
|
||||||
s = escape(s);
|
|
||||||
s = s.replace(/\+/g, '%2b');
|
|
||||||
s = s.replace(/ /g, '+');
|
|
||||||
return s;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an element by ID.
|
|
||||||
*/
|
|
||||||
$: function(id) {
|
|
||||||
return document.getElementById(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Null function
|
|
||||||
*/
|
|
||||||
F: function() {},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the status shown in the UI
|
|
||||||
*/
|
|
||||||
status: function(msg) {
|
|
||||||
var el = jsl.$('jsl_status');
|
|
||||||
if (el) el.innerHTML = msg || '';
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a number to an abbreviated string like, "15K" or "10M"
|
|
||||||
*/
|
|
||||||
toLabel: function(n) {
|
|
||||||
if (n == Infinity) {
|
|
||||||
return 'Infinity';
|
|
||||||
} else if (n > 1e9) {
|
|
||||||
n = Math.round(n/1e8);
|
|
||||||
return n/10 + 'B';
|
|
||||||
} else if (n > 1e6) {
|
|
||||||
n = Math.round(n/1e5);
|
|
||||||
return n/10 + 'M';
|
|
||||||
} else if (n > 1e3) {
|
|
||||||
n = Math.round(n/1e2);
|
|
||||||
return n/10 + 'K';
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy properties from src to dst
|
|
||||||
*/
|
|
||||||
extend: function(dst, src) {
|
|
||||||
for (var k in src) dst[k] = src[k]; return dst;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like Array.join(), but for the key-value pairs in an object
|
|
||||||
*/
|
|
||||||
join: function(o, delimit1, delimit2) {
|
|
||||||
if (o.join) return o.join(delimit1); // If it's an array
|
|
||||||
var pairs = [];
|
|
||||||
for (var k in o) pairs.push(k + delimit1 + o[k]);
|
|
||||||
return pairs.join(delimit2);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
|
|
||||||
*/
|
|
||||||
indexOf: function(arr, o) {
|
|
||||||
if (arr.indexOf) return arr.indexOf(o);
|
|
||||||
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test manages a single test (created with
|
|
||||||
* JSLitmus.test())
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
var Test = function (name, f) {
|
|
||||||
if (!f) throw new Error('Undefined test function');
|
|
||||||
if (!/function[^\(]*\(([^,\)]*)/.test(f.toString())) {
|
|
||||||
throw new Error('"' + name + '" test: Test is not a valid Function object');
|
|
||||||
}
|
|
||||||
this.loopArg = RegExp.$1;
|
|
||||||
this.name = name;
|
|
||||||
this.f = f;
|
|
||||||
};
|
|
||||||
|
|
||||||
jsl.extend(Test, /** @lends Test */ {
|
|
||||||
/** Calibration tests for establishing iteration loop overhead */
|
|
||||||
CALIBRATIONS: [
|
|
||||||
new Test('calibrating loop', function(count) {while (count--);}),
|
|
||||||
new Test('calibrating function', jsl.F)
|
|
||||||
],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run calibration tests. Returns true if calibrations are not yet
|
|
||||||
* complete (in which case calling code should run the tests yet again).
|
|
||||||
* onCalibrated - Callback to invoke when calibrations have finished
|
|
||||||
*/
|
|
||||||
calibrate: function(onCalibrated) {
|
|
||||||
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
|
|
||||||
var cal = Test.CALIBRATIONS[i];
|
|
||||||
if (cal.running) return true;
|
|
||||||
if (!cal.count) {
|
|
||||||
cal.isCalibration = true;
|
|
||||||
cal.onStop = onCalibrated;
|
|
||||||
//cal.MIN_TIME = .1; // Do calibrations quickly
|
|
||||||
cal.run(2e4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
jsl.extend(Test.prototype, {/** @lends Test.prototype */
|
|
||||||
/** Initial number of iterations */
|
|
||||||
INIT_COUNT: 10,
|
|
||||||
/** Max iterations allowed (i.e. used to detect bad looping functions) */
|
|
||||||
MAX_COUNT: 1e9,
|
|
||||||
/** Minimum time a test should take to get valid results (secs) */
|
|
||||||
MIN_TIME: .5,
|
|
||||||
|
|
||||||
/** Callback invoked when test state changes */
|
|
||||||
onChange: jsl.F,
|
|
||||||
|
|
||||||
/** Callback invoked when test is finished */
|
|
||||||
onStop: jsl.F,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset test state
|
|
||||||
*/
|
|
||||||
reset: function() {
|
|
||||||
delete this.count;
|
|
||||||
delete this.time;
|
|
||||||
delete this.running;
|
|
||||||
delete this.error;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the test (in a timeout). We use a timeout to make sure the browser
|
|
||||||
* has a chance to finish rendering any UI changes we've made, like
|
|
||||||
* updating the status message.
|
|
||||||
*/
|
|
||||||
run: function(count) {
|
|
||||||
count = count || this.INIT_COUNT;
|
|
||||||
jsl.status(this.name + ' x ' + count);
|
|
||||||
this.running = true;
|
|
||||||
var me = this;
|
|
||||||
setTimeout(function() {me._run(count);}, 200);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The nuts and bolts code that actually runs a test
|
|
||||||
*/
|
|
||||||
_run: function(count) {
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
// Make sure calibration tests have run
|
|
||||||
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
|
|
||||||
this.error = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var start, f = this.f, now, i = count;
|
|
||||||
|
|
||||||
// Start the timer
|
|
||||||
start = new Date();
|
|
||||||
|
|
||||||
// Now for the money shot. If this is a looping function ...
|
|
||||||
if (this.loopArg) {
|
|
||||||
// ... let it do the iteration itself
|
|
||||||
f(count);
|
|
||||||
} else {
|
|
||||||
// ... otherwise do the iteration for it
|
|
||||||
while (i--) f();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get time test took (in secs)
|
|
||||||
this.time = Math.max(1,new Date() - start)/1000;
|
|
||||||
|
|
||||||
// Store iteration count and per-operation time taken
|
|
||||||
this.count = count;
|
|
||||||
this.period = this.time/count;
|
|
||||||
|
|
||||||
// Do we need to do another run?
|
|
||||||
this.running = this.time <= this.MIN_TIME;
|
|
||||||
|
|
||||||
// ... if so, compute how many times we should iterate
|
|
||||||
if (this.running) {
|
|
||||||
// Bump the count to the nearest power of 2
|
|
||||||
var x = this.MIN_TIME/this.time;
|
|
||||||
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
|
|
||||||
count *= pow;
|
|
||||||
if (count > this.MAX_COUNT) {
|
|
||||||
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Exceptions are caught and displayed in the test UI
|
|
||||||
this.reset();
|
|
||||||
this.error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out what to do next
|
|
||||||
if (this.running) {
|
|
||||||
me.run(count);
|
|
||||||
} else {
|
|
||||||
jsl.status('');
|
|
||||||
me.onStop(me);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish up
|
|
||||||
this.onChange(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of operations per second for this test.
|
|
||||||
*
|
|
||||||
* @param normalize if true, iteration loop overhead taken into account
|
|
||||||
*/
|
|
||||||
getHz: function(/**Boolean*/ normalize) {
|
|
||||||
var p = this.period;
|
|
||||||
|
|
||||||
// Adjust period based on the calibration test time
|
|
||||||
if (normalize && !this.isCalibration) {
|
|
||||||
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
|
|
||||||
|
|
||||||
// If the period is within 20% of the calibration time, then zero the
|
|
||||||
// it out
|
|
||||||
p = p < cal.period*1.2 ? 0 : p - cal.period;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(1/p);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a friendly string describing the test
|
|
||||||
*/
|
|
||||||
toString: function() {
|
|
||||||
return this.name + ' - ' + this.time/this.count + ' secs';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CSS we need for the UI
|
|
||||||
var STYLESHEET = '<style> \
|
|
||||||
#jslitmus {font-family:sans-serif; font-size: 12px;} \
|
|
||||||
#jslitmus a {text-decoration: none;} \
|
|
||||||
#jslitmus a:hover {text-decoration: underline;} \
|
|
||||||
#jsl_status { \
|
|
||||||
margin-top: 10px; \
|
|
||||||
font-size: 10px; \
|
|
||||||
color: #888; \
|
|
||||||
} \
|
|
||||||
A IMG {border:none} \
|
|
||||||
#test_results { \
|
|
||||||
margin-top: 10px; \
|
|
||||||
font-size: 12px; \
|
|
||||||
font-family: sans-serif; \
|
|
||||||
border-collapse: collapse; \
|
|
||||||
border-spacing: 0px; \
|
|
||||||
} \
|
|
||||||
#test_results th, #test_results td { \
|
|
||||||
border: solid 1px #ccc; \
|
|
||||||
vertical-align: top; \
|
|
||||||
padding: 3px; \
|
|
||||||
} \
|
|
||||||
#test_results th { \
|
|
||||||
vertical-align: bottom; \
|
|
||||||
background-color: #ccc; \
|
|
||||||
padding: 1px; \
|
|
||||||
font-size: 10px; \
|
|
||||||
} \
|
|
||||||
#test_results #test_platform { \
|
|
||||||
color: #444; \
|
|
||||||
text-align:center; \
|
|
||||||
} \
|
|
||||||
#test_results .test_row { \
|
|
||||||
color: #006; \
|
|
||||||
cursor: pointer; \
|
|
||||||
} \
|
|
||||||
#test_results .test_nonlooping { \
|
|
||||||
border-left-style: dotted; \
|
|
||||||
border-left-width: 2px; \
|
|
||||||
} \
|
|
||||||
#test_results .test_looping { \
|
|
||||||
border-left-style: solid; \
|
|
||||||
border-left-width: 2px; \
|
|
||||||
} \
|
|
||||||
#test_results .test_name {white-space: nowrap;} \
|
|
||||||
#test_results .test_pending { \
|
|
||||||
} \
|
|
||||||
#test_results .test_running { \
|
|
||||||
font-style: italic; \
|
|
||||||
} \
|
|
||||||
#test_results .test_done {} \
|
|
||||||
#test_results .test_done { \
|
|
||||||
text-align: right; \
|
|
||||||
font-family: monospace; \
|
|
||||||
} \
|
|
||||||
#test_results .test_error {color: #600;} \
|
|
||||||
#test_results .test_error .error_head {font-weight:bold;} \
|
|
||||||
#test_results .test_error .error_body {font-size:85%;} \
|
|
||||||
#test_results .test_row:hover td { \
|
|
||||||
background-color: #ffc; \
|
|
||||||
text-decoration: underline; \
|
|
||||||
} \
|
|
||||||
#chart { \
|
|
||||||
margin: 10px 0px; \
|
|
||||||
width: 250px; \
|
|
||||||
} \
|
|
||||||
#chart img { \
|
|
||||||
border: solid 1px #ccc; \
|
|
||||||
margin-bottom: 5px; \
|
|
||||||
} \
|
|
||||||
#chart #tiny_url { \
|
|
||||||
height: 40px; \
|
|
||||||
width: 250px; \
|
|
||||||
} \
|
|
||||||
#jslitmus_credit { \
|
|
||||||
font-size: 10px; \
|
|
||||||
color: #888; \
|
|
||||||
margin-top: 8px; \
|
|
||||||
} \
|
|
||||||
</style>';
|
|
||||||
|
|
||||||
// HTML markup for the UI
|
|
||||||
var MARKUP = '<div id="jslitmus"> \
|
|
||||||
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
|
|
||||||
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
|
|
||||||
<br \> \
|
|
||||||
<br \> \
|
|
||||||
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
|
|
||||||
<table id="test_results"> \
|
|
||||||
<colgroup> \
|
|
||||||
<col /> \
|
|
||||||
<col width="100" /> \
|
|
||||||
</colgroup> \
|
|
||||||
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
|
|
||||||
<tr><th>Test</th><th>Ops/sec</th></tr> \
|
|
||||||
<tr id="test_row_template" class="test_row" style="display:none"> \
|
|
||||||
<td class="test_name"></td> \
|
|
||||||
<td class="test_result">Ready</td> \
|
|
||||||
</tr> \
|
|
||||||
</table> \
|
|
||||||
<div id="jsl_status"></div> \
|
|
||||||
<div id="chart" style="display:none"> \
|
|
||||||
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
|
|
||||||
TinyURL (for chart): \
|
|
||||||
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
|
|
||||||
</div> \
|
|
||||||
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
|
|
||||||
</div>';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The public API for creating and running tests
|
|
||||||
*/
|
|
||||||
window.JSLitmus = {
|
|
||||||
/** The list of all tests that have been registered with JSLitmus.test */
|
|
||||||
_tests: [],
|
|
||||||
/** The queue of tests that need to be run */
|
|
||||||
_queue: [],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parsed query parameters the current page URL. This is provided as a
|
|
||||||
* convenience for test functions - it's not used by JSLitmus proper
|
|
||||||
*/
|
|
||||||
params: {},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize
|
|
||||||
*/
|
|
||||||
_init: function() {
|
|
||||||
// Parse query params into JSLitmus.params[] hash
|
|
||||||
var match = (location + '').match(/([^?#]*)(#.*)?$/);
|
|
||||||
if (match) {
|
|
||||||
var pairs = match[1].split('&');
|
|
||||||
for (var i = 0; i < pairs.length; i++) {
|
|
||||||
var pair = pairs[i].split('=');
|
|
||||||
if (pair.length > 1) {
|
|
||||||
var key = pair.shift();
|
|
||||||
var value = pair.length > 1 ? pair.join('=') : pair[0];
|
|
||||||
this.params[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out the stylesheet. We have to do this here because IE
|
|
||||||
// doesn't honor sheets written after the document has loaded.
|
|
||||||
document.write(STYLESHEET);
|
|
||||||
|
|
||||||
// Setup the rest of the UI once the document is loaded
|
|
||||||
if (window.addEventListener) {
|
|
||||||
window.addEventListener('load', this._setup, false);
|
|
||||||
} else if (document.addEventListener) {
|
|
||||||
document.addEventListener('load', this._setup, false);
|
|
||||||
} else if (window.attachEvent) {
|
|
||||||
window.attachEvent('onload', this._setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the UI
|
|
||||||
*/
|
|
||||||
_setup: function() {
|
|
||||||
var el = jsl.$('jslitmus_container');
|
|
||||||
if (!el) document.body.appendChild(el = document.createElement('div'));
|
|
||||||
|
|
||||||
el.innerHTML = MARKUP;
|
|
||||||
|
|
||||||
// Render the UI for all our tests
|
|
||||||
for (var i=0; i < JSLitmus._tests.length; i++)
|
|
||||||
JSLitmus.renderTest(JSLitmus._tests[i]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render all the test results
|
|
||||||
*/
|
|
||||||
renderAll: function() {
|
|
||||||
for (var i = 0; i < JSLitmus._tests.length; i++)
|
|
||||||
JSLitmus.renderTest(JSLitmus._tests[i]);
|
|
||||||
JSLitmus.renderChart();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render the chart graphics
|
|
||||||
*/
|
|
||||||
renderChart: function() {
|
|
||||||
var url = JSLitmus.chartUrl();
|
|
||||||
jsl.$('chart_link').href = url;
|
|
||||||
jsl.$('chart_image').src = url;
|
|
||||||
jsl.$('chart').style.display = '';
|
|
||||||
|
|
||||||
// Update the tiny URL
|
|
||||||
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render the results for a specific test
|
|
||||||
*/
|
|
||||||
renderTest: function(test) {
|
|
||||||
// Make a new row if needed
|
|
||||||
if (!test._row) {
|
|
||||||
var trow = jsl.$('test_row_template');
|
|
||||||
if (!trow) return;
|
|
||||||
|
|
||||||
test._row = trow.cloneNode(true);
|
|
||||||
test._row.style.display = '';
|
|
||||||
test._row.id = '';
|
|
||||||
test._row.onclick = function() {JSLitmus._queueTest(test);};
|
|
||||||
test._row.title = 'Run ' + test.name + ' test';
|
|
||||||
trow.parentNode.appendChild(test._row);
|
|
||||||
test._row.cells[0].innerHTML = test.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = test._row.cells[1];
|
|
||||||
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
|
|
||||||
|
|
||||||
if (test.error) {
|
|
||||||
cns.push('test_error');
|
|
||||||
cell.innerHTML =
|
|
||||||
'<div class="error_head">' + test.error + '</div>' +
|
|
||||||
'<ul class="error_body"><li>' +
|
|
||||||
jsl.join(test.error, ': ', '</li><li>') +
|
|
||||||
'</li></ul>';
|
|
||||||
} else {
|
|
||||||
if (test.running) {
|
|
||||||
cns.push('test_running');
|
|
||||||
cell.innerHTML = 'running';
|
|
||||||
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
|
|
||||||
cns.push('test_pending');
|
|
||||||
cell.innerHTML = 'pending';
|
|
||||||
} else if (test.count) {
|
|
||||||
cns.push('test_done');
|
|
||||||
var hz = test.getHz(jsl.$('test_normalize').checked);
|
|
||||||
cell.innerHTML = hz != Infinity ? hz : '∞';
|
|
||||||
cell.title = 'Looped ' + test.count + ' times in ' + test.time + ' seconds';
|
|
||||||
} else {
|
|
||||||
cell.innerHTML = 'ready';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cell.className = cns.join(' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new test
|
|
||||||
*/
|
|
||||||
test: function(name, f) {
|
|
||||||
// Create the Test object
|
|
||||||
var test = new Test(name, f);
|
|
||||||
JSLitmus._tests.push(test);
|
|
||||||
|
|
||||||
// Re-render if the test state changes
|
|
||||||
test.onChange = JSLitmus.renderTest;
|
|
||||||
|
|
||||||
// Run the next test if this one finished
|
|
||||||
test.onStop = function(test) {
|
|
||||||
if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test);
|
|
||||||
JSLitmus.currentTest = null;
|
|
||||||
JSLitmus._nextTest();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the new test
|
|
||||||
this.renderTest(test);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all tests to the run queue
|
|
||||||
*/
|
|
||||||
runAll: function(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
var reverse = e && e.shiftKey, len = JSLitmus._tests.length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all tests from the run queue. The current test has to finish on
|
|
||||||
* it's own though
|
|
||||||
*/
|
|
||||||
stop: function() {
|
|
||||||
while (JSLitmus._queue.length) {
|
|
||||||
var test = JSLitmus._queue.shift();
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the next test in the run queue
|
|
||||||
*/
|
|
||||||
_nextTest: function() {
|
|
||||||
if (!JSLitmus.currentTest) {
|
|
||||||
var test = JSLitmus._queue.shift();
|
|
||||||
if (test) {
|
|
||||||
jsl.$('stop_button').disabled = false;
|
|
||||||
JSLitmus.currentTest = test;
|
|
||||||
test.run();
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
if (JSLitmus.onTestStart) JSLitmus.onTestStart(test);
|
|
||||||
} else {
|
|
||||||
jsl.$('stop_button').disabled = true;
|
|
||||||
JSLitmus.renderChart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a test to the run queue
|
|
||||||
*/
|
|
||||||
_queueTest: function(test) {
|
|
||||||
if (jsl.indexOf(JSLitmus._queue, test) >= 0) return;
|
|
||||||
JSLitmus._queue.push(test);
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
JSLitmus._nextTest();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a Google Chart URL that shows the data for all tests
|
|
||||||
*/
|
|
||||||
chartUrl: function() {
|
|
||||||
var n = JSLitmus._tests.length, markers = [], data = [];
|
|
||||||
var d, min = 0, max = -1e10;
|
|
||||||
var normalize = jsl.$('test_normalize').checked;
|
|
||||||
|
|
||||||
// Gather test data
|
|
||||||
for (var i=0; i < JSLitmus._tests.length; i++) {
|
|
||||||
var test = JSLitmus._tests[i];
|
|
||||||
if (test.count) {
|
|
||||||
var hz = test.getHz(normalize);
|
|
||||||
var v = hz != Infinity ? hz : 0;
|
|
||||||
data.push(v);
|
|
||||||
markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' +
|
|
||||||
markers.length + ',10');
|
|
||||||
max = Math.max(v, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (markers.length <= 0) return null;
|
|
||||||
|
|
||||||
// Build chart title
|
|
||||||
var title = document.getElementsByTagName('title');
|
|
||||||
title = (title && title.length) ? title[0].innerHTML : null;
|
|
||||||
var chart_title = [];
|
|
||||||
if (title) chart_title.push(title);
|
|
||||||
chart_title.push('Ops/sec (' + platform + ')');
|
|
||||||
|
|
||||||
// Build labels
|
|
||||||
var labels = [jsl.toLabel(min), jsl.toLabel(max)];
|
|
||||||
|
|
||||||
var w = 250, bw = 15;
|
|
||||||
var bs = 5;
|
|
||||||
var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
|
|
||||||
|
|
||||||
var params = {
|
|
||||||
chtt: escape(chart_title.join('|')),
|
|
||||||
chts: '000000,10',
|
|
||||||
cht: 'bhg', // chart type
|
|
||||||
chd: 't:' + data.join(','), // data set
|
|
||||||
chds: min + ',' + max, // max/min of data
|
|
||||||
chxt: 'x', // label axes
|
|
||||||
chxl: '0:|' + labels.join('|'), // labels
|
|
||||||
chsp: '0,1',
|
|
||||||
chm: markers.join('|'), // test names
|
|
||||||
chbh: [bw, 0, bs].join(','), // bar widths
|
|
||||||
// chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
|
|
||||||
chs: w + 'x' + h
|
|
||||||
};
|
|
||||||
return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JSLitmus._init();
|
|
||||||
})();
|
|
||||||
481
vendor/backbone/test/vendor/json2.js
vendored
481
vendor/backbone/test/vendor/json2.js
vendored
@@ -1,481 +0,0 @@
|
|||||||
/*
|
|
||||||
http://www.JSON.org/json2.js
|
|
||||||
2009-09-29
|
|
||||||
|
|
||||||
Public Domain.
|
|
||||||
|
|
||||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
|
||||||
|
|
||||||
See http://www.JSON.org/js.html
|
|
||||||
|
|
||||||
|
|
||||||
This code should be minified before deployment.
|
|
||||||
See http://javascript.crockford.com/jsmin.html
|
|
||||||
|
|
||||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
|
||||||
NOT CONTROL.
|
|
||||||
|
|
||||||
|
|
||||||
This file creates a global JSON object containing two methods: stringify
|
|
||||||
and parse.
|
|
||||||
|
|
||||||
JSON.stringify(value, replacer, space)
|
|
||||||
value any JavaScript value, usually an object or array.
|
|
||||||
|
|
||||||
replacer an optional parameter that determines how object
|
|
||||||
values are stringified for objects. It can be a
|
|
||||||
function or an array of strings.
|
|
||||||
|
|
||||||
space an optional parameter that specifies the indentation
|
|
||||||
of nested structures. If it is omitted, the text will
|
|
||||||
be packed without extra whitespace. If it is a number,
|
|
||||||
it will specify the number of spaces to indent at each
|
|
||||||
level. If it is a string (such as '\t' or ' '),
|
|
||||||
it contains the characters used to indent at each level.
|
|
||||||
|
|
||||||
This method produces a JSON text from a JavaScript value.
|
|
||||||
|
|
||||||
When an object value is found, if the object contains a toJSON
|
|
||||||
method, its toJSON method will be called and the result will be
|
|
||||||
stringified. A toJSON method does not serialize: it returns the
|
|
||||||
value represented by the name/value pair that should be serialized,
|
|
||||||
or undefined if nothing should be serialized. The toJSON method
|
|
||||||
will be passed the key associated with the value, and this will be
|
|
||||||
bound to the value
|
|
||||||
|
|
||||||
For example, this would serialize Dates as ISO strings.
|
|
||||||
|
|
||||||
Date.prototype.toJSON = function (key) {
|
|
||||||
function f(n) {
|
|
||||||
// Format integers to have at least two digits.
|
|
||||||
return n < 10 ? '0' + n : n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getUTCFullYear() + '-' +
|
|
||||||
f(this.getUTCMonth() + 1) + '-' +
|
|
||||||
f(this.getUTCDate()) + 'T' +
|
|
||||||
f(this.getUTCHours()) + ':' +
|
|
||||||
f(this.getUTCMinutes()) + ':' +
|
|
||||||
f(this.getUTCSeconds()) + 'Z';
|
|
||||||
};
|
|
||||||
|
|
||||||
You can provide an optional replacer method. It will be passed the
|
|
||||||
key and value of each member, with this bound to the containing
|
|
||||||
object. The value that is returned from your method will be
|
|
||||||
serialized. If your method returns undefined, then the member will
|
|
||||||
be excluded from the serialization.
|
|
||||||
|
|
||||||
If the replacer parameter is an array of strings, then it will be
|
|
||||||
used to select the members to be serialized. It filters the results
|
|
||||||
such that only members with keys listed in the replacer array are
|
|
||||||
stringified.
|
|
||||||
|
|
||||||
Values that do not have JSON representations, such as undefined or
|
|
||||||
functions, will not be serialized. Such values in objects will be
|
|
||||||
dropped; in arrays they will be replaced with null. You can use
|
|
||||||
a replacer function to replace those with JSON values.
|
|
||||||
JSON.stringify(undefined) returns undefined.
|
|
||||||
|
|
||||||
The optional space parameter produces a stringification of the
|
|
||||||
value that is filled with line breaks and indentation to make it
|
|
||||||
easier to read.
|
|
||||||
|
|
||||||
If the space parameter is a non-empty string, then that string will
|
|
||||||
be used for indentation. If the space parameter is a number, then
|
|
||||||
the indentation will be that many spaces.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
|
||||||
// text is '["e",{"pluribus":"unum"}]'
|
|
||||||
|
|
||||||
|
|
||||||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
|
||||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
|
||||||
|
|
||||||
text = JSON.stringify([new Date()], function (key, value) {
|
|
||||||
return this[key] instanceof Date ?
|
|
||||||
'Date(' + this[key] + ')' : value;
|
|
||||||
});
|
|
||||||
// text is '["Date(---current time---)"]'
|
|
||||||
|
|
||||||
|
|
||||||
JSON.parse(text, reviver)
|
|
||||||
This method parses a JSON text to produce an object or array.
|
|
||||||
It can throw a SyntaxError exception.
|
|
||||||
|
|
||||||
The optional reviver parameter is a function that can filter and
|
|
||||||
transform the results. It receives each of the keys and values,
|
|
||||||
and its return value is used instead of the original value.
|
|
||||||
If it returns what it received, then the structure is not modified.
|
|
||||||
If it returns undefined then the member is deleted.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
// Parse the text. Values that look like ISO date strings will
|
|
||||||
// be converted to Date objects.
|
|
||||||
|
|
||||||
myData = JSON.parse(text, function (key, value) {
|
|
||||||
var a;
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
a =
|
|
||||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
|
||||||
if (a) {
|
|
||||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
|
||||||
+a[5], +a[6]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
|
||||||
var d;
|
|
||||||
if (typeof value === 'string' &&
|
|
||||||
value.slice(0, 5) === 'Date(' &&
|
|
||||||
value.slice(-1) === ')') {
|
|
||||||
d = new Date(value.slice(5, -1));
|
|
||||||
if (d) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
This is a reference implementation. You are free to copy, modify, or
|
|
||||||
redistribute.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*jslint evil: true, strict: false */
|
|
||||||
|
|
||||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
|
||||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
|
||||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
|
||||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
|
||||||
test, toJSON, toString, valueOf
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Create a JSON object only if one does not already exist. We create the
|
|
||||||
// methods in a closure to avoid creating global variables.
|
|
||||||
|
|
||||||
if (!this.JSON) {
|
|
||||||
this.JSON = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
function f(n) {
|
|
||||||
// Format integers to have at least two digits.
|
|
||||||
return n < 10 ? '0' + n : n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof Date.prototype.toJSON !== 'function') {
|
|
||||||
|
|
||||||
Date.prototype.toJSON = function (key) {
|
|
||||||
|
|
||||||
return isFinite(this.valueOf()) ?
|
|
||||||
this.getUTCFullYear() + '-' +
|
|
||||||
f(this.getUTCMonth() + 1) + '-' +
|
|
||||||
f(this.getUTCDate()) + 'T' +
|
|
||||||
f(this.getUTCHours()) + ':' +
|
|
||||||
f(this.getUTCMinutes()) + ':' +
|
|
||||||
f(this.getUTCSeconds()) + 'Z' : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype.toJSON =
|
|
||||||
Number.prototype.toJSON =
|
|
||||||
Boolean.prototype.toJSON = function (key) {
|
|
||||||
return this.valueOf();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
|
||||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
|
||||||
gap,
|
|
||||||
indent,
|
|
||||||
meta = { // table of character substitutions
|
|
||||||
'\b': '\\b',
|
|
||||||
'\t': '\\t',
|
|
||||||
'\n': '\\n',
|
|
||||||
'\f': '\\f',
|
|
||||||
'\r': '\\r',
|
|
||||||
'"' : '\\"',
|
|
||||||
'\\': '\\\\'
|
|
||||||
},
|
|
||||||
rep;
|
|
||||||
|
|
||||||
|
|
||||||
function quote(string) {
|
|
||||||
|
|
||||||
// If the string contains no control characters, no quote characters, and no
|
|
||||||
// backslash characters, then we can safely slap some quotes around it.
|
|
||||||
// Otherwise we must also replace the offending characters with safe escape
|
|
||||||
// sequences.
|
|
||||||
|
|
||||||
escapable.lastIndex = 0;
|
|
||||||
return escapable.test(string) ?
|
|
||||||
'"' + string.replace(escapable, function (a) {
|
|
||||||
var c = meta[a];
|
|
||||||
return typeof c === 'string' ? c :
|
|
||||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
}) + '"' :
|
|
||||||
'"' + string + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function str(key, holder) {
|
|
||||||
|
|
||||||
// Produce a string from holder[key].
|
|
||||||
|
|
||||||
var i, // The loop counter.
|
|
||||||
k, // The member key.
|
|
||||||
v, // The member value.
|
|
||||||
length,
|
|
||||||
mind = gap,
|
|
||||||
partial,
|
|
||||||
value = holder[key];
|
|
||||||
|
|
||||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
|
||||||
|
|
||||||
if (value && typeof value === 'object' &&
|
|
||||||
typeof value.toJSON === 'function') {
|
|
||||||
value = value.toJSON(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were called with a replacer function, then call the replacer to
|
|
||||||
// obtain a replacement value.
|
|
||||||
|
|
||||||
if (typeof rep === 'function') {
|
|
||||||
value = rep.call(holder, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// What happens next depends on the value's type.
|
|
||||||
|
|
||||||
switch (typeof value) {
|
|
||||||
case 'string':
|
|
||||||
return quote(value);
|
|
||||||
|
|
||||||
case 'number':
|
|
||||||
|
|
||||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
|
||||||
|
|
||||||
return isFinite(value) ? String(value) : 'null';
|
|
||||||
|
|
||||||
case 'boolean':
|
|
||||||
case 'null':
|
|
||||||
|
|
||||||
// If the value is a boolean or null, convert it to a string. Note:
|
|
||||||
// typeof null does not produce 'null'. The case is included here in
|
|
||||||
// the remote chance that this gets fixed someday.
|
|
||||||
|
|
||||||
return String(value);
|
|
||||||
|
|
||||||
// If the type is 'object', we might be dealing with an object or an array or
|
|
||||||
// null.
|
|
||||||
|
|
||||||
case 'object':
|
|
||||||
|
|
||||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
|
||||||
// so watch out for that case.
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make an array to hold the partial results of stringifying this object value.
|
|
||||||
|
|
||||||
gap += indent;
|
|
||||||
partial = [];
|
|
||||||
|
|
||||||
// Is the value an array?
|
|
||||||
|
|
||||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
|
||||||
|
|
||||||
// The value is an array. Stringify every element. Use null as a placeholder
|
|
||||||
// for non-JSON values.
|
|
||||||
|
|
||||||
length = value.length;
|
|
||||||
for (i = 0; i < length; i += 1) {
|
|
||||||
partial[i] = str(i, value) || 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join all of the elements together, separated with commas, and wrap them in
|
|
||||||
// brackets.
|
|
||||||
|
|
||||||
v = partial.length === 0 ? '[]' :
|
|
||||||
gap ? '[\n' + gap +
|
|
||||||
partial.join(',\n' + gap) + '\n' +
|
|
||||||
mind + ']' :
|
|
||||||
'[' + partial.join(',') + ']';
|
|
||||||
gap = mind;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the replacer is an array, use it to select the members to be stringified.
|
|
||||||
|
|
||||||
if (rep && typeof rep === 'object') {
|
|
||||||
length = rep.length;
|
|
||||||
for (i = 0; i < length; i += 1) {
|
|
||||||
k = rep[i];
|
|
||||||
if (typeof k === 'string') {
|
|
||||||
v = str(k, value);
|
|
||||||
if (v) {
|
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Otherwise, iterate through all of the keys in the object.
|
|
||||||
|
|
||||||
for (k in value) {
|
|
||||||
if (Object.hasOwnProperty.call(value, k)) {
|
|
||||||
v = str(k, value);
|
|
||||||
if (v) {
|
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join all of the member texts together, separated with commas,
|
|
||||||
// and wrap them in braces.
|
|
||||||
|
|
||||||
v = partial.length === 0 ? '{}' :
|
|
||||||
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
|
|
||||||
mind + '}' : '{' + partial.join(',') + '}';
|
|
||||||
gap = mind;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the JSON object does not yet have a stringify method, give it one.
|
|
||||||
|
|
||||||
if (typeof JSON.stringify !== 'function') {
|
|
||||||
JSON.stringify = function (value, replacer, space) {
|
|
||||||
|
|
||||||
// The stringify method takes a value and an optional replacer, and an optional
|
|
||||||
// space parameter, and returns a JSON text. The replacer can be a function
|
|
||||||
// that can replace values, or an array of strings that will select the keys.
|
|
||||||
// A default replacer method can be provided. Use of the space parameter can
|
|
||||||
// produce text that is more easily readable.
|
|
||||||
|
|
||||||
var i;
|
|
||||||
gap = '';
|
|
||||||
indent = '';
|
|
||||||
|
|
||||||
// If the space parameter is a number, make an indent string containing that
|
|
||||||
// many spaces.
|
|
||||||
|
|
||||||
if (typeof space === 'number') {
|
|
||||||
for (i = 0; i < space; i += 1) {
|
|
||||||
indent += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the space parameter is a string, it will be used as the indent string.
|
|
||||||
|
|
||||||
} else if (typeof space === 'string') {
|
|
||||||
indent = space;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is a replacer, it must be a function or an array.
|
|
||||||
// Otherwise, throw an error.
|
|
||||||
|
|
||||||
rep = replacer;
|
|
||||||
if (replacer && typeof replacer !== 'function' &&
|
|
||||||
(typeof replacer !== 'object' ||
|
|
||||||
typeof replacer.length !== 'number')) {
|
|
||||||
throw new Error('JSON.stringify');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a fake root object containing our value under the key of ''.
|
|
||||||
// Return the result of stringifying the value.
|
|
||||||
|
|
||||||
return str('', {'': value});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// If the JSON object does not yet have a parse method, give it one.
|
|
||||||
|
|
||||||
if (typeof JSON.parse !== 'function') {
|
|
||||||
JSON.parse = function (text, reviver) {
|
|
||||||
|
|
||||||
// The parse method takes a text and an optional reviver function, and returns
|
|
||||||
// a JavaScript value if the text is a valid JSON text.
|
|
||||||
|
|
||||||
var j;
|
|
||||||
|
|
||||||
function walk(holder, key) {
|
|
||||||
|
|
||||||
// The walk method is used to recursively walk the resulting structure so
|
|
||||||
// that modifications can be made.
|
|
||||||
|
|
||||||
var k, v, value = holder[key];
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
for (k in value) {
|
|
||||||
if (Object.hasOwnProperty.call(value, k)) {
|
|
||||||
v = walk(value, k);
|
|
||||||
if (v !== undefined) {
|
|
||||||
value[k] = v;
|
|
||||||
} else {
|
|
||||||
delete value[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reviver.call(holder, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Parsing happens in four stages. In the first stage, we replace certain
|
|
||||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
|
||||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
|
||||||
|
|
||||||
cx.lastIndex = 0;
|
|
||||||
if (cx.test(text)) {
|
|
||||||
text = text.replace(cx, function (a) {
|
|
||||||
return '\\u' +
|
|
||||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the second stage, we run the text against regular expressions that look
|
|
||||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
|
||||||
// because they can cause invocation, and '=' because it can cause mutation.
|
|
||||||
// But just to be safe, we want to reject all unexpected forms.
|
|
||||||
|
|
||||||
// We split the second stage into 4 regexp operations in order to work around
|
|
||||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
|
||||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
|
||||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
|
||||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
|
||||||
// we look to see that the remaining characters are only whitespace or ']' or
|
|
||||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
|
||||||
|
|
||||||
if (/^[\],:{}\s]*$/.
|
|
||||||
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
|
|
||||||
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
|
|
||||||
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
|
||||||
|
|
||||||
// In the third stage we use the eval function to compile the text into a
|
|
||||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
|
||||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
|
||||||
// in parens to eliminate the ambiguity.
|
|
||||||
|
|
||||||
j = eval('(' + text + ')');
|
|
||||||
|
|
||||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
|
||||||
// each name/value pair to a reviver function for possible transformation.
|
|
||||||
|
|
||||||
return typeof reviver === 'function' ?
|
|
||||||
walk({'': j}, '') : j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
|
||||||
|
|
||||||
throw new SyntaxError('JSON.parse');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
20
vendor/json3/LICENSE
vendored
Normal file
20
vendor/json3/LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2012 Kit Cambridge.
|
||||||
|
http://kitcambridge.github.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.
|
||||||
124
vendor/json3/README.md
vendored
Normal file
124
vendor/json3/README.md
vendored
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# JSON 3 #
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**JSON 3** is a modern JSON implementation compatible with a variety of JavaScript platforms, including Internet Explorer 6, Opera 7, Safari 2, and Netscape 6. The current version is **3.2.4**.
|
||||||
|
|
||||||
|
- [Development Version](http://bestiejs.github.com/json3/lib/json3.js) *(36.5 KB; uncompressed with comments)*
|
||||||
|
- [Production Version](http://bestiejs.github.com/json3/lib/json3.min.js) *(3.0 KB; compressed and `gzip`-ped)*
|
||||||
|
|
||||||
|
[JSON](http://json.org/) is a language-independent data interchange format based on a loose subset of the JavaScript grammar. Originally popularized by [Douglas Crockford](http://www.crockford.com/), the format was standardized in the [fifth edition](http://es5.github.com/) of the ECMAScript specification. The 5.1 edition, ratified in June 2011, incorporates several modifications to the grammar pertaining to the serialization of dates.
|
||||||
|
|
||||||
|
JSON 3 exposes two functions: `stringify()` for [serializing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify) a JavaScript value to JSON, and `parse()` for [producing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse) a JavaScript value from a JSON source string. It is a **drop-in replacement** for [JSON 2](http://json.org/js). The functions behave exactly as described in the ECMAScript spec, **except** for the date serialization discrepancy noted below.
|
||||||
|
|
||||||
|
The JSON 3 parser does **not** use `eval` or regular expressions. This provides security and performance benefits in obsolete and mobile environments, where the margin is particularly significant. The complete [benchmark suite](http://jsperf.com/json3) is available on [jsPerf](http://jsperf.com/).
|
||||||
|
|
||||||
|
The project is [hosted on GitHub](http://git.io/json3), along with the [unit tests](http://bestiejs.github.com/json3/test/test_browser.html). It is part of the [BestieJS](https://github.com/bestiejs) family, a collection of best-in-class JavaScript libraries that promote cross-platform support, specification precedents, unit testing, and plenty of documentation.
|
||||||
|
|
||||||
|
# Changes from JSON 2 #
|
||||||
|
|
||||||
|
JSON 3...
|
||||||
|
|
||||||
|
* Correctly serializes primitive wrapper objects.
|
||||||
|
* Throws a `TypeError` when serializing cyclic structures (JSON 2 recurses until the call stack overflows).
|
||||||
|
* Utilizes **feature tests** to detect broken or incomplete *native* JSON implementations (JSON 2 only checks for the presence of the native functions). The tests are only executed once at runtime, so there is no additional performance cost when parsing or serializing values.
|
||||||
|
|
||||||
|
**As of v3.2.3**, JSON 3 is compatible with [Prototype](http://prototypejs.org) 1.6.1 and older.
|
||||||
|
|
||||||
|
In contrast to JSON 2, JSON 3 **does not**...
|
||||||
|
|
||||||
|
* Add `toJSON()` methods to the `Boolean`, `Number`, and `String` prototypes. These are not part of any standard, and are made redundant by the design of the `stringify()` implementation.
|
||||||
|
* Add `toJSON()` or `toISOString()` methods to `Date.prototype`. See the note about date serialization below.
|
||||||
|
|
||||||
|
## Date Serialization
|
||||||
|
|
||||||
|
**JSON 3 deviates from the specification in one important way**: it does not define `Date#toISOString()` or `Date#toJSON()`. This preserves CommonJS compatibility and avoids polluting native prototypes. Instead, date serialization is performed internally by the `stringify()` implementation: if a date object does not define a custom `toJSON()` method, it is serialized as a [simplified ISO 8601 date-time string](http://es5.github.com/#x15.9.1.15).
|
||||||
|
|
||||||
|
**Several native `Date#toJSON()` implementations produce date time strings that do *not* conform to the grammar outlined in the spec**. For instance, all versions of Safari 4, as well as JSON 2, fail to serialize extended years correctly. Furthermore, JSON 2 and older implementations omit the milliseconds from the date-time string (optional in ES 5, but required in 5.1). Finally, in all versions of Safari 4 and 5, serializing an invalid date will produce the string `"Invalid Date"`, rather than `null`. Because these environments exhibit other serialization bugs, however, JSON 3 will override the native `stringify()` implementation.
|
||||||
|
|
||||||
|
Portions of the date serialization code are adapted from the [`date-shim`](https://github.com/Yaffle/date-shim) project.
|
||||||
|
|
||||||
|
# Usage #
|
||||||
|
|
||||||
|
## Web Browsers
|
||||||
|
|
||||||
|
<script src="http://bestiejs.github.com/json3/lib/json3.min.js"></script>
|
||||||
|
<script>
|
||||||
|
JSON.stringify({"Hello": 123});
|
||||||
|
// => '{"Hello":123}'
|
||||||
|
JSON.parse("[[1, 2, 3], 1, 2, 3, 4]", function (key, value) {
|
||||||
|
if (typeof value == "number") {
|
||||||
|
value = value % 2 ? "Odd" : "Even";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
// => [["Odd", "Even", "Odd"], "Odd", "Even", "Odd", "Even"]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## CommonJS Environments
|
||||||
|
|
||||||
|
var JSON3 = require("./path/to/json3");
|
||||||
|
JSON3.parse("[1, 2, 3]");
|
||||||
|
// => [1, 2, 3]
|
||||||
|
|
||||||
|
## JavaScript Engines
|
||||||
|
|
||||||
|
load("path/to/json3.js");
|
||||||
|
JSON.stringify({"Hello": 123, "Good-bye": 456}, ["Hello"], "\t");
|
||||||
|
// => '{\n\t"Hello": 123\n}'
|
||||||
|
|
||||||
|
# Compatibility #
|
||||||
|
|
||||||
|
JSON 3 has been **tested** with the following web browsers, CommonJS environments, and JavaScript engines.
|
||||||
|
|
||||||
|
## Web Browsers
|
||||||
|
|
||||||
|
- Windows [Internet Explorer](http://www.microsoft.com/windows/internet-explorer), version 6.0 and higher
|
||||||
|
- Mozilla [Firefox](http://www.mozilla.com/firefox), version 1.0 and higher
|
||||||
|
- Apple [Safari](http://www.apple.com/safari), version 2.0 and higher
|
||||||
|
- [Opera](http://www.opera.com) 7.02 and higher
|
||||||
|
- [Mozilla](http://sillydog.org/narchive/gecko.php) 1.0, [Netscape](http://sillydog.org/narchive/) 6.2.3, and [SeaMonkey](http://www.seamonkey-project.org/) 1.0 and higher
|
||||||
|
|
||||||
|
## CommonJS Environments
|
||||||
|
|
||||||
|
- [Node](http://nodejs.org/) 0.2.6 and higher
|
||||||
|
- [RingoJS](http://ringojs.org/) 0.4 and higher
|
||||||
|
- [Narwhal](http://narwhaljs.org/) 0.3.2 and higher
|
||||||
|
|
||||||
|
## JavaScript Engines
|
||||||
|
|
||||||
|
- Mozilla [Rhino](http://www.mozilla.org/rhino) 1.5R5 and higher
|
||||||
|
- WebKit [JSC](https://trac.webkit.org/wiki/JSC)
|
||||||
|
- Google [V8](http://code.google.com/p/v8)
|
||||||
|
|
||||||
|
## Known Incompatibilities
|
||||||
|
|
||||||
|
* Attempting to serialize the `arguments` object may produce inconsistent results across environments due to specification version differences. As a workaround, please convert the `arguments` object to an array first: `JSON.stringify([].slice.call(arguments, 0))`.
|
||||||
|
|
||||||
|
## Required Native Methods
|
||||||
|
|
||||||
|
JSON 3 assumes that the following methods exist and function as described in the ECMAScript specification:
|
||||||
|
|
||||||
|
- The `Number`, `String`, `Array`, `Object`, `Date`, `SyntaxError`, and `TypeError` constructors.
|
||||||
|
- `String.fromCharCode`
|
||||||
|
- `Object#toString`
|
||||||
|
- `Function#call`
|
||||||
|
- `Math.floor`
|
||||||
|
- `Number#toString`
|
||||||
|
- `Date#valueOf`
|
||||||
|
- `String.prototype`: `indexOf`, `charCodeAt`, `charAt`, `slice`.
|
||||||
|
- `Array.prototype`: `push`, `pop`, `join`.
|
||||||
|
|
||||||
|
# Contribute #
|
||||||
|
|
||||||
|
Check out a working copy of the JSON 3 source code with [Git](http://git-scm.com/):
|
||||||
|
|
||||||
|
$ git clone git://github.com/bestiejs/json3.git
|
||||||
|
$ cd json3
|
||||||
|
$ git submodule update --init
|
||||||
|
|
||||||
|
If you'd like to contribute a feature or bug fix, you can [fork](http://help.github.com/fork-a-repo/) JSON 3, commit your changes, and [send a pull request](http://help.github.com/send-pull-requests/). Please make sure to update the unit tests in the `test` directory as well.
|
||||||
|
|
||||||
|
Alternatively, you can use the [GitHub issue tracker](https://github.com/bestiejs/json3/issues) to submit bug reports, feature requests, and questions, or send tweets to [@kitcambridge](http://twitter.com/kitcambridge).
|
||||||
|
|
||||||
|
JSON 3 is released under the [MIT License](http://kit.mit-license.org/).
|
||||||
783
vendor/json3/lib/json3.js
vendored
Normal file
783
vendor/json3/lib/json3.js
vendored
Normal file
@@ -0,0 +1,783 @@
|
|||||||
|
/*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */
|
||||||
|
;(function () {
|
||||||
|
// Convenience aliases.
|
||||||
|
var getClass = {}.toString, isProperty, forEach, undef;
|
||||||
|
|
||||||
|
// Detect the `define` function exposed by asynchronous module loaders. The
|
||||||
|
// strict `define` check is necessary for compatibility with `r.js`.
|
||||||
|
var isLoader = typeof define === "function" && define.amd, JSON3 = !isLoader && typeof exports == "object" && exports;
|
||||||
|
|
||||||
|
if (JSON3 || isLoader) {
|
||||||
|
if (typeof JSON == "object" && JSON) {
|
||||||
|
// Delegate to the native `stringify` and `parse` implementations in
|
||||||
|
// asynchronous module loaders and CommonJS environments.
|
||||||
|
if (isLoader) {
|
||||||
|
JSON3 = JSON;
|
||||||
|
} else {
|
||||||
|
JSON3.stringify = JSON.stringify;
|
||||||
|
JSON3.parse = JSON.parse;
|
||||||
|
}
|
||||||
|
} else if (isLoader) {
|
||||||
|
JSON3 = this.JSON = {};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Export for web browsers and JavaScript engines.
|
||||||
|
JSON3 = this.JSON || (this.JSON = {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local variables.
|
||||||
|
var Escapes, toPaddedString, quote, serialize;
|
||||||
|
var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source;
|
||||||
|
|
||||||
|
// Test the `Date#getUTC*` methods. Based on work by @Yaffle.
|
||||||
|
var isExtended = new Date(-3509827334573292), floor, Months, getDay;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
|
||||||
|
// results for certain dates in Opera >= 10.53.
|
||||||
|
isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() == 1 &&
|
||||||
|
// Safari < 2.0.2 stores the internal millisecond time value correctly,
|
||||||
|
// but clips the values returned by the date methods to the range of
|
||||||
|
// signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
|
||||||
|
isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
|
||||||
|
} catch (exception) {}
|
||||||
|
|
||||||
|
// Internal: Determines whether the native `JSON.stringify` and `parse`
|
||||||
|
// implementations are spec-compliant. Based on work by Ken Snyder.
|
||||||
|
function has(name) {
|
||||||
|
var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
|
||||||
|
if (all || name == "json-stringify" || name == "json-parse") {
|
||||||
|
// Test `JSON.stringify`.
|
||||||
|
if (name == "json-stringify" || all) {
|
||||||
|
if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) {
|
||||||
|
// A test function object with a custom `toJSON` method.
|
||||||
|
(value = function () {
|
||||||
|
return 1;
|
||||||
|
}).toJSON = value;
|
||||||
|
try {
|
||||||
|
stringifySupported =
|
||||||
|
// Firefox 3.1b1 and b2 serialize string, number, and boolean
|
||||||
|
// primitives as object literals.
|
||||||
|
JSON3.stringify(0) === "0" &&
|
||||||
|
// FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
|
||||||
|
// literals.
|
||||||
|
JSON3.stringify(new Number()) === "0" &&
|
||||||
|
JSON3.stringify(new String()) == '""' &&
|
||||||
|
// FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
|
||||||
|
// does not define a canonical JSON representation (this applies to
|
||||||
|
// objects with `toJSON` properties as well, *unless* they are nested
|
||||||
|
// within an object or array).
|
||||||
|
JSON3.stringify(getClass) === undef &&
|
||||||
|
// IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
|
||||||
|
// FF 3.1b3 pass this test.
|
||||||
|
JSON3.stringify(undef) === undef &&
|
||||||
|
// Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
|
||||||
|
// respectively, if the value is omitted entirely.
|
||||||
|
JSON3.stringify() === undef &&
|
||||||
|
// FF 3.1b1, 2 throw an error if the given value is not a number,
|
||||||
|
// string, array, object, Boolean, or `null` literal. This applies to
|
||||||
|
// objects with custom `toJSON` methods as well, unless they are nested
|
||||||
|
// inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
|
||||||
|
// methods entirely.
|
||||||
|
JSON3.stringify(value) === "1" &&
|
||||||
|
JSON3.stringify([value]) == "[1]" &&
|
||||||
|
// Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
|
||||||
|
// `"[null]"`.
|
||||||
|
JSON3.stringify([undef]) == "[null]" &&
|
||||||
|
// YUI 3.0.0b1 fails to serialize `null` literals.
|
||||||
|
JSON3.stringify(null) == "null" &&
|
||||||
|
// FF 3.1b1, 2 halts serialization if an array contains a function:
|
||||||
|
// `[1, true, getClass, 1]` serializes as "[1,true,],". These versions
|
||||||
|
// of Firefox also allow trailing commas in JSON objects and arrays.
|
||||||
|
// FF 3.1b3 elides non-JSON values from objects and arrays, unless they
|
||||||
|
// define custom `toJSON` methods.
|
||||||
|
JSON3.stringify([undef, getClass, null]) == "[null,null,null]" &&
|
||||||
|
// Simple serialization test. FF 3.1b1 uses Unicode escape sequences
|
||||||
|
// where character escape codes are expected (e.g., `\b` => `\u0008`).
|
||||||
|
JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized &&
|
||||||
|
// FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
|
||||||
|
JSON3.stringify(null, value) === "1" &&
|
||||||
|
JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
|
||||||
|
// JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
|
||||||
|
// serialize extended years.
|
||||||
|
JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
|
||||||
|
// The milliseconds are optional in ES 5, but required in 5.1.
|
||||||
|
JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
|
||||||
|
// Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
|
||||||
|
// four-digit years instead of six-digit years. Credits: @Yaffle.
|
||||||
|
JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
|
||||||
|
// Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
|
||||||
|
// values less than 1000. Credits: @Yaffle.
|
||||||
|
JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
|
||||||
|
} catch (exception) {
|
||||||
|
stringifySupported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all) {
|
||||||
|
return stringifySupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Test `JSON.parse`.
|
||||||
|
if (name == "json-parse" || all) {
|
||||||
|
if (typeof JSON3.parse == "function") {
|
||||||
|
try {
|
||||||
|
// FF 3.1b1, b2 will throw an exception if a bare literal is provided.
|
||||||
|
// Conforming implementations should also coerce the initial argument to
|
||||||
|
// a string prior to parsing.
|
||||||
|
if (JSON3.parse("0") === 0 && !JSON3.parse(false)) {
|
||||||
|
// Simple parsing test.
|
||||||
|
value = JSON3.parse(serialized);
|
||||||
|
if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) {
|
||||||
|
try {
|
||||||
|
// Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
|
||||||
|
parseSupported = !JSON3.parse('"\t"');
|
||||||
|
} catch (exception) {}
|
||||||
|
if (parseSupported) {
|
||||||
|
try {
|
||||||
|
// FF 4.0 and 4.0.1 allow leading `+` signs, and leading and
|
||||||
|
// trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also
|
||||||
|
// allow certain octal literals.
|
||||||
|
parseSupported = JSON3.parse("01") != 1;
|
||||||
|
} catch (exception) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception) {
|
||||||
|
parseSupported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all) {
|
||||||
|
return parseSupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringifySupported && parseSupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has("json")) {
|
||||||
|
// Define additional utility methods if the `Date` methods are buggy.
|
||||||
|
if (!isExtended) {
|
||||||
|
floor = Math.floor;
|
||||||
|
// A mapping between the months of the year and the number of days between
|
||||||
|
// January 1st and the first of the respective month.
|
||||||
|
Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
||||||
|
// Internal: Calculates the number of days between the Unix epoch and the
|
||||||
|
// first day of the given month.
|
||||||
|
getDay = function (year, month) {
|
||||||
|
return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal: Determines if a property is a direct property of the given
|
||||||
|
// object. Delegates to the native `Object#hasOwnProperty` method.
|
||||||
|
if (!(isProperty = {}.hasOwnProperty)) {
|
||||||
|
isProperty = function (property) {
|
||||||
|
var members = {}, constructor;
|
||||||
|
if ((members.__proto__ = null, members.__proto__ = {
|
||||||
|
// The *proto* property cannot be set multiple times in recent
|
||||||
|
// versions of Firefox and SeaMonkey.
|
||||||
|
"toString": 1
|
||||||
|
}, members).toString != getClass) {
|
||||||
|
// Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
|
||||||
|
// supports the mutable *proto* property.
|
||||||
|
isProperty = function (property) {
|
||||||
|
// Capture and break the object's prototype chain (see section 8.6.2
|
||||||
|
// of the ES 5.1 spec). The parenthesized expression prevents an
|
||||||
|
// unsafe transformation by the Closure Compiler.
|
||||||
|
var original = this.__proto__, result = property in (this.__proto__ = null, this);
|
||||||
|
// Restore the original prototype chain.
|
||||||
|
this.__proto__ = original;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Capture a reference to the top-level `Object` constructor.
|
||||||
|
constructor = members.constructor;
|
||||||
|
// Use the `constructor` property to simulate `Object#hasOwnProperty` in
|
||||||
|
// other environments.
|
||||||
|
isProperty = function (property) {
|
||||||
|
var parent = (this.constructor || constructor).prototype;
|
||||||
|
return property in this && !(property in parent && this[property] === parent[property]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
members = null;
|
||||||
|
return isProperty.call(this, property);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal: Normalizes the `for...in` iteration algorithm across
|
||||||
|
// environments. Each enumerated key is yielded to a `callback` function.
|
||||||
|
forEach = function (object, callback) {
|
||||||
|
var size = 0, Properties, members, property, forEach;
|
||||||
|
|
||||||
|
// Tests for bugs in the current environment's `for...in` algorithm. The
|
||||||
|
// `valueOf` property inherits the non-enumerable flag from
|
||||||
|
// `Object.prototype` in older versions of IE, Netscape, and Mozilla.
|
||||||
|
(Properties = function () {
|
||||||
|
this.valueOf = 0;
|
||||||
|
}).prototype.valueOf = 0;
|
||||||
|
|
||||||
|
// Iterate over a new instance of the `Properties` class.
|
||||||
|
members = new Properties();
|
||||||
|
for (property in members) {
|
||||||
|
// Ignore all properties inherited from `Object.prototype`.
|
||||||
|
if (isProperty.call(members, property)) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Properties = members = null;
|
||||||
|
|
||||||
|
// Normalize the iteration algorithm.
|
||||||
|
if (!size) {
|
||||||
|
// A list of non-enumerable properties inherited from `Object.prototype`.
|
||||||
|
members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
|
||||||
|
// IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
|
||||||
|
// properties.
|
||||||
|
forEach = function (object, callback) {
|
||||||
|
var isFunction = getClass.call(object) == "[object Function]", property, length;
|
||||||
|
for (property in object) {
|
||||||
|
// Gecko <= 1.0 enumerates the `prototype` property of functions under
|
||||||
|
// certain conditions; IE does not.
|
||||||
|
if (!(isFunction && property == "prototype") && isProperty.call(object, property)) {
|
||||||
|
callback(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Manually invoke the callback for each non-enumerable property.
|
||||||
|
for (length = members.length; property = members[--length]; isProperty.call(object, property) && callback(property));
|
||||||
|
};
|
||||||
|
} else if (size == 2) {
|
||||||
|
// Safari <= 2.0.4 enumerates shadowed properties twice.
|
||||||
|
forEach = function (object, callback) {
|
||||||
|
// Create a set of iterated properties.
|
||||||
|
var members = {}, isFunction = getClass.call(object) == "[object Function]", property;
|
||||||
|
for (property in object) {
|
||||||
|
// Store each property name to prevent double enumeration. The
|
||||||
|
// `prototype` property of functions is not enumerated due to cross-
|
||||||
|
// environment inconsistencies.
|
||||||
|
if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
|
||||||
|
callback(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// No bugs detected; use the standard `for...in` algorithm.
|
||||||
|
forEach = function (object, callback) {
|
||||||
|
var isFunction = getClass.call(object) == "[object Function]", property, isConstructor;
|
||||||
|
for (property in object) {
|
||||||
|
if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
|
||||||
|
callback(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Manually invoke the callback for the `constructor` property due to
|
||||||
|
// cross-environment inconsistencies.
|
||||||
|
if (isConstructor || isProperty.call(object, (property = "constructor"))) {
|
||||||
|
callback(property);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return forEach(object, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Public: Serializes a JavaScript `value` as a JSON string. The optional
|
||||||
|
// `filter` argument may specify either a function that alters how object and
|
||||||
|
// array members are serialized, or an array of strings and numbers that
|
||||||
|
// indicates which properties should be serialized. The optional `width`
|
||||||
|
// argument may be either a string or number that specifies the indentation
|
||||||
|
// level of the output.
|
||||||
|
if (!has("json-stringify")) {
|
||||||
|
// Internal: A map of control characters and their escaped equivalents.
|
||||||
|
Escapes = {
|
||||||
|
"\\": "\\\\",
|
||||||
|
'"': '\\"',
|
||||||
|
"\b": "\\b",
|
||||||
|
"\f": "\\f",
|
||||||
|
"\n": "\\n",
|
||||||
|
"\r": "\\r",
|
||||||
|
"\t": "\\t"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Converts `value` into a zero-padded string such that its
|
||||||
|
// length is at least equal to `width`. The `width` must be <= 6.
|
||||||
|
toPaddedString = function (width, value) {
|
||||||
|
// The `|| 0` expression is necessary to work around a bug in
|
||||||
|
// Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
|
||||||
|
return ("000000" + (value || 0)).slice(-width);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Double-quotes a string `value`, replacing all ASCII control
|
||||||
|
// characters (characters with code unit values between 0 and 31) with
|
||||||
|
// their escaped equivalents. This is an implementation of the
|
||||||
|
// `Quote(value)` operation defined in ES 5.1 section 15.12.3.
|
||||||
|
quote = function (value) {
|
||||||
|
var result = '"', index = 0, symbol;
|
||||||
|
for (; symbol = value.charAt(index); index++) {
|
||||||
|
// Escape the reverse solidus, double quote, backspace, form feed, line
|
||||||
|
// feed, carriage return, and tab characters.
|
||||||
|
result += '\\"\b\f\n\r\t'.indexOf(symbol) > -1 ? Escapes[symbol] :
|
||||||
|
// If the character is a control character, append its Unicode escape
|
||||||
|
// sequence; otherwise, append the character as-is.
|
||||||
|
(Escapes[symbol] = symbol < " " ? "\\u00" + toPaddedString(2, symbol.charCodeAt(0).toString(16)) : symbol);
|
||||||
|
}
|
||||||
|
return result + '"';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Recursively serializes an object. Implements the
|
||||||
|
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
|
||||||
|
serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
|
||||||
|
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
|
||||||
|
if (typeof value == "object" && value) {
|
||||||
|
className = getClass.call(value);
|
||||||
|
if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
|
||||||
|
if (value > -1 / 0 && value < 1 / 0) {
|
||||||
|
// Dates are serialized according to the `Date#toJSON` method
|
||||||
|
// specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
|
||||||
|
// for the ISO 8601 date time string format.
|
||||||
|
if (getDay) {
|
||||||
|
// Manually compute the year, month, date, hours, minutes,
|
||||||
|
// seconds, and milliseconds if the `getUTC*` methods are
|
||||||
|
// buggy. Adapted from @Yaffle's `date-shim` project.
|
||||||
|
date = floor(value / 864e5);
|
||||||
|
for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
|
||||||
|
for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
|
||||||
|
date = 1 + date - getDay(year, month);
|
||||||
|
// The `time` value specifies the time within the day (see ES
|
||||||
|
// 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
|
||||||
|
// to compute `A modulo B`, as the `%` operator does not
|
||||||
|
// correspond to the `modulo` operation for negative numbers.
|
||||||
|
time = (value % 864e5 + 864e5) % 864e5;
|
||||||
|
// The hours, minutes, seconds, and milliseconds are obtained by
|
||||||
|
// decomposing the time within the day. See section 15.9.1.10.
|
||||||
|
hours = floor(time / 36e5) % 24;
|
||||||
|
minutes = floor(time / 6e4) % 60;
|
||||||
|
seconds = floor(time / 1e3) % 60;
|
||||||
|
milliseconds = time % 1e3;
|
||||||
|
} else {
|
||||||
|
year = value.getUTCFullYear();
|
||||||
|
month = value.getUTCMonth();
|
||||||
|
date = value.getUTCDate();
|
||||||
|
hours = value.getUTCHours();
|
||||||
|
minutes = value.getUTCMinutes();
|
||||||
|
seconds = value.getUTCSeconds();
|
||||||
|
milliseconds = value.getUTCMilliseconds();
|
||||||
|
}
|
||||||
|
// Serialize extended years correctly.
|
||||||
|
value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
|
||||||
|
"-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
|
||||||
|
// Months, dates, hours, minutes, and seconds should have two
|
||||||
|
// digits; milliseconds should have three.
|
||||||
|
"T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
|
||||||
|
// Milliseconds are optional in ES 5.0, but required in 5.1.
|
||||||
|
"." + toPaddedString(3, milliseconds) + "Z";
|
||||||
|
} else {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
} else if (typeof value.toJSON == "function" && ((className != "[object Number]" && className != "[object String]" && className != "[object Array]") || isProperty.call(value, "toJSON"))) {
|
||||||
|
// Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
|
||||||
|
// `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
|
||||||
|
// ignores all `toJSON` methods on these objects unless they are
|
||||||
|
// defined directly on an instance.
|
||||||
|
value = value.toJSON(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (callback) {
|
||||||
|
// If a replacement function was provided, call it to obtain the value
|
||||||
|
// for serialization.
|
||||||
|
value = callback.call(object, property, value);
|
||||||
|
}
|
||||||
|
if (value === null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
className = getClass.call(value);
|
||||||
|
if (className == "[object Boolean]") {
|
||||||
|
// Booleans are represented literally.
|
||||||
|
return "" + value;
|
||||||
|
} else if (className == "[object Number]") {
|
||||||
|
// JSON numbers must be finite. `Infinity` and `NaN` are serialized as
|
||||||
|
// `"null"`.
|
||||||
|
return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
|
||||||
|
} else if (className == "[object String]") {
|
||||||
|
// Strings are double-quoted and escaped.
|
||||||
|
return quote(value);
|
||||||
|
}
|
||||||
|
// Recursively serialize objects and arrays.
|
||||||
|
if (typeof value == "object") {
|
||||||
|
// Check for cyclic structures. This is a linear search; performance
|
||||||
|
// is inversely proportional to the number of unique nested objects.
|
||||||
|
for (length = stack.length; length--;) {
|
||||||
|
if (stack[length] === value) {
|
||||||
|
// Cyclic structures cannot be serialized by `JSON.stringify`.
|
||||||
|
throw TypeError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the object to the stack of traversed objects.
|
||||||
|
stack.push(value);
|
||||||
|
results = [];
|
||||||
|
// Save the current indentation level and indent one additional level.
|
||||||
|
prefix = indentation;
|
||||||
|
indentation += whitespace;
|
||||||
|
if (className == "[object Array]") {
|
||||||
|
// Recursively serialize array elements.
|
||||||
|
for (index = 0, length = value.length; index < length; any || (any = true), index++) {
|
||||||
|
element = serialize(index, value, callback, properties, whitespace, indentation, stack);
|
||||||
|
results.push(element === undef ? "null" : element);
|
||||||
|
}
|
||||||
|
result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
|
||||||
|
} else {
|
||||||
|
// Recursively serialize object members. Members are selected from
|
||||||
|
// either a user-specified list of property names, or the object
|
||||||
|
// itself.
|
||||||
|
forEach(properties || value, function (property) {
|
||||||
|
var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
|
||||||
|
if (element !== undef) {
|
||||||
|
// According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
|
||||||
|
// is not the empty string, let `member` {quote(property) + ":"}
|
||||||
|
// be the concatenation of `member` and the `space` character."
|
||||||
|
// The "`space` character" refers to the literal space
|
||||||
|
// character, not the `space` {width} argument provided to
|
||||||
|
// `JSON.stringify`.
|
||||||
|
results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
|
||||||
|
}
|
||||||
|
any || (any = true);
|
||||||
|
});
|
||||||
|
result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
|
||||||
|
}
|
||||||
|
// Remove the object from the traversed object stack.
|
||||||
|
stack.pop();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
|
||||||
|
JSON3.stringify = function (source, filter, width) {
|
||||||
|
var whitespace, callback, properties, index, length, value;
|
||||||
|
if (typeof filter == "function" || typeof filter == "object" && filter) {
|
||||||
|
if (getClass.call(filter) == "[object Function]") {
|
||||||
|
callback = filter;
|
||||||
|
} else if (getClass.call(filter) == "[object Array]") {
|
||||||
|
// Convert the property names array into a makeshift set.
|
||||||
|
properties = {};
|
||||||
|
for (index = 0, length = filter.length; index < length; value = filter[index++], ((getClass.call(value) == "[object String]" || getClass.call(value) == "[object Number]") && (properties[value] = 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (width) {
|
||||||
|
if (getClass.call(width) == "[object Number]") {
|
||||||
|
// Convert the `width` to an integer and create a string containing
|
||||||
|
// `width` number of space characters.
|
||||||
|
if ((width -= width % 1) > 0) {
|
||||||
|
for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
|
||||||
|
}
|
||||||
|
} else if (getClass.call(width) == "[object String]") {
|
||||||
|
whitespace = width.length <= 10 ? width : width.slice(0, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Opera <= 7.54u2 discards the values associated with empty string keys
|
||||||
|
// (`""`) only if they are used directly within an object member list
|
||||||
|
// (e.g., `!("" in { "": 1})`).
|
||||||
|
return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: Parses a JSON source string.
|
||||||
|
if (!has("json-parse")) {
|
||||||
|
fromCharCode = String.fromCharCode;
|
||||||
|
// Internal: A map of escaped control characters and their unescaped
|
||||||
|
// equivalents.
|
||||||
|
Unescapes = {
|
||||||
|
"\\": "\\",
|
||||||
|
'"': '"',
|
||||||
|
"/": "/",
|
||||||
|
"b": "\b",
|
||||||
|
"t": "\t",
|
||||||
|
"n": "\n",
|
||||||
|
"f": "\f",
|
||||||
|
"r": "\r"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Resets the parser state and throws a `SyntaxError`.
|
||||||
|
abort = function() {
|
||||||
|
Index = Source = null;
|
||||||
|
throw SyntaxError();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Returns the next token, or `"$"` if the parser has reached
|
||||||
|
// the end of the source string. A token may be a string, number, `null`
|
||||||
|
// literal, or Boolean literal.
|
||||||
|
lex = function () {
|
||||||
|
var source = Source, length = source.length, symbol, value, begin, position, sign;
|
||||||
|
while (Index < length) {
|
||||||
|
symbol = source.charAt(Index);
|
||||||
|
if ("\t\r\n ".indexOf(symbol) > -1) {
|
||||||
|
// Skip whitespace tokens, including tabs, carriage returns, line
|
||||||
|
// feeds, and space characters.
|
||||||
|
Index++;
|
||||||
|
} else if ("{}[]:,".indexOf(symbol) > -1) {
|
||||||
|
// Parse a punctuator token at the current position.
|
||||||
|
Index++;
|
||||||
|
return symbol;
|
||||||
|
} else if (symbol == '"') {
|
||||||
|
// Advance to the next character and parse a JSON string at the
|
||||||
|
// current position. String tokens are prefixed with the sentinel
|
||||||
|
// `@` character to distinguish them from punctuators.
|
||||||
|
for (value = "@", Index++; Index < length;) {
|
||||||
|
symbol = source.charAt(Index);
|
||||||
|
if (symbol < " ") {
|
||||||
|
// Unescaped ASCII control characters are not permitted.
|
||||||
|
abort();
|
||||||
|
} else if (symbol == "\\") {
|
||||||
|
// Parse escaped JSON control characters, `"`, `\`, `/`, and
|
||||||
|
// Unicode escape sequences.
|
||||||
|
symbol = source.charAt(++Index);
|
||||||
|
if ('\\"/btnfr'.indexOf(symbol) > -1) {
|
||||||
|
// Revive escaped control characters.
|
||||||
|
value += Unescapes[symbol];
|
||||||
|
Index++;
|
||||||
|
} else if (symbol == "u") {
|
||||||
|
// Advance to the first character of the escape sequence.
|
||||||
|
begin = ++Index;
|
||||||
|
// Validate the Unicode escape sequence.
|
||||||
|
for (position = Index + 4; Index < position; Index++) {
|
||||||
|
symbol = source.charAt(Index);
|
||||||
|
// A valid sequence comprises four hexdigits that form a
|
||||||
|
// single hexadecimal value.
|
||||||
|
if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
|
||||||
|
// Invalid Unicode escape sequence.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Revive the escaped character.
|
||||||
|
value += fromCharCode("0x" + source.slice(begin, Index));
|
||||||
|
} else {
|
||||||
|
// Invalid escape sequence.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (symbol == '"') {
|
||||||
|
// An unescaped double-quote character marks the end of the
|
||||||
|
// string.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Append the original character as-is.
|
||||||
|
value += symbol;
|
||||||
|
Index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (source.charAt(Index) == '"') {
|
||||||
|
Index++;
|
||||||
|
// Return the revived string.
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// Unterminated string.
|
||||||
|
abort();
|
||||||
|
} else {
|
||||||
|
// Parse numbers and literals.
|
||||||
|
begin = Index;
|
||||||
|
// Advance the scanner's position past the sign, if one is
|
||||||
|
// specified.
|
||||||
|
if (symbol == "-") {
|
||||||
|
sign = true;
|
||||||
|
symbol = source.charAt(++Index);
|
||||||
|
}
|
||||||
|
// Parse an integer or floating-point value.
|
||||||
|
if (symbol >= "0" && symbol <= "9") {
|
||||||
|
// Leading zeroes are interpreted as octal literals.
|
||||||
|
if (symbol == "0" && (symbol = source.charAt(Index + 1), symbol >= "0" && symbol <= "9")) {
|
||||||
|
// Illegal octal literal.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
sign = false;
|
||||||
|
// Parse the integer component.
|
||||||
|
for (; Index < length && (symbol = source.charAt(Index), symbol >= "0" && symbol <= "9"); Index++);
|
||||||
|
// Floats cannot contain a leading decimal point; however, this
|
||||||
|
// case is already accounted for by the parser.
|
||||||
|
if (source.charAt(Index) == ".") {
|
||||||
|
position = ++Index;
|
||||||
|
// Parse the decimal component.
|
||||||
|
for (; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
|
||||||
|
if (position == Index) {
|
||||||
|
// Illegal trailing decimal.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
Index = position;
|
||||||
|
}
|
||||||
|
// Parse exponents.
|
||||||
|
symbol = source.charAt(Index);
|
||||||
|
if (symbol == "e" || symbol == "E") {
|
||||||
|
// Skip past the sign following the exponent, if one is
|
||||||
|
// specified.
|
||||||
|
symbol = source.charAt(++Index);
|
||||||
|
if (symbol == "+" || symbol == "-") {
|
||||||
|
Index++;
|
||||||
|
}
|
||||||
|
// Parse the exponential component.
|
||||||
|
for (position = Index; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
|
||||||
|
if (position == Index) {
|
||||||
|
// Illegal empty exponent.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
Index = position;
|
||||||
|
}
|
||||||
|
// Coerce the parsed value to a JavaScript number.
|
||||||
|
return +source.slice(begin, Index);
|
||||||
|
}
|
||||||
|
// A negative sign may only precede numbers.
|
||||||
|
if (sign) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
// `true`, `false`, and `null` literals.
|
||||||
|
if (source.slice(Index, Index + 4) == "true") {
|
||||||
|
Index += 4;
|
||||||
|
return true;
|
||||||
|
} else if (source.slice(Index, Index + 5) == "false") {
|
||||||
|
Index += 5;
|
||||||
|
return false;
|
||||||
|
} else if (source.slice(Index, Index + 4) == "null") {
|
||||||
|
Index += 4;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Unrecognized token.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return the sentinel `$` character if the parser has reached the end
|
||||||
|
// of the source string.
|
||||||
|
return "$";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Parses a JSON `value` token.
|
||||||
|
get = function (value) {
|
||||||
|
var results, any, key;
|
||||||
|
if (value == "$") {
|
||||||
|
// Unexpected end of input.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (typeof value == "string") {
|
||||||
|
if (value.charAt(0) == "@") {
|
||||||
|
// Remove the sentinel `@` character.
|
||||||
|
return value.slice(1);
|
||||||
|
}
|
||||||
|
// Parse object and array literals.
|
||||||
|
if (value == "[") {
|
||||||
|
// Parses a JSON array, returning a new JavaScript array.
|
||||||
|
results = [];
|
||||||
|
for (;; any || (any = true)) {
|
||||||
|
value = lex();
|
||||||
|
// A closing square bracket marks the end of the array literal.
|
||||||
|
if (value == "]") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If the array literal contains elements, the current token
|
||||||
|
// should be a comma separating the previous element from the
|
||||||
|
// next.
|
||||||
|
if (any) {
|
||||||
|
if (value == ",") {
|
||||||
|
value = lex();
|
||||||
|
if (value == "]") {
|
||||||
|
// Unexpected trailing `,` in array literal.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A `,` must separate each array element.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Elisions and leading commas are not permitted.
|
||||||
|
if (value == ",") {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
results.push(get(value));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
} else if (value == "{") {
|
||||||
|
// Parses a JSON object, returning a new JavaScript object.
|
||||||
|
results = {};
|
||||||
|
for (;; any || (any = true)) {
|
||||||
|
value = lex();
|
||||||
|
// A closing curly brace marks the end of the object literal.
|
||||||
|
if (value == "}") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If the object literal contains members, the current token
|
||||||
|
// should be a comma separator.
|
||||||
|
if (any) {
|
||||||
|
if (value == ",") {
|
||||||
|
value = lex();
|
||||||
|
if (value == "}") {
|
||||||
|
// Unexpected trailing `,` in object literal.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A `,` must separate each object member.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Leading commas are not permitted, object property names must be
|
||||||
|
// double-quoted strings, and a `:` must separate each property
|
||||||
|
// name and value.
|
||||||
|
if (value == "," || typeof value != "string" || value.charAt(0) != "@" || lex() != ":") {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
results[value.slice(1)] = get(lex());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
// Unexpected token encountered.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Updates a traversed object member.
|
||||||
|
update = function(source, property, callback) {
|
||||||
|
var element = walk(source, property, callback);
|
||||||
|
if (element === undef) {
|
||||||
|
delete source[property];
|
||||||
|
} else {
|
||||||
|
source[property] = element;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal: Recursively traverses a parsed JSON object, invoking the
|
||||||
|
// `callback` function for each value. This is an implementation of the
|
||||||
|
// `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
|
||||||
|
walk = function (source, property, callback) {
|
||||||
|
var value = source[property], length;
|
||||||
|
if (typeof value == "object" && value) {
|
||||||
|
if (getClass.call(value) == "[object Array]") {
|
||||||
|
for (length = value.length; length--;) {
|
||||||
|
update(value, length, callback);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// `forEach` can't be used to traverse an array in Opera <= 8.54,
|
||||||
|
// as `Object#hasOwnProperty` returns `false` for array indices
|
||||||
|
// (e.g., `![1, 2, 3].hasOwnProperty("0")`).
|
||||||
|
forEach(value, function (property) {
|
||||||
|
update(value, property, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callback.call(source, property, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Public: `JSON.parse`. See ES 5.1 section 15.12.2.
|
||||||
|
JSON3.parse = function (source, callback) {
|
||||||
|
var result, value;
|
||||||
|
Index = 0;
|
||||||
|
Source = source;
|
||||||
|
result = get(lex());
|
||||||
|
// If a JSON string contains multiple tokens, it is invalid.
|
||||||
|
if (lex() != "$") {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
// Reset the parser state.
|
||||||
|
Index = Source = null;
|
||||||
|
return callback && getClass.call(callback) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for asynchronous module loaders.
|
||||||
|
if (isLoader) {
|
||||||
|
define(function () {
|
||||||
|
return JSON3;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).call(this);
|
||||||
670
vendor/underscore/test/vendor/jslitmus.js
vendored
670
vendor/underscore/test/vendor/jslitmus.js
vendored
@@ -1,670 +0,0 @@
|
|||||||
// JSLitmus.js
|
|
||||||
//
|
|
||||||
// History:
|
|
||||||
// 2008-10-27: Initial release
|
|
||||||
// 2008-11-09: Account for iteration loop overhead
|
|
||||||
// 2008-11-13: Added OS detection
|
|
||||||
// 2009-02-25: Create tinyURL automatically, shift-click runs tests in reverse
|
|
||||||
//
|
|
||||||
// Copyright (c) 2008-2009, Robert Kieffer
|
|
||||||
// All Rights Reserved
|
|
||||||
//
|
|
||||||
// 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 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.
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
// Private methods and state
|
|
||||||
|
|
||||||
// Get platform info but don't go crazy trying to recognize everything
|
|
||||||
// that's out there. This is just for the major platforms and OSes.
|
|
||||||
var platform = 'unknown platform', ua = navigator.userAgent;
|
|
||||||
|
|
||||||
// Detect OS
|
|
||||||
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
|
|
||||||
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
|
|
||||||
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
|
|
||||||
|
|
||||||
// Detect browser
|
|
||||||
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
|
|
||||||
|
|
||||||
// Detect version
|
|
||||||
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
|
|
||||||
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
|
|
||||||
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A smattering of methods that are needed to implement the JSLitmus testbed.
|
|
||||||
*/
|
|
||||||
var jsl = {
|
|
||||||
/**
|
|
||||||
* Enhanced version of escape()
|
|
||||||
*/
|
|
||||||
escape: function(s) {
|
|
||||||
s = s.replace(/,/g, '\\,');
|
|
||||||
s = escape(s);
|
|
||||||
s = s.replace(/\+/g, '%2b');
|
|
||||||
s = s.replace(/ /g, '+');
|
|
||||||
return s;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an element by ID.
|
|
||||||
*/
|
|
||||||
$: function(id) {
|
|
||||||
return document.getElementById(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Null function
|
|
||||||
*/
|
|
||||||
F: function() {},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the status shown in the UI
|
|
||||||
*/
|
|
||||||
status: function(msg) {
|
|
||||||
var el = jsl.$('jsl_status');
|
|
||||||
if (el) el.innerHTML = msg || '';
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a number to an abbreviated string like, "15K" or "10M"
|
|
||||||
*/
|
|
||||||
toLabel: function(n) {
|
|
||||||
if (n == Infinity) {
|
|
||||||
return 'Infinity';
|
|
||||||
} else if (n > 1e9) {
|
|
||||||
n = Math.round(n/1e8);
|
|
||||||
return n/10 + 'B';
|
|
||||||
} else if (n > 1e6) {
|
|
||||||
n = Math.round(n/1e5);
|
|
||||||
return n/10 + 'M';
|
|
||||||
} else if (n > 1e3) {
|
|
||||||
n = Math.round(n/1e2);
|
|
||||||
return n/10 + 'K';
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy properties from src to dst
|
|
||||||
*/
|
|
||||||
extend: function(dst, src) {
|
|
||||||
for (var k in src) dst[k] = src[k]; return dst;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like Array.join(), but for the key-value pairs in an object
|
|
||||||
*/
|
|
||||||
join: function(o, delimit1, delimit2) {
|
|
||||||
if (o.join) return o.join(delimit1); // If it's an array
|
|
||||||
var pairs = [];
|
|
||||||
for (var k in o) pairs.push(k + delimit1 + o[k]);
|
|
||||||
return pairs.join(delimit2);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
|
|
||||||
*/
|
|
||||||
indexOf: function(arr, o) {
|
|
||||||
if (arr.indexOf) return arr.indexOf(o);
|
|
||||||
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test manages a single test (created with
|
|
||||||
* JSLitmus.test())
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
var Test = function (name, f) {
|
|
||||||
if (!f) throw new Error('Undefined test function');
|
|
||||||
if (!(/function[^\(]*\(([^,\)]*)/).test(f.toString())) {
|
|
||||||
throw new Error('"' + name + '" test: Test is not a valid Function object');
|
|
||||||
}
|
|
||||||
this.loopArg = RegExp.$1;
|
|
||||||
this.name = name;
|
|
||||||
this.f = f;
|
|
||||||
};
|
|
||||||
|
|
||||||
jsl.extend(Test, /** @lends Test */ {
|
|
||||||
/** Calibration tests for establishing iteration loop overhead */
|
|
||||||
CALIBRATIONS: [
|
|
||||||
new Test('calibrating loop', function(count) {while (count--);}),
|
|
||||||
new Test('calibrating function', jsl.F)
|
|
||||||
],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run calibration tests. Returns true if calibrations are not yet
|
|
||||||
* complete (in which case calling code should run the tests yet again).
|
|
||||||
* onCalibrated - Callback to invoke when calibrations have finished
|
|
||||||
*/
|
|
||||||
calibrate: function(onCalibrated) {
|
|
||||||
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
|
|
||||||
var cal = Test.CALIBRATIONS[i];
|
|
||||||
if (cal.running) return true;
|
|
||||||
if (!cal.count) {
|
|
||||||
cal.isCalibration = true;
|
|
||||||
cal.onStop = onCalibrated;
|
|
||||||
//cal.MIN_TIME = .1; // Do calibrations quickly
|
|
||||||
cal.run(2e4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
jsl.extend(Test.prototype, {/** @lends Test.prototype */
|
|
||||||
/** Initial number of iterations */
|
|
||||||
INIT_COUNT: 10,
|
|
||||||
/** Max iterations allowed (i.e. used to detect bad looping functions) */
|
|
||||||
MAX_COUNT: 1e9,
|
|
||||||
/** Minimum time a test should take to get valid results (secs) */
|
|
||||||
MIN_TIME: .5,
|
|
||||||
|
|
||||||
/** Callback invoked when test state changes */
|
|
||||||
onChange: jsl.F,
|
|
||||||
|
|
||||||
/** Callback invoked when test is finished */
|
|
||||||
onStop: jsl.F,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset test state
|
|
||||||
*/
|
|
||||||
reset: function() {
|
|
||||||
delete this.count;
|
|
||||||
delete this.time;
|
|
||||||
delete this.running;
|
|
||||||
delete this.error;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the test (in a timeout). We use a timeout to make sure the browser
|
|
||||||
* has a chance to finish rendering any UI changes we've made, like
|
|
||||||
* updating the status message.
|
|
||||||
*/
|
|
||||||
run: function(count) {
|
|
||||||
count = count || this.INIT_COUNT;
|
|
||||||
jsl.status(this.name + ' x ' + count);
|
|
||||||
this.running = true;
|
|
||||||
var me = this;
|
|
||||||
setTimeout(function() {me._run(count);}, 200);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The nuts and bolts code that actually runs a test
|
|
||||||
*/
|
|
||||||
_run: function(count) {
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
// Make sure calibration tests have run
|
|
||||||
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
|
|
||||||
this.error = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var start, f = this.f, now, i = count;
|
|
||||||
|
|
||||||
// Start the timer
|
|
||||||
start = new Date();
|
|
||||||
|
|
||||||
// Now for the money shot. If this is a looping function ...
|
|
||||||
if (this.loopArg) {
|
|
||||||
// ... let it do the iteration itself
|
|
||||||
f(count);
|
|
||||||
} else {
|
|
||||||
// ... otherwise do the iteration for it
|
|
||||||
while (i--) f();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get time test took (in secs)
|
|
||||||
this.time = Math.max(1,new Date() - start)/1000;
|
|
||||||
|
|
||||||
// Store iteration count and per-operation time taken
|
|
||||||
this.count = count;
|
|
||||||
this.period = this.time/count;
|
|
||||||
|
|
||||||
// Do we need to do another run?
|
|
||||||
this.running = this.time <= this.MIN_TIME;
|
|
||||||
|
|
||||||
// ... if so, compute how many times we should iterate
|
|
||||||
if (this.running) {
|
|
||||||
// Bump the count to the nearest power of 2
|
|
||||||
var x = this.MIN_TIME/this.time;
|
|
||||||
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
|
|
||||||
count *= pow;
|
|
||||||
if (count > this.MAX_COUNT) {
|
|
||||||
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Exceptions are caught and displayed in the test UI
|
|
||||||
this.reset();
|
|
||||||
this.error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out what to do next
|
|
||||||
if (this.running) {
|
|
||||||
me.run(count);
|
|
||||||
} else {
|
|
||||||
jsl.status('');
|
|
||||||
me.onStop(me);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish up
|
|
||||||
this.onChange(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of operations per second for this test.
|
|
||||||
*
|
|
||||||
* @param normalize if true, iteration loop overhead taken into account
|
|
||||||
*/
|
|
||||||
getHz: function(/**Boolean*/ normalize) {
|
|
||||||
var p = this.period;
|
|
||||||
|
|
||||||
// Adjust period based on the calibration test time
|
|
||||||
if (normalize && !this.isCalibration) {
|
|
||||||
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
|
|
||||||
|
|
||||||
// If the period is within 20% of the calibration time, then zero the
|
|
||||||
// it out
|
|
||||||
p = p < cal.period*1.2 ? 0 : p - cal.period;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(1/p);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a friendly string describing the test
|
|
||||||
*/
|
|
||||||
toString: function() {
|
|
||||||
return this.name + ' - ' + this.time/this.count + ' secs';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CSS we need for the UI
|
|
||||||
var STYLESHEET = '<style> \
|
|
||||||
#jslitmus {font-family:sans-serif; font-size: 12px;} \
|
|
||||||
#jslitmus a {text-decoration: none;} \
|
|
||||||
#jslitmus a:hover {text-decoration: underline;} \
|
|
||||||
#jsl_status { \
|
|
||||||
margin-top: 10px; \
|
|
||||||
font-size: 10px; \
|
|
||||||
color: #888; \
|
|
||||||
} \
|
|
||||||
A IMG {border:none} \
|
|
||||||
#test_results { \
|
|
||||||
margin-top: 10px; \
|
|
||||||
font-size: 12px; \
|
|
||||||
font-family: sans-serif; \
|
|
||||||
border-collapse: collapse; \
|
|
||||||
border-spacing: 0px; \
|
|
||||||
} \
|
|
||||||
#test_results th, #test_results td { \
|
|
||||||
border: solid 1px #ccc; \
|
|
||||||
vertical-align: top; \
|
|
||||||
padding: 3px; \
|
|
||||||
} \
|
|
||||||
#test_results th { \
|
|
||||||
vertical-align: bottom; \
|
|
||||||
background-color: #ccc; \
|
|
||||||
padding: 1px; \
|
|
||||||
font-size: 10px; \
|
|
||||||
} \
|
|
||||||
#test_results #test_platform { \
|
|
||||||
color: #444; \
|
|
||||||
text-align:center; \
|
|
||||||
} \
|
|
||||||
#test_results .test_row { \
|
|
||||||
color: #006; \
|
|
||||||
cursor: pointer; \
|
|
||||||
} \
|
|
||||||
#test_results .test_nonlooping { \
|
|
||||||
border-left-style: dotted; \
|
|
||||||
border-left-width: 2px; \
|
|
||||||
} \
|
|
||||||
#test_results .test_looping { \
|
|
||||||
border-left-style: solid; \
|
|
||||||
border-left-width: 2px; \
|
|
||||||
} \
|
|
||||||
#test_results .test_name {white-space: nowrap;} \
|
|
||||||
#test_results .test_pending { \
|
|
||||||
} \
|
|
||||||
#test_results .test_running { \
|
|
||||||
font-style: italic; \
|
|
||||||
} \
|
|
||||||
#test_results .test_done {} \
|
|
||||||
#test_results .test_done { \
|
|
||||||
text-align: right; \
|
|
||||||
font-family: monospace; \
|
|
||||||
} \
|
|
||||||
#test_results .test_error {color: #600;} \
|
|
||||||
#test_results .test_error .error_head {font-weight:bold;} \
|
|
||||||
#test_results .test_error .error_body {font-size:85%;} \
|
|
||||||
#test_results .test_row:hover td { \
|
|
||||||
background-color: #ffc; \
|
|
||||||
text-decoration: underline; \
|
|
||||||
} \
|
|
||||||
#chart { \
|
|
||||||
margin: 10px 0px; \
|
|
||||||
width: 250px; \
|
|
||||||
} \
|
|
||||||
#chart img { \
|
|
||||||
border: solid 1px #ccc; \
|
|
||||||
margin-bottom: 5px; \
|
|
||||||
} \
|
|
||||||
#chart #tiny_url { \
|
|
||||||
height: 40px; \
|
|
||||||
width: 250px; \
|
|
||||||
} \
|
|
||||||
#jslitmus_credit { \
|
|
||||||
font-size: 10px; \
|
|
||||||
color: #888; \
|
|
||||||
margin-top: 8px; \
|
|
||||||
} \
|
|
||||||
</style>';
|
|
||||||
|
|
||||||
// HTML markup for the UI
|
|
||||||
var MARKUP = '<div id="jslitmus"> \
|
|
||||||
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
|
|
||||||
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
|
|
||||||
<br \> \
|
|
||||||
<br \> \
|
|
||||||
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
|
|
||||||
<table id="test_results"> \
|
|
||||||
<colgroup> \
|
|
||||||
<col /> \
|
|
||||||
<col width="100" /> \
|
|
||||||
</colgroup> \
|
|
||||||
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
|
|
||||||
<tr><th>Test</th><th>Ops/sec</th></tr> \
|
|
||||||
<tr id="test_row_template" class="test_row" style="display:none"> \
|
|
||||||
<td class="test_name"></td> \
|
|
||||||
<td class="test_result">Ready</td> \
|
|
||||||
</tr> \
|
|
||||||
</table> \
|
|
||||||
<div id="jsl_status"></div> \
|
|
||||||
<div id="chart" style="display:none"> \
|
|
||||||
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
|
|
||||||
TinyURL (for chart): \
|
|
||||||
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
|
|
||||||
</div> \
|
|
||||||
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
|
|
||||||
</div>';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The public API for creating and running tests
|
|
||||||
*/
|
|
||||||
window.JSLitmus = {
|
|
||||||
/** The list of all tests that have been registered with JSLitmus.test */
|
|
||||||
_tests: [],
|
|
||||||
/** The queue of tests that need to be run */
|
|
||||||
_queue: [],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parsed query parameters the current page URL. This is provided as a
|
|
||||||
* convenience for test functions - it's not used by JSLitmus proper
|
|
||||||
*/
|
|
||||||
params: {},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize
|
|
||||||
*/
|
|
||||||
_init: function() {
|
|
||||||
// Parse query params into JSLitmus.params[] hash
|
|
||||||
var match = (location + '').match(/([^?#]*)(#.*)?$/);
|
|
||||||
if (match) {
|
|
||||||
var pairs = match[1].split('&');
|
|
||||||
for (var i = 0; i < pairs.length; i++) {
|
|
||||||
var pair = pairs[i].split('=');
|
|
||||||
if (pair.length > 1) {
|
|
||||||
var key = pair.shift();
|
|
||||||
var value = pair.length > 1 ? pair.join('=') : pair[0];
|
|
||||||
this.params[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out the stylesheet. We have to do this here because IE
|
|
||||||
// doesn't honor sheets written after the document has loaded.
|
|
||||||
document.write(STYLESHEET);
|
|
||||||
|
|
||||||
// Setup the rest of the UI once the document is loaded
|
|
||||||
if (window.addEventListener) {
|
|
||||||
window.addEventListener('load', this._setup, false);
|
|
||||||
} else if (document.addEventListener) {
|
|
||||||
document.addEventListener('load', this._setup, false);
|
|
||||||
} else if (window.attachEvent) {
|
|
||||||
window.attachEvent('onload', this._setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the UI
|
|
||||||
*/
|
|
||||||
_setup: function() {
|
|
||||||
var el = jsl.$('jslitmus_container');
|
|
||||||
if (!el) document.body.appendChild(el = document.createElement('div'));
|
|
||||||
|
|
||||||
el.innerHTML = MARKUP;
|
|
||||||
|
|
||||||
// Render the UI for all our tests
|
|
||||||
for (var i=0; i < JSLitmus._tests.length; i++)
|
|
||||||
JSLitmus.renderTest(JSLitmus._tests[i]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render all the test results
|
|
||||||
*/
|
|
||||||
renderAll: function() {
|
|
||||||
for (var i = 0; i < JSLitmus._tests.length; i++)
|
|
||||||
JSLitmus.renderTest(JSLitmus._tests[i]);
|
|
||||||
JSLitmus.renderChart();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render the chart graphics
|
|
||||||
*/
|
|
||||||
renderChart: function() {
|
|
||||||
var url = JSLitmus.chartUrl();
|
|
||||||
jsl.$('chart_link').href = url;
|
|
||||||
jsl.$('chart_image').src = url;
|
|
||||||
jsl.$('chart').style.display = '';
|
|
||||||
|
|
||||||
// Update the tiny URL
|
|
||||||
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (Re)render the results for a specific test
|
|
||||||
*/
|
|
||||||
renderTest: function(test) {
|
|
||||||
// Make a new row if needed
|
|
||||||
if (!test._row) {
|
|
||||||
var trow = jsl.$('test_row_template');
|
|
||||||
if (!trow) return;
|
|
||||||
|
|
||||||
test._row = trow.cloneNode(true);
|
|
||||||
test._row.style.display = '';
|
|
||||||
test._row.id = '';
|
|
||||||
test._row.onclick = function() {JSLitmus._queueTest(test);};
|
|
||||||
test._row.title = 'Run ' + test.name + ' test';
|
|
||||||
trow.parentNode.appendChild(test._row);
|
|
||||||
test._row.cells[0].innerHTML = test.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = test._row.cells[1];
|
|
||||||
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
|
|
||||||
|
|
||||||
if (test.error) {
|
|
||||||
cns.push('test_error');
|
|
||||||
cell.innerHTML =
|
|
||||||
'<div class="error_head">' + test.error + '</div>' +
|
|
||||||
'<ul class="error_body"><li>' +
|
|
||||||
jsl.join(test.error, ': ', '</li><li>') +
|
|
||||||
'</li></ul>';
|
|
||||||
} else {
|
|
||||||
if (test.running) {
|
|
||||||
cns.push('test_running');
|
|
||||||
cell.innerHTML = 'running';
|
|
||||||
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
|
|
||||||
cns.push('test_pending');
|
|
||||||
cell.innerHTML = 'pending';
|
|
||||||
} else if (test.count) {
|
|
||||||
cns.push('test_done');
|
|
||||||
var hz = test.getHz(jsl.$('test_normalize').checked);
|
|
||||||
cell.innerHTML = hz != Infinity ? hz : '∞';
|
|
||||||
} else {
|
|
||||||
cell.innerHTML = 'ready';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cell.className = cns.join(' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new test
|
|
||||||
*/
|
|
||||||
test: function(name, f) {
|
|
||||||
// Create the Test object
|
|
||||||
var test = new Test(name, f);
|
|
||||||
JSLitmus._tests.push(test);
|
|
||||||
|
|
||||||
// Re-render if the test state changes
|
|
||||||
test.onChange = JSLitmus.renderTest;
|
|
||||||
|
|
||||||
// Run the next test if this one finished
|
|
||||||
test.onStop = function(test) {
|
|
||||||
if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test);
|
|
||||||
JSLitmus.currentTest = null;
|
|
||||||
JSLitmus._nextTest();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the new test
|
|
||||||
this.renderTest(test);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all tests to the run queue
|
|
||||||
*/
|
|
||||||
runAll: function(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
var reverse = e && e.shiftKey, len = JSLitmus._tests.length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all tests from the run queue. The current test has to finish on
|
|
||||||
* it's own though
|
|
||||||
*/
|
|
||||||
stop: function() {
|
|
||||||
while (JSLitmus._queue.length) {
|
|
||||||
var test = JSLitmus._queue.shift();
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the next test in the run queue
|
|
||||||
*/
|
|
||||||
_nextTest: function() {
|
|
||||||
if (!JSLitmus.currentTest) {
|
|
||||||
var test = JSLitmus._queue.shift();
|
|
||||||
if (test) {
|
|
||||||
jsl.$('stop_button').disabled = false;
|
|
||||||
JSLitmus.currentTest = test;
|
|
||||||
test.run();
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
if (JSLitmus.onTestStart) JSLitmus.onTestStart(test);
|
|
||||||
} else {
|
|
||||||
jsl.$('stop_button').disabled = true;
|
|
||||||
JSLitmus.renderChart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a test to the run queue
|
|
||||||
*/
|
|
||||||
_queueTest: function(test) {
|
|
||||||
if (jsl.indexOf(JSLitmus._queue, test) >= 0) return;
|
|
||||||
JSLitmus._queue.push(test);
|
|
||||||
JSLitmus.renderTest(test);
|
|
||||||
JSLitmus._nextTest();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a Google Chart URL that shows the data for all tests
|
|
||||||
*/
|
|
||||||
chartUrl: function() {
|
|
||||||
var n = JSLitmus._tests.length, markers = [], data = [];
|
|
||||||
var d, min = 0, max = -1e10;
|
|
||||||
var normalize = jsl.$('test_normalize').checked;
|
|
||||||
|
|
||||||
// Gather test data
|
|
||||||
for (var i=0; i < JSLitmus._tests.length; i++) {
|
|
||||||
var test = JSLitmus._tests[i];
|
|
||||||
if (test.count) {
|
|
||||||
var hz = test.getHz(normalize);
|
|
||||||
var v = hz != Infinity ? hz : 0;
|
|
||||||
data.push(v);
|
|
||||||
markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' +
|
|
||||||
markers.length + ',10');
|
|
||||||
max = Math.max(v, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (markers.length <= 0) return null;
|
|
||||||
|
|
||||||
// Build chart title
|
|
||||||
var title = document.getElementsByTagName('title');
|
|
||||||
title = (title && title.length) ? title[0].innerHTML : null;
|
|
||||||
var chart_title = [];
|
|
||||||
if (title) chart_title.push(title);
|
|
||||||
chart_title.push('Ops/sec (' + platform + ')');
|
|
||||||
|
|
||||||
// Build labels
|
|
||||||
var labels = [jsl.toLabel(min), jsl.toLabel(max)];
|
|
||||||
|
|
||||||
var w = 250, bw = 15;
|
|
||||||
var bs = 5;
|
|
||||||
var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
|
|
||||||
|
|
||||||
var params = {
|
|
||||||
chtt: escape(chart_title.join('|')),
|
|
||||||
chts: '000000,10',
|
|
||||||
cht: 'bhg', // chart type
|
|
||||||
chd: 't:' + data.join(','), // data set
|
|
||||||
chds: min + ',' + max, // max/min of data
|
|
||||||
chxt: 'x', // label axes
|
|
||||||
chxl: '0:|' + labels.join('|'), // labels
|
|
||||||
chsp: '0,1',
|
|
||||||
chm: markers.join('|'), // test names
|
|
||||||
chbh: [bw, 0, bs].join(','), // bar widths
|
|
||||||
// chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
|
|
||||||
chs: w + 'x' + h
|
|
||||||
};
|
|
||||||
return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JSLitmus._init();
|
|
||||||
})();
|
|
||||||
Reference in New Issue
Block a user