init
This commit is contained in:
21
bower_components/matchMedia/.bower.json
vendored
Normal file
21
bower_components/matchMedia/.bower.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"author": "Scott Jehl",
|
||||
"name": "matchMedia",
|
||||
"description": "matchMedia polyfill for testing media queries in JS",
|
||||
"version": "0.3.2",
|
||||
"homepage": "http://github.com/paulirish/matchMedia.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/paulirish/matchMedia.js.git"
|
||||
},
|
||||
"main": "./matchMedia.js",
|
||||
"_release": "0.3.2",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.3.2",
|
||||
"commit": "e5d3ec47dd5b109e68261885b01b9e9ff8d49efe"
|
||||
},
|
||||
"_source": "https://github.com/paulirish/matchMedia.js.git",
|
||||
"_target": "~0.3.2",
|
||||
"_originalSource": "matchMedia"
|
||||
}
|
2
bower_components/matchMedia/.npmignore
vendored
Normal file
2
bower_components/matchMedia/.npmignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
test/
|
||||
bower.json
|
7
bower_components/matchMedia/LICENSE.txt
vendored
Normal file
7
bower_components/matchMedia/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2012 Scott Jehl
|
||||
|
||||
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.
|
53
bower_components/matchMedia/README.md
vendored
Normal file
53
bower_components/matchMedia/README.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
[](https://npmjs.com/package/matchmedia-polyfill)
|
||||
|
||||
# matchMedia() polyfill
|
||||
|
||||
## test whether a CSS media type or media query applies
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
// Likely want to requier both polyfills..
|
||||
require('matchmedia-polyfill');
|
||||
require('matchmedia-polyfill/matchMedia.addListener');
|
||||
```
|
||||
|
||||
#### test 'tv' media type
|
||||
```js
|
||||
if (matchMedia('tv').matches) {
|
||||
// tv media type supported
|
||||
}
|
||||
```
|
||||
|
||||
### test a mobile device media query
|
||||
```js
|
||||
if (matchMedia('only screen and (max-width: 480px)').matches) {
|
||||
// smartphone/iphone... maybe run some small-screen related dom scripting?
|
||||
}
|
||||
```
|
||||
|
||||
#### test landscape orientation
|
||||
```js
|
||||
if (matchMedia('all and (orientation:landscape)').matches) {
|
||||
// probably tablet in widescreen view
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Used in:
|
||||
|
||||
* [Respond.js](https://github.com/scottjehl/Respond)
|
||||
* [FormFactor](https://github.com/PaulKinlan/formfactor)
|
||||
* [Modernizr](http://www.modernizr.com/)
|
||||
|
||||
|
||||
### How about resizing the browser?
|
||||
Paul Hayes [tackled this using CSS transitions and their transitionEnd event](http://www.paulrhayes.com/2011-11/use-css-transitions-to-link-media-queries-and-javascript/)
|
||||
|
||||
His code: https://github.com/fofr/matchMedia.js -- though currently it doesnt support IE6-9, since they dont have transitions, obviously. :)
|
||||
|
||||
----------
|
||||
|
||||
* **Authors**: Scott Jehl, Paul Irish, Nicholas Zakas
|
||||
* **Spec**: [dev.w3.org/csswg/cssom-view/#dom-window-matchmedia](http://dev.w3.org/csswg/cssom-view/#dom-window-matchmedia)
|
||||
* **Native support**: Chrome [since m10](http://trac.webkit.org/changeset/72552), Firefox [since 6](https://developer.mozilla.org/en/Firefox/Releases/6), and Safari [since 5.1](https://developer.mozilla.org/en/DOM/window.matchMedia#Browser_compatibility)
|
12
bower_components/matchMedia/bower.json
vendored
Normal file
12
bower_components/matchMedia/bower.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"author": "Scott Jehl",
|
||||
"name": "matchMedia",
|
||||
"description": "matchMedia polyfill for testing media queries in JS",
|
||||
"version": "0.3.1",
|
||||
"homepage": "http://github.com/paulirish/matchMedia.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/paulirish/matchMedia.js.git"
|
||||
},
|
||||
"main": "./matchMedia.js"
|
||||
}
|
75
bower_components/matchMedia/matchMedia.addListener.js
vendored
Normal file
75
bower_components/matchMedia/matchMedia.addListener.js
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/*! matchMedia() polyfill addListener/removeListener extension. Author & copyright (c) 2012: Scott Jehl. MIT license */
|
||||
(function(){
|
||||
// Bail out for browsers that have addListener support
|
||||
if (window.matchMedia && window.matchMedia('all').addListener) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var localMatchMedia = window.matchMedia,
|
||||
hasMediaQueries = localMatchMedia('only all').matches,
|
||||
isListening = false,
|
||||
timeoutID = 0, // setTimeout for debouncing 'handleChange'
|
||||
queries = [], // Contains each 'mql' and associated 'listeners' if 'addListener' is used
|
||||
handleChange = function(evt) {
|
||||
// Debounce
|
||||
clearTimeout(timeoutID);
|
||||
|
||||
timeoutID = setTimeout(function() {
|
||||
for (var i = 0, il = queries.length; i < il; i++) {
|
||||
var mql = queries[i].mql,
|
||||
listeners = queries[i].listeners || [],
|
||||
matches = localMatchMedia(mql.media).matches;
|
||||
|
||||
// Update mql.matches value and call listeners
|
||||
// Fire listeners only if transitioning to or from matched state
|
||||
if (matches !== mql.matches) {
|
||||
mql.matches = matches;
|
||||
|
||||
for (var j = 0, jl = listeners.length; j < jl; j++) {
|
||||
listeners[j].call(window, mql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 30);
|
||||
};
|
||||
|
||||
window.matchMedia = function(media) {
|
||||
var mql = localMatchMedia(media),
|
||||
listeners = [],
|
||||
index = 0;
|
||||
|
||||
mql.addListener = function(listener) {
|
||||
// Changes would not occur to css media type so return now (Affects IE <= 8)
|
||||
if (!hasMediaQueries) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up 'resize' listener for browsers that support CSS3 media queries (Not for IE <= 8)
|
||||
// There should only ever be 1 resize listener running for performance
|
||||
if (!isListening) {
|
||||
isListening = true;
|
||||
window.addEventListener('resize', handleChange, true);
|
||||
}
|
||||
|
||||
// Push object only if it has not been pushed already
|
||||
if (index === 0) {
|
||||
index = queries.push({
|
||||
mql : mql,
|
||||
listeners : listeners
|
||||
});
|
||||
}
|
||||
|
||||
listeners.push(listener);
|
||||
};
|
||||
|
||||
mql.removeListener = function(listener) {
|
||||
for (var i = 0, il = listeners.length; i < il; i++){
|
||||
if (listeners[i] === listener){
|
||||
listeners.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return mql;
|
||||
};
|
||||
}());
|
50
bower_components/matchMedia/matchMedia.js
vendored
Normal file
50
bower_components/matchMedia/matchMedia.js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. MIT license */
|
||||
|
||||
window.matchMedia || (window.matchMedia = function() {
|
||||
"use strict";
|
||||
|
||||
// For browsers that support matchMedium api such as IE 9 and webkit
|
||||
var styleMedia = (window.styleMedia || window.media);
|
||||
|
||||
// For those that don't support matchMedium
|
||||
if (!styleMedia) {
|
||||
var style = document.createElement('style'),
|
||||
script = document.getElementsByTagName('script')[0],
|
||||
info = null;
|
||||
|
||||
style.type = 'text/css';
|
||||
style.id = 'matchmediajs-test';
|
||||
|
||||
if (!script) {
|
||||
document.head.appendChild(style);
|
||||
} else {
|
||||
script.parentNode.insertBefore(style, script);
|
||||
}
|
||||
|
||||
// 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
|
||||
info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;
|
||||
|
||||
styleMedia = {
|
||||
matchMedium: function(media) {
|
||||
var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
|
||||
|
||||
// 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = text;
|
||||
} else {
|
||||
style.textContent = text;
|
||||
}
|
||||
|
||||
// Test if media query is true or false
|
||||
return info.width === '1px';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return function(media) {
|
||||
return {
|
||||
matches: styleMedia.matchMedium(media || 'all'),
|
||||
media: media || 'all'
|
||||
};
|
||||
};
|
||||
}());
|
13
bower_components/matchMedia/package.json
vendored
Normal file
13
bower_components/matchMedia/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"author": "Scott Jehl",
|
||||
"name": "matchmedia-polyfill",
|
||||
"description": "matchMedia polyfill for testing media queries in JS",
|
||||
"license": "MIT",
|
||||
"version": "0.3.2",
|
||||
"homepage": "http://github.com/paulirish/matchMedia.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/paulirish/matchMedia.js"
|
||||
},
|
||||
"main": "./matchMedia.js"
|
||||
}
|
75
bower_components/matchMedia/test/body.html
vendored
Normal file
75
bower_components/matchMedia/test/body.html
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>matchMedia polyfill test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="lib/JSLitmus.js"></script>
|
||||
<script type="text/javascript" src="../matchMedia.js"></script>
|
||||
<script type="text/javascript" src="../matchMedia.addListener.js"></script>
|
||||
<script type="text/javascript">
|
||||
var typeScreen = window.matchMedia('screen'),
|
||||
typePrint = window.matchMedia('print'),
|
||||
onlyAll = window.matchMedia('only all'),
|
||||
minWidth = window.matchMedia('(min-width: 0px)'),
|
||||
minWidth768 = window.matchMedia('(min-width: 768px)'),
|
||||
handleMinWidth768 = function(mql) {
|
||||
alert('Browsers that support CSS3 media queries: "(min-width: 768px)" = ' + mql.matches);
|
||||
};
|
||||
|
||||
// This group should be supported by everything
|
||||
alert('All browsers: "screen" = ' + typeScreen.matches);
|
||||
alert('All browsers: "print" = ' + typePrint.matches);
|
||||
|
||||
// Supports CSS3 media queries such as width/height, device-width/height, orientation but not matchMedia api
|
||||
// See http://caniuse.com/#search=media%20queries and http://caniuse.com/#search=matchMedia
|
||||
// The following browsers should return true to 'onlyAll.matches' and 'minWidth.matches'
|
||||
// IE 9,
|
||||
// Firefox 3.5 - 5.0,
|
||||
// Chrome 4.0 - 8.0,
|
||||
// Safari 4 - 5.0,
|
||||
// Opera 9.5 - 12.0,
|
||||
// iOS 3.2 - 4.3,
|
||||
// Android 2.1 - 2.3
|
||||
// Opera mini 10.0 - 12.0
|
||||
// Blackberry 7.0
|
||||
alert('Browsers that support CSS3 media queries: "only all" = ' + onlyAll.matches);
|
||||
alert('Browsers that support CSS3 media queries: "(min-width: 0px)" = ' + minWidth.matches);
|
||||
|
||||
// Testing addListener support
|
||||
minWidth768.addListener(handleMinWidth768);
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
// Performance test
|
||||
JSLitmus.test('matchMedia.js', function() {
|
||||
window.matchMedia('screen and (min-width: 600px) and (min-height: 400px), screen and (min-height: 400px)');
|
||||
});
|
||||
</script>
|
||||
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>
|
||||
Test case for IE auto closing select elements
|
||||
</legend>
|
||||
|
||||
<select name="demoSelect" id="demoSelect">
|
||||
<option value="value1" selected>Value 1</option>
|
||||
<option value="value2">Value 2</option>
|
||||
<option value="value3">Value 3</option>
|
||||
</select>
|
||||
|
||||
<p>
|
||||
Click on the select. If it auto closes in 500 milliseconds then we've failed.
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('demoSelect').onmousedown = function() {
|
||||
setTimeout(function() {
|
||||
window.matchMedia('screen');
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
74
bower_components/matchMedia/test/head.html
vendored
Normal file
74
bower_components/matchMedia/test/head.html
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>matchMedia polyfill test page</title>
|
||||
<script type="text/javascript" src="lib/JSLitmus.js"></script>
|
||||
<script type="text/javascript" src="../matchMedia.js"></script>
|
||||
<script type="text/javascript" src="../matchMedia.addListener.js"></script>
|
||||
<script type="text/javascript">
|
||||
var typeScreen = window.matchMedia('screen'),
|
||||
typePrint = window.matchMedia('print'),
|
||||
onlyAll = window.matchMedia('only all'),
|
||||
minWidth = window.matchMedia('(min-width: 0px)'),
|
||||
minWidth768 = window.matchMedia('(min-width: 768px)'),
|
||||
handleMinWidth768 = function(mql) {
|
||||
alert('Browsers that support CSS3 media queries: "(min-width: 768px)" = ' + mql.matches);
|
||||
};
|
||||
|
||||
// This group should be supported by everything
|
||||
alert('All browsers: "screen" = ' + typeScreen.matches);
|
||||
alert('All browsers: "print" = ' + typePrint.matches);
|
||||
|
||||
// Supports CSS3 media queries such as width/height, device-width/height, orientation but not matchMedia api
|
||||
// See http://caniuse.com/#search=media%20queries and http://caniuse.com/#search=matchMedia
|
||||
// The following browsers should return true to 'onlyAll.matches' and 'minWidth.matches'
|
||||
// IE 9,
|
||||
// Firefox 3.5 - 5.0,
|
||||
// Chrome 4.0 - 8.0,
|
||||
// Safari 4 - 5.0,
|
||||
// Opera 9.5 - 12.0,
|
||||
// iOS 3.2 - 4.3,
|
||||
// Android 2.1 - 2.3
|
||||
// Opera mini 10.0 - 12.0
|
||||
// Blackberry 7.0
|
||||
alert('Browsers that support CSS3 media queries: "only all" = ' + onlyAll.matches);
|
||||
alert('Browsers that support CSS3 media queries: "(min-width: 0px)" = ' + minWidth.matches);
|
||||
|
||||
// Testing addListener support
|
||||
minWidth768.addListener(handleMinWidth768);
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
// Performance test
|
||||
JSLitmus.test('matchMedia.js', function() {
|
||||
window.matchMedia('screen and (min-width: 600px) and (min-height: 400px), screen and (min-height: 400px)');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>
|
||||
Test case for IE auto closing select elements
|
||||
</legend>
|
||||
|
||||
<select name="demoSelect" id="demoSelect">
|
||||
<option value="value1" selected>Value 1</option>
|
||||
<option value="value2">Value 2</option>
|
||||
<option value="value3">Value 3</option>
|
||||
</select>
|
||||
|
||||
<p>
|
||||
Click on the select. If it auto closes in 500 milliseconds then we've failed.
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('demoSelect').onmousedown = function() {
|
||||
setTimeout(function() {
|
||||
window.matchMedia('screen');
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
9
bower_components/matchMedia/test/iframe_body.html
vendored
Normal file
9
bower_components/matchMedia/test/iframe_body.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>matchMedia polyfill test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="body.html"></iframe>
|
||||
</body>
|
||||
</html>
|
9
bower_components/matchMedia/test/iframe_head.html
vendored
Normal file
9
bower_components/matchMedia/test/iframe_head.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>matchMedia polyfill test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="body.html"></iframe>
|
||||
</body>
|
||||
</html>
|
649
bower_components/matchMedia/test/lib/JSLitmus.js
vendored
Normal file
649
bower_components/matchMedia/test/lib/JSLitmus.js
vendored
Normal file
@@ -0,0 +1,649 @@
|
||||
// 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();
|
||||
})();
|
Reference in New Issue
Block a user