mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +00:00
Optimize benchmark setups, cleanup perf/index.html, and add platform.js.
Former-commit-id: 228b7b6fd230638f9ec8c79eafa56506fc84b79a
This commit is contained in:
@@ -21,36 +21,37 @@
|
||||
var lodash = _.noConflict();
|
||||
</script>
|
||||
<script src="../vendor/underscore/underscore.js"></script>
|
||||
<script src="../vendor/platform.js/platform.js"></script>
|
||||
<script src="../vendor/benchmark.js/benchmark.js"></script>
|
||||
<script src="../vendor/firebug-lite/src/firebug-lite-debug.js"></script>
|
||||
<script src="perf.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
if (!/[?&]nojava=true(?:&|$)/.test(location.search)) {
|
||||
var useApplet = !/[?&]nojava=true(?:&|$)/.test(location.search);
|
||||
|
||||
function init() {
|
||||
var fbUI = document.getElementById('FirebugUI'),
|
||||
fbDoc = fbUI && (fbDoc = fbUI.contentWindow || fbUI.contentDocument).document || fbDoc,
|
||||
fbCommandLine = fbDoc && fbDoc.getElementById('fbCommandLine');
|
||||
|
||||
if (!fbCommandLine) {
|
||||
return setTimeout(init, 15);
|
||||
}
|
||||
fbUI.style.height = fbDoc.body.style.height = fbDoc.documentElement.style.height = '100%';
|
||||
|
||||
// give applet time to initialize
|
||||
lodash.delay(run, useApplet ? 500 : 15);
|
||||
}
|
||||
|
||||
if (useApplet) {
|
||||
// using innerHTML avoids an alert in some versions of IE6
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<applet code=nano archive="../vendor/benchmark.js/nano.jar">';
|
||||
document.body.insertBefore(div.lastChild, document.body.firstChild);
|
||||
}
|
||||
|
||||
window.onload = init;
|
||||
}());
|
||||
|
||||
window.onload = function() {
|
||||
function init() {
|
||||
var fbUI = document.getElementById('FirebugUI'),
|
||||
fbDoc = fbUI && (fbDoc = fbUI.contentWindow || fbUI.contentDocument).document || fbDoc;
|
||||
|
||||
if (!fbDoc || !fbDoc.body) {
|
||||
return setTimeout(init, 15);
|
||||
}
|
||||
var sibling = document.getElementsByTagName('script')[0],
|
||||
script = document.createElement('script');
|
||||
|
||||
fbUI.style.height = fbDoc.body.style.height = fbDoc.documentElement.style.height = '100%';
|
||||
script.src = 'perf.js?t=' + (+new Date);
|
||||
sibling.parentNode.insertBefore(script, sibling);
|
||||
|
||||
}
|
||||
init();
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
639
perf/perf.js
639
perf/perf.js
@@ -25,10 +25,8 @@
|
||||
_._ || _
|
||||
);
|
||||
|
||||
/** Used to access the Firebug Lite panel */
|
||||
var fbPanel = (fbPanel = window.document && document.getElementById('FirebugUI')) &&
|
||||
(fbPanel = (fbPanel = fbPanel.contentWindow || fbPanel.contentDocument).document || fbPanel) &&
|
||||
fbPanel.getElementById('fbPanel1');
|
||||
/** Used to access the Firebug Lite panel (set by `run`) */
|
||||
var fbPanel;
|
||||
|
||||
/** Used to score Lo-Dash and Underscore performance */
|
||||
var score = { 'lodash': 0, 'underscore': 0 };
|
||||
@@ -72,20 +70,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all benchmark suites.
|
||||
*
|
||||
* @private (@public in the browser)
|
||||
*/
|
||||
function run() {
|
||||
fbPanel = (fbPanel = window.document && document.getElementById('FirebugUI')) &&
|
||||
(fbPanel = (fbPanel = fbPanel.contentWindow || fbPanel.contentDocument).document || fbPanel) &&
|
||||
fbPanel.getElementById('fbPanel1');
|
||||
|
||||
log('\nSit back and relax, this may take a while.');
|
||||
suites[0].run();
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
lodash.extend(Benchmark.options, {
|
||||
'async': true,
|
||||
'maxTime': 1,
|
||||
'minSamples': 30,
|
||||
'setup': function() {
|
||||
var window = Function('return this || global')(),
|
||||
_ = window._,
|
||||
lodash = window.lodash;
|
||||
lodash = window.lodash,
|
||||
belt = this.name == 'Lo-Dash' ? lodash : _;
|
||||
|
||||
var index,
|
||||
length,
|
||||
belt = this.name == 'Lo-Dash' ? lodash : _,
|
||||
limit = 20,
|
||||
object = {},
|
||||
objects = Array(limit),
|
||||
@@ -94,23 +104,30 @@
|
||||
nestedNumbers = [1, [2], [3, [[4]]]],
|
||||
twoNumbers = [12, 21];
|
||||
|
||||
var object2 = {},
|
||||
objects2 = Array(limit),
|
||||
numbers2 = Array(limit),
|
||||
nestedNumbers2 = [1, [2], [3, [[4]]]];
|
||||
|
||||
for (index = 0, length = limit; index < length; index++) {
|
||||
numbers[index] = index;
|
||||
numbers2[index] = index;
|
||||
|
||||
object['key' + index] = index;
|
||||
object2['key' + index] = index;
|
||||
|
||||
objects[index] = { 'num': index };
|
||||
objects2[index] = { 'num': index };
|
||||
}
|
||||
|
||||
if (typeof isBindAll != 'undefined') {
|
||||
if (typeof bind != 'undefined') {
|
||||
var contextObject = { 'name': 'moe' },
|
||||
ctor = function() {};
|
||||
|
||||
var func = function(greeting, punctuation) {
|
||||
return greeting + ', ' + this.name + (punctuation || '.');
|
||||
};
|
||||
|
||||
var lodashBoundNormal = lodash.bind(func, contextObject),
|
||||
lodashBoundCtor = lodash.bind(ctor, contextObject),
|
||||
lodashBoundPartial = lodash.bind(func, contextObject, 'hi');
|
||||
|
||||
var _boundNormal = _.bind(func, contextObject),
|
||||
_boundCtor = _.bind(ctor, contextObject),
|
||||
_boundPartial = _.bind(func, contextObject, 'hi');
|
||||
}
|
||||
|
||||
if (typeof bindAll != 'undefined') {
|
||||
var bindAllObjects = Array(this.count),
|
||||
funcNames = belt.functions(lodash);
|
||||
|
||||
@@ -120,153 +137,154 @@
|
||||
}
|
||||
}
|
||||
|
||||
var ctor = function() {};
|
||||
if (typeof groupBy != 'undefined') {
|
||||
var wordToNumber = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'three': 3,
|
||||
'four': 4,
|
||||
'five': 5,
|
||||
'six': 6,
|
||||
'seven': 7,
|
||||
'eight': 8,
|
||||
'nine': 9,
|
||||
'ten': 10,
|
||||
'eleven': 11,
|
||||
'twelve': 12,
|
||||
'thirteen': 13,
|
||||
'fourteen': 14,
|
||||
'fifteen': 15,
|
||||
'sixteen': 16,
|
||||
'seventeen': 17,
|
||||
'eighteen': 18,
|
||||
'nineteen': 19,
|
||||
'twenty': 20,
|
||||
'twenty-one': 21,
|
||||
'twenty-two': 22,
|
||||
'twenty-three': 23,
|
||||
'twenty-four': 24,
|
||||
'twenty-five': 25
|
||||
};
|
||||
|
||||
var func = function(greeting, punctuation) {
|
||||
return greeting + ', ' + this.name + (punctuation || '.');
|
||||
};
|
||||
var words = belt.keys(wordToNumber).slice(0, limit);
|
||||
}
|
||||
|
||||
var objectOfPrimitives = {
|
||||
'boolean': true,
|
||||
'number': 1,
|
||||
'string': 'a'
|
||||
};
|
||||
if (typeof isEqual != 'undefined') {
|
||||
var objectOfPrimitives = {
|
||||
'boolean': true,
|
||||
'number': 1,
|
||||
'string': 'a'
|
||||
};
|
||||
|
||||
var objectOfObjects = {
|
||||
'boolean': new Boolean(true),
|
||||
'number': new Number(1),
|
||||
'string': new String('a')
|
||||
};
|
||||
var objectOfObjects = {
|
||||
'boolean': new Boolean(true),
|
||||
'number': new Number(1),
|
||||
'string': new String('a')
|
||||
};
|
||||
|
||||
var contextObject = { 'name': 'moe' };
|
||||
var object2 = {},
|
||||
objects2 = Array(limit),
|
||||
numbers2 = Array(limit),
|
||||
nestedNumbers2 = [1, [2], [3, [[4]]]];
|
||||
|
||||
var lodashBoundNormal = lodash.bind(func, contextObject),
|
||||
lodashBoundCtor = lodash.bind(ctor, contextObject),
|
||||
lodashBoundPartial = lodash.bind(func, contextObject, 'hi');
|
||||
for (index = 0, length = limit; index < length; index++) {
|
||||
numbers2[index] = index;
|
||||
object2['key' + index] = index;
|
||||
objects2[index] = { 'num': index };
|
||||
}
|
||||
}
|
||||
|
||||
var _boundNormal = _.bind(func, contextObject),
|
||||
_boundCtor = _.bind(ctor, contextObject),
|
||||
_boundPartial = _.bind(func, contextObject, 'hi');
|
||||
if (typeof template != 'undefined') {
|
||||
var tplData = {
|
||||
'header1': 'Header1',
|
||||
'header2': 'Header2',
|
||||
'header3': 'Header3',
|
||||
'header4': 'Header4',
|
||||
'header5': 'Header5',
|
||||
'header6': 'Header6',
|
||||
'list': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
|
||||
};
|
||||
|
||||
var tplData = {
|
||||
'header1': 'Header1',
|
||||
'header2': 'Header2',
|
||||
'header3': 'Header3',
|
||||
'header4': 'Header4',
|
||||
'header5': 'Header5',
|
||||
'header6': 'Header6',
|
||||
'list': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
|
||||
};
|
||||
var tplBase =
|
||||
'<div>' +
|
||||
"<h1 class='header1'><%= header1 %></h1>" +
|
||||
"<h2 class='header2'><%= header2 %></h2>" +
|
||||
"<h3 class='header3'><%= header3 %></h3>" +
|
||||
"<h4 class='header4'><%= header4 %></h4>" +
|
||||
"<h5 class='header5'><%= header5 %></h5>" +
|
||||
"<h6 class='header6'><%= header6 %></h6>";
|
||||
|
||||
var tplBase =
|
||||
'<div>' +
|
||||
"<h1 class='header1'><%= header1 %></h1>" +
|
||||
"<h2 class='header2'><%= header2 %></h2>" +
|
||||
"<h3 class='header3'><%= header3 %></h3>" +
|
||||
"<h4 class='header4'><%= header4 %></h4>" +
|
||||
"<h5 class='header5'><%= header5 %></h5>" +
|
||||
"<h6 class='header6'><%= header6 %></h6>";
|
||||
var tpl =
|
||||
tplBase +
|
||||
"<ul class='list'>" +
|
||||
"<li class='item'><%= list[0] %></li>" +
|
||||
"<li class='item'><%= list[1] %></li>" +
|
||||
"<li class='item'><%= list[2] %></li>" +
|
||||
"<li class='item'><%= list[3] %></li>" +
|
||||
"<li class='item'><%= list[4] %></li>" +
|
||||
"<li class='item'><%= list[5] %></li>" +
|
||||
"<li class='item'><%= list[6] %></li>" +
|
||||
"<li class='item'><%= list[7] %></li>" +
|
||||
"<li class='item'><%= list[8] %></li>" +
|
||||
"<li class='item'><%= list[9] %></li>" +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
var tpl =
|
||||
tplBase +
|
||||
"<ul class='list'>" +
|
||||
"<li class='item'><%= list[0] %></li>" +
|
||||
"<li class='item'><%= list[1] %></li>" +
|
||||
"<li class='item'><%= list[2] %></li>" +
|
||||
"<li class='item'><%= list[3] %></li>" +
|
||||
"<li class='item'><%= list[4] %></li>" +
|
||||
"<li class='item'><%= list[5] %></li>" +
|
||||
"<li class='item'><%= list[6] %></li>" +
|
||||
"<li class='item'><%= list[7] %></li>" +
|
||||
"<li class='item'><%= list[8] %></li>" +
|
||||
"<li class='item'><%= list[9] %></li>" +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
var tplWithEvaluate =
|
||||
tplBase +
|
||||
"<ul class='list'>" +
|
||||
'<% for (var index = 0, length = list.length; index < length; index++) { %>' +
|
||||
"<li class='item'><%= list[index] %></li>" +
|
||||
'<% } %>' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
var tplWithEvaluate =
|
||||
tplBase +
|
||||
"<ul class='list'>" +
|
||||
'<% for (var index = 0, length = list.length; index < length; index++) { %>' +
|
||||
"<li class='item'><%= list[index] %></li>" +
|
||||
'<% } %>' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
var tplBaseVerbose =
|
||||
'<div>' +
|
||||
"<h1 class='header1'><%= data.header1 %></h1>" +
|
||||
"<h2 class='header2'><%= data.header2 %></h2>" +
|
||||
"<h3 class='header3'><%= data.header3 %></h3>" +
|
||||
"<h4 class='header4'><%= data.header4 %></h4>" +
|
||||
"<h5 class='header5'><%= data.header5 %></h5>" +
|
||||
"<h6 class='header6'><%= data.header6 %></h6>";
|
||||
|
||||
var tplBaseVerbose =
|
||||
'<div>' +
|
||||
"<h1 class='header1'><%= data.header1 %></h1>" +
|
||||
"<h2 class='header2'><%= data.header2 %></h2>" +
|
||||
"<h3 class='header3'><%= data.header3 %></h3>" +
|
||||
"<h4 class='header4'><%= data.header4 %></h4>" +
|
||||
"<h5 class='header5'><%= data.header5 %></h5>" +
|
||||
"<h6 class='header6'><%= data.header6 %></h6>";
|
||||
var tplVerbose =
|
||||
tplBaseVerbose +
|
||||
"<ul class='list'>" +
|
||||
"<li class='item'><%= data.list[0] %></li>" +
|
||||
"<li class='item'><%= data.list[1] %></li>" +
|
||||
"<li class='item'><%= data.list[2] %></li>" +
|
||||
"<li class='item'><%= data.list[3] %></li>" +
|
||||
"<li class='item'><%= data.list[4] %></li>" +
|
||||
"<li class='item'><%= data.list[5] %></li>" +
|
||||
"<li class='item'><%= data.list[6] %></li>" +
|
||||
"<li class='item'><%= data.list[7] %></li>" +
|
||||
"<li class='item'><%= data.list[8] %></li>" +
|
||||
"<li class='item'><%= data.list[9] %></li>" +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
var tplVerbose =
|
||||
tplBaseVerbose +
|
||||
"<ul class='list'>" +
|
||||
"<li class='item'><%= data.list[0] %></li>" +
|
||||
"<li class='item'><%= data.list[1] %></li>" +
|
||||
"<li class='item'><%= data.list[2] %></li>" +
|
||||
"<li class='item'><%= data.list[3] %></li>" +
|
||||
"<li class='item'><%= data.list[4] %></li>" +
|
||||
"<li class='item'><%= data.list[5] %></li>" +
|
||||
"<li class='item'><%= data.list[6] %></li>" +
|
||||
"<li class='item'><%= data.list[7] %></li>" +
|
||||
"<li class='item'><%= data.list[8] %></li>" +
|
||||
"<li class='item'><%= data.list[9] %></li>" +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
var tplVerboseWithEvaluate =
|
||||
tplBaseVerbose +
|
||||
"<ul class='list'>" +
|
||||
'<% for (var index = 0, length = data.list.length; index < length; index++) { %>' +
|
||||
"<li class='item'><%= data.list[index] %></li>" +
|
||||
'<% } %>' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
var tplVerboseWithEvaluate =
|
||||
tplBaseVerbose +
|
||||
"<ul class='list'>" +
|
||||
'<% for (var index = 0, length = data.list.length; index < length; index++) { %>' +
|
||||
"<li class='item'><%= data.list[index] %></li>" +
|
||||
'<% } %>' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
var settingsObject = { 'variable': 'data' };
|
||||
|
||||
var settingsObject = { 'variable': 'data' };
|
||||
var lodashTpl = lodash.template(tpl),
|
||||
lodashTplWithEvaluate = lodash.template(tplWithEvaluate),
|
||||
lodashTplVerbose = lodash.template(tplVerbose, null, settingsObject),
|
||||
lodashTplVerboseWithEvaluate = lodash.template(tplVerboseWithEvaluate, null, settingsObject);
|
||||
|
||||
var lodashTpl = lodash.template(tpl),
|
||||
lodashTplWithEvaluate = lodash.template(tplWithEvaluate),
|
||||
lodashTplVerbose = lodash.template(tplVerbose, null, settingsObject),
|
||||
lodashTplVerboseWithEvaluate = lodash.template(tplVerboseWithEvaluate, null, settingsObject);
|
||||
|
||||
var _tpl = _.template(tpl),
|
||||
_tplWithEvaluate = _.template(tplWithEvaluate),
|
||||
_tplVerbose = _.template(tplVerbose, null, settingsObject),
|
||||
_tplVerboseWithEvaluate = _.template(tplVerboseWithEvaluate, null, settingsObject);
|
||||
|
||||
var wordToNumber = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'three': 3,
|
||||
'four': 4,
|
||||
'five': 5,
|
||||
'six': 6,
|
||||
'seven': 7,
|
||||
'eight': 8,
|
||||
'nine': 9,
|
||||
'ten': 10,
|
||||
'eleven': 11,
|
||||
'twelve': 12,
|
||||
'thirteen': 13,
|
||||
'fourteen': 14,
|
||||
'fifteen': 15,
|
||||
'sixteen': 16,
|
||||
'seventeen': 17,
|
||||
'eighteen': 18,
|
||||
'nineteen': 19,
|
||||
'twenty': 20,
|
||||
'twenty-one': 21,
|
||||
'twenty-two': 22,
|
||||
'twenty-three': 23,
|
||||
'twenty-four': 24,
|
||||
'twenty-five': 25
|
||||
};
|
||||
|
||||
var words = belt.keys(wordToNumber).slice(0, limit);
|
||||
var _tpl = _.template(tpl),
|
||||
_tplWithEvaluate = _.template(tplWithEvaluate),
|
||||
_tplVerbose = _.template(tplVerbose, null, settingsObject),
|
||||
_tplVerboseWithEvaluate = _.template(tplVerboseWithEvaluate, null, settingsObject);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -331,61 +349,97 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.bind` (uses native `Function#bind` if available and inferred fast)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.bind(func, { 'name': 'moe' }, 'hi');
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.bind(func, { 'name': 'moe' }, 'hi');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.bind(func, { 'name': 'moe' }, 'hi');
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.bind(func, { 'name': 'moe' }, 'hi');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('bound call')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashBoundNormal();
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashBoundNormal();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_boundNormal();
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_boundNormal();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('bound call with arguments')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashBoundNormal('hi', '!');
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashBoundNormal('hi', '!');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_boundNormal('hi', '!');
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_boundNormal('hi', '!');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('bound and partially applied call (uses native `Function#bind` if available)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashBoundPartial();
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashBoundPartial();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_boundPartial();
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_boundPartial();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('bound and partially applied call with arguments (uses native `Function#bind` if available)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashBoundPartial('!');
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashBoundPartial('!');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_boundPartial('!');
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_boundPartial('!');
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('bound and called in a `new` expression, i.e. `new bound` (edge case)')
|
||||
.add('Lo-Dash', function() {
|
||||
new lodashBoundCtor();
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
new lodashBoundCtor();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
new _boundCtor();
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
new _boundCtor();
|
||||
},
|
||||
'teardown': 'function bind(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -397,13 +451,13 @@
|
||||
'fn': function() {
|
||||
lodash.bindAll.apply(lodash, [bindAllObjects.pop()].concat(funcNames));
|
||||
},
|
||||
'teardown': 'function isBindAll(){}'
|
||||
'teardown': 'function bindAll(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.bindAll.apply(_, [bindAllObjects.pop()].concat(funcNames));
|
||||
},
|
||||
'teardown': 'function isBindAll(){}'
|
||||
'teardown': 'function bindAll(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -413,13 +467,13 @@
|
||||
'fn': function() {
|
||||
lodash.bindAll(bindAllObjects.pop());
|
||||
},
|
||||
'teardown': 'function isBindAll(){}'
|
||||
'teardown': 'function bindAll(){}'
|
||||
})
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.bindAll(bindAllObjects.pop());
|
||||
},
|
||||
'teardown': 'function isBindAll(){}'
|
||||
'teardown': 'function bindAll(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -607,21 +661,33 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.groupBy` with `property` name iterating an array')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.groupBy(words, 'length');
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.groupBy(words, 'length');
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.groupBy(words, 'length');
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.groupBy(words, 'length');
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.groupBy` with `callback` iterating an object')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.groupBy(wordToNumber, function(num) { return num >> 1; });
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.groupBy(wordToNumber, function(num) { return num >> 1; });
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.groupBy(wordToNumber, function(num) { return num >> 1; });
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.groupBy(wordToNumber, function(num) { return num >> 1; });
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -663,51 +729,81 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.isEqual(objectOfPrimitives, objectOfObjects);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.isEqual(objectOfPrimitives, objectOfObjects);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.isEqual(objectOfPrimitives, objectOfObjects);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.isEqual(objectOfPrimitives, objectOfObjects);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing arrays')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.isEqual(numbers, numbers2);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.isEqual(numbers, numbers2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.isEqual(numbers, numbers2);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.isEqual(numbers, numbers2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing nested arrays')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.isEqual(nestedNumbers, nestedNumbers2);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.isEqual(nestedNumbers, nestedNumbers2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.isEqual(nestedNumbers, nestedNumbers2);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.isEqual(nestedNumbers, nestedNumbers2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing arrays of objects')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.isEqual(objects, objects2);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.isEqual(objects, objects2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.isEqual(objects, objects2);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.isEqual(objects, objects2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.isEqual` comparing objects')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.isEqual(object, object2);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.isEqual(object, object2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.isEqual(object, object2);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.isEqual(object, object2);
|
||||
},
|
||||
'teardown': 'function isEqual(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -885,11 +981,17 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.sortBy` with `property` name')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.sortBy(words, 'length');
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.sortBy(words, 'length');
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.sortBy(words, 'length');
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.sortBy(words, 'length');
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -907,15 +1009,21 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.sortedIndex` with `callback`')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.sortedIndex(words, 'twenty-five', function(value) {
|
||||
return wordToNumber[value];
|
||||
});
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.sortedIndex(words, 'twenty-five', function(value) {
|
||||
return wordToNumber[value];
|
||||
});
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.sortedIndex(words, 'twenty-five', function(value) {
|
||||
return wordToNumber[value];
|
||||
});
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.sortedIndex(words, 'twenty-five', function(value) {
|
||||
return wordToNumber[value];
|
||||
});
|
||||
},
|
||||
'teardown': 'function groupBy(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -923,61 +1031,97 @@
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.template` without "evaluate" delimiters (slow path)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.template(tpl, tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.template(tpl, tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.template(tpl, tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.template(tpl, tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('`_.template` with "evaluate" delimiters (slow path)')
|
||||
.add('Lo-Dash', function() {
|
||||
lodash.template(tplWithEvaluate, tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodash.template(tplWithEvaluate, tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_.template(tplWithEvaluate, tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_.template(tplWithEvaluate, tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('compiled template without "evaluate" delimiters')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashTpl(tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashTpl(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_tpl(tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_tpl(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('compiled template with "evaluate" delimiters')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashTplWithEvaluate(tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashTplWithEvaluate(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_tplWithEvaluate(tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_tplWithEvaluate(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('compiled template without a with-statement or "evaluate" delimiters')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashTplVerbose(tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashTplVerbose(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_tplVerbose(tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_tplVerbose(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
suites.push(
|
||||
Benchmark.Suite('compiled template without a with-statement using "evaluate" delimiters')
|
||||
.add('Lo-Dash', function() {
|
||||
lodashTplVerboseWithEvaluate(tplData);
|
||||
.add('Lo-Dash', {
|
||||
'fn': function() {
|
||||
lodashTplVerboseWithEvaluate(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
.add('Underscore', function() {
|
||||
_tplVerboseWithEvaluate(tplData);
|
||||
.add('Underscore', {
|
||||
'fn': function() {
|
||||
_tplVerboseWithEvaluate(tplData);
|
||||
},
|
||||
'teardown': 'function template(){}'
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1084,8 +1228,11 @@
|
||||
if (Benchmark.platform + '') {
|
||||
log(Benchmark.platform + '');
|
||||
}
|
||||
// start suites
|
||||
log('\nSit back and relax, this may take a while.');
|
||||
suites[0].run();
|
||||
|
||||
// in the browser, expose `run` to be called later
|
||||
if (window.document) {
|
||||
window.run = run;
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}(typeof global == 'object' && global || this));
|
||||
|
||||
20
vendor/platform.js/LICENSE.txt
vendored
Normal file
20
vendor/platform.js/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
99
vendor/platform.js/README.md
vendored
Normal file
99
vendor/platform.js/README.md
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
# Platform.js <sup>v1.0.0-pre</sup>
|
||||
|
||||
A platform detection library that works on nearly all JavaScript platforms<sup><a name="fnref1" href="#fn1">1</a></sup>.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Platform.js is for informational purposes only and **not** intended as a substitution for [feature detection/inference](http://allyoucanleet.com/post/18087210413/feature-testing-costs#screencast2) checks.
|
||||
|
||||
## BestieJS
|
||||
|
||||
Platform.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation for Platform.js can be viewed here: [/doc/README.md](https://github.com/bestiejs/platform.js/blob/master/doc/README.md#readme)
|
||||
|
||||
For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/platform.js/wiki/Roadmap).
|
||||
|
||||
## Installation and usage
|
||||
|
||||
In a browser or Adobe AIR:
|
||||
|
||||
~~~ html
|
||||
<script src="platform.js"></script>
|
||||
~~~
|
||||
|
||||
Via [npm](http://npmjs.org/):
|
||||
|
||||
~~~ bash
|
||||
npm install platform
|
||||
~~~
|
||||
|
||||
In [Narwhal](http://narwhaljs.org/), [Node.js](http://nodejs.org/), and [RingoJS](http://ringojs.org/):
|
||||
|
||||
~~~ js
|
||||
var platform = require('platform');
|
||||
~~~
|
||||
|
||||
In [Rhino](http://www.mozilla.org/rhino/):
|
||||
|
||||
~~~ js
|
||||
load('platform.js');
|
||||
~~~
|
||||
|
||||
In an AMD loader like [RequireJS](http://requirejs.org/):
|
||||
|
||||
~~~ js
|
||||
require({
|
||||
'paths': {
|
||||
'platform': 'path/to/platform'
|
||||
}
|
||||
},
|
||||
['platform'], function(platform) {
|
||||
console.log(platform.name);
|
||||
});
|
||||
~~~
|
||||
|
||||
Usage example:
|
||||
|
||||
~~~ js
|
||||
// on IE10 x86 platform preview running in IE7 compatibility mode on Windows 7 64 bit edition
|
||||
platform.name; // 'IE'
|
||||
platform.version; // '10.0'
|
||||
platform.layout; // 'Trident'
|
||||
platform.os; // 'Windows Server 2008 R2 / 7 x64'
|
||||
platform.description; // 'IE 10.0 x86 (platform preview; running in IE 7 mode) on Windows Server 2008 R2 / 7 x64'
|
||||
|
||||
// or on an iPad
|
||||
platform.name; // 'Safari'
|
||||
platform.version; // '5.1'
|
||||
platform.product; // 'iPad'
|
||||
platform.manufacturer; // 'Apple'
|
||||
platform.layout; // 'WebKit'
|
||||
platform.os; // 'iOS 5.0'
|
||||
platform.description; // 'Safari 5.1 on Apple iPad (iOS 5.0)'
|
||||
|
||||
// or parsing a given UA string
|
||||
var info = platform.parse('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7.2; en; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 11.52');
|
||||
info.name; // 'Opera'
|
||||
info.version; // '11.52'
|
||||
info.layout; // 'Presto'
|
||||
info.os; // 'Mac OS X 10.7.2'
|
||||
info.description; // 'Opera 11.52 (identifying as Firefox 4.0) on Mac OS X 10.7.2'
|
||||
~~~
|
||||
|
||||
## Footnotes
|
||||
|
||||
1. Platform.js has been tested in at least Adobe AIR 2.6, Chrome 5-15, Firefox 1.5-8, IE 6-10, Opera 9.25-11.52, Safari 2-5.1.1, Node.js 0.4.8-0.6.1, Narwhal 0.3.2, RingoJS 0.7-0.8, and Rhino 1.7RC3.
|
||||
<a name="fn1" title="Jump back to footnote 1 in the text." href="#fnref1">↩</a>
|
||||
|
||||
## Author
|
||||
|
||||
* [John-David Dalton](http://allyoucanleet.com/)
|
||||
[](https://twitter.com/jdalton "Follow @jdalton on Twitter")
|
||||
|
||||
## Contributors
|
||||
|
||||
* [Mathias Bynens](http://mathiasbynens.be/)
|
||||
[](https://twitter.com/mathias "Follow @mathias on Twitter")
|
||||
922
vendor/platform.js/platform.js
vendored
Normal file
922
vendor/platform.js/platform.js
vendored
Normal file
@@ -0,0 +1,922 @@
|
||||
/*!
|
||||
* Platform.js v1.0.0-pre <http://mths.be/platform>
|
||||
* Copyright 2010-2012 John-David Dalton <http://allyoucanleet.com/>
|
||||
* Available under MIT license <http://mths.be/mit>
|
||||
*/
|
||||
;(function(window) {
|
||||
'use strict';
|
||||
|
||||
/** Backup possible window/global object */
|
||||
var oldWin = window;
|
||||
|
||||
/** Detect free variable `exports` */
|
||||
var freeExports = typeof exports == 'object' && exports;
|
||||
|
||||
/** Detect free variable `global` */
|
||||
var freeGlobal = typeof global == 'object' && global &&
|
||||
(global == global.global ? (window = global) : global);
|
||||
|
||||
/** Opera regexp */
|
||||
var reOpera = /Opera/;
|
||||
|
||||
/** Used to resolve a value's internal [[Class]] */
|
||||
var toString = {}.toString;
|
||||
|
||||
/** Detect Java environment */
|
||||
var java = /Java/.test(getClassOf(window.java)) && window.java;
|
||||
|
||||
/** A character to represent alpha */
|
||||
var alpha = java ? 'a' : '\u03b1';
|
||||
|
||||
/** A character to represent beta */
|
||||
var beta = java ? 'b' : '\u03b2';
|
||||
|
||||
/** Browser document object */
|
||||
var doc = window.document || {};
|
||||
|
||||
/** Used to check for own properties of an object */
|
||||
var hasOwnProperty = {}.hasOwnProperty;
|
||||
|
||||
/** Browser navigator object */
|
||||
var nav = window.navigator || {};
|
||||
|
||||
/**
|
||||
* Detect Opera browser
|
||||
* http://www.howtocreate.co.uk/operaStuff/operaObject.html
|
||||
* http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini
|
||||
*/
|
||||
var opera = window.operamini || window.opera;
|
||||
|
||||
/** Opera [[Class]] */
|
||||
var operaClass = reOpera.test(operaClass = getClassOf(opera)) ? operaClass : (opera = null);
|
||||
|
||||
/** Possible global object */
|
||||
var thisBinding = this;
|
||||
|
||||
/** Browser user agent string */
|
||||
var userAgent = nav.userAgent || '';
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Capitalizes a string value.
|
||||
*
|
||||
* @private
|
||||
* @param {String} string The string to capitalize.
|
||||
* @returns {String} The capitalized string.
|
||||
*/
|
||||
function capitalize(string) {
|
||||
string = String(string);
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* An iteration utility for arrays and objects.
|
||||
*
|
||||
* @private
|
||||
* @param {Array|Object} object The object to iterate over.
|
||||
* @param {Function} callback The function called per iteration.
|
||||
*/
|
||||
function each(object, callback) {
|
||||
var index = -1,
|
||||
length = object.length;
|
||||
|
||||
if (length == length >>> 0) {
|
||||
while (++index < length) {
|
||||
callback(object[index], index, object);
|
||||
}
|
||||
} else {
|
||||
forOwn(object, callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim and conditionally capitalize string values.
|
||||
*
|
||||
* @private
|
||||
* @param {String} string The string to format.
|
||||
* @returns {String} The formatted string.
|
||||
*/
|
||||
function format(string) {
|
||||
string = trim(string);
|
||||
return /^(?:webOS|i(?:OS|P))/.test(string)
|
||||
? string
|
||||
: capitalize(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over an object's own properties, executing the `callback` for each.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to iterate over.
|
||||
* @param {Function} callback The function executed per own property.
|
||||
*/
|
||||
function forOwn(object, callback) {
|
||||
for (var key in object) {
|
||||
hasKey(object, key) && callback(object[key], key, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal [[Class]] of a value.
|
||||
*
|
||||
* @private
|
||||
* @param {Mixed} value The value.
|
||||
* @returns {String} The [[Class]].
|
||||
*/
|
||||
function getClassOf(value) {
|
||||
return value == null
|
||||
? capitalize(value)
|
||||
: toString.call(value).slice(8, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an object has the specified key as a direct property.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to check.
|
||||
* @param {String} key The key to check for.
|
||||
* @returns {Boolean} Returns `true` if key is a direct property, else `false`.
|
||||
*/
|
||||
function hasKey() {
|
||||
// lazy define for others (not as accurate)
|
||||
hasKey = function(object, key) {
|
||||
var parent = object != null && (object.constructor || Object).prototype;
|
||||
return !!parent && key in Object(object) && !(key in parent && object[key] === parent[key]);
|
||||
};
|
||||
// for modern browsers
|
||||
if (getClassOf(hasOwnProperty) == 'Function') {
|
||||
hasKey = function(object, key) {
|
||||
return object != null && hasOwnProperty.call(object, key);
|
||||
};
|
||||
}
|
||||
// for Safari 2
|
||||
else if ({}.__proto__ == Object.prototype) {
|
||||
hasKey = function(object, key) {
|
||||
var result = false;
|
||||
if (object != null) {
|
||||
object = Object(object);
|
||||
object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
return hasKey.apply(this, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Host objects can return type values that are different from their actual
|
||||
* data type. The objects we are concerned with usually return non-primitive
|
||||
* types of object, function, or unknown.
|
||||
*
|
||||
* @private
|
||||
* @param {Mixed} object The owner of the property.
|
||||
* @param {String} property The property to check.
|
||||
* @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
|
||||
*/
|
||||
function isHostType(object, property) {
|
||||
var type = object != null ? typeof object[property] : 'number';
|
||||
return !/^(?:boolean|number|string|undefined)$/.test(type) &&
|
||||
(type == 'object' ? !!object[property] : true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a string for use in a RegExp constructor by making hyphens and
|
||||
* spaces optional.
|
||||
*
|
||||
* @private
|
||||
* @param {String} string The string to qualify.
|
||||
* @returns {String} The qualified string.
|
||||
*/
|
||||
function qualify(string) {
|
||||
return String(string).replace(/([ -])(?!$)/g, '$1?');
|
||||
}
|
||||
|
||||
/**
|
||||
* A bare-bones` Array#reduce` like utility function.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to iterate over.
|
||||
* @param {Function} callback The function called per iteration.
|
||||
* @param {Mixed} accumulator Initial value of the accumulator.
|
||||
* @returns {Mixed} The accumulator.
|
||||
*/
|
||||
function reduce(array, callback) {
|
||||
var accumulator = null;
|
||||
each(array, function(value, index) {
|
||||
accumulator = callback(accumulator, value, index, array);
|
||||
});
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes leading and trailing whitespace from a string.
|
||||
*
|
||||
* @private
|
||||
* @param {String} string The string to trim.
|
||||
* @returns {String} The trimmed string.
|
||||
*/
|
||||
function trim(string) {
|
||||
return String(string).replace(/^ +| +$/g, '');
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Creates a new platform object.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @param {String} [ua = navigator.userAgent] The user agent string.
|
||||
* @returns {Object} A platform object.
|
||||
*/
|
||||
function parse(ua) {
|
||||
|
||||
ua || (ua = userAgent);
|
||||
|
||||
/** Temporary variable used over the script's lifetime */
|
||||
var data;
|
||||
|
||||
/** The CPU architecture */
|
||||
var arch = ua;
|
||||
|
||||
/** Platform description array */
|
||||
var description = [];
|
||||
|
||||
/** Platform alpha/beta indicator */
|
||||
var prerelease = null;
|
||||
|
||||
/** A flag to indicate that environment features should be used to resolve the platform */
|
||||
var useFeatures = ua == userAgent;
|
||||
|
||||
/** The browser/environment version */
|
||||
var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();
|
||||
|
||||
/* Detectable layout engines (order is important) */
|
||||
var layout = getLayout([
|
||||
{ 'label': 'WebKit', 'pattern': 'AppleWebKit' },
|
||||
'iCab',
|
||||
'Presto',
|
||||
'NetFront',
|
||||
'Tasman',
|
||||
'Trident',
|
||||
'KHTML',
|
||||
'Gecko'
|
||||
]);
|
||||
|
||||
/* Detectable browser names (order is important) */
|
||||
var name = getName([
|
||||
'Adobe AIR',
|
||||
'Arora',
|
||||
'Avant Browser',
|
||||
'Camino',
|
||||
'Epiphany',
|
||||
'Fennec',
|
||||
'Flock',
|
||||
'Galeon',
|
||||
'GreenBrowser',
|
||||
'iCab',
|
||||
'Iceweasel',
|
||||
'Iron',
|
||||
'K-Meleon',
|
||||
'Konqueror',
|
||||
'Lunascape',
|
||||
'Maxthon',
|
||||
'Midori',
|
||||
'Nook Browser',
|
||||
'PhantomJS',
|
||||
'Raven',
|
||||
'Rekonq',
|
||||
'RockMelt',
|
||||
'SeaMonkey',
|
||||
{ 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
|
||||
'Sleipnir',
|
||||
'SlimBrowser',
|
||||
'Sunrise',
|
||||
'Swiftfox',
|
||||
'WebPositive',
|
||||
'Opera Mini',
|
||||
'Opera',
|
||||
'Chrome',
|
||||
{ 'label': 'Chrome Mobile', 'pattern': 'CrMo' },
|
||||
{ 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },
|
||||
{ 'label': 'IE', 'pattern': 'MSIE' },
|
||||
'Safari'
|
||||
]);
|
||||
|
||||
/* Detectable products (order is important) */
|
||||
var product = getProduct([
|
||||
'BlackBerry',
|
||||
{ 'label': 'Galaxy S', 'pattern': 'GT-I9000' },
|
||||
{ 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },
|
||||
'Google TV',
|
||||
'iPad',
|
||||
'iPod',
|
||||
'iPhone',
|
||||
'Kindle',
|
||||
{ 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
|
||||
'Nook',
|
||||
'PlayBook',
|
||||
'PlayStation Vita',
|
||||
'TouchPad',
|
||||
'Transformer',
|
||||
'Xoom'
|
||||
]);
|
||||
|
||||
/* Detectable manufacturers */
|
||||
var manufacturer = getManufacturer({
|
||||
'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },
|
||||
'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },
|
||||
'Asus': { 'Transformer': 1 },
|
||||
'Barnes & Noble': { 'Nook': 1 },
|
||||
'BlackBerry': { 'PlayBook': 1 },
|
||||
'Google': { 'Google TV': 1 },
|
||||
'HP': { 'TouchPad': 1 },
|
||||
'LG': { },
|
||||
'Motorola': { 'Xoom': 1 },
|
||||
'Nokia': { },
|
||||
'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1 },
|
||||
'Sony': { 'PlayStation Vita': 1 }
|
||||
});
|
||||
|
||||
/* Detectable OSes (order is important) */
|
||||
var os = getOS([
|
||||
'Android',
|
||||
'CentOS',
|
||||
'Debian',
|
||||
'Fedora',
|
||||
'FreeBSD',
|
||||
'Gentoo',
|
||||
'Haiku',
|
||||
'Kubuntu',
|
||||
'Linux Mint',
|
||||
'Red Hat',
|
||||
'SuSE',
|
||||
'Ubuntu',
|
||||
'Xubuntu',
|
||||
'Cygwin',
|
||||
'Symbian OS',
|
||||
'hpwOS',
|
||||
'webOS ',
|
||||
'webOS',
|
||||
'Tablet OS',
|
||||
'Linux',
|
||||
'Mac OS X',
|
||||
'Macintosh',
|
||||
'Mac',
|
||||
'Windows 98;',
|
||||
'Windows '
|
||||
]);
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Picks the layout engine from an array of guesses.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} guesses An array of guesses.
|
||||
* @returns {String|Null} The detected layout engine.
|
||||
*/
|
||||
function getLayout(guesses) {
|
||||
return reduce(guesses, function(result, guess) {
|
||||
return result || RegExp('\\b' + (
|
||||
guess.pattern || qualify(guess)
|
||||
) + '\\b', 'i').exec(ua) && (guess.label || guess);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the manufacturer from an array of guesses.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} guesses An array of guesses.
|
||||
* @returns {String|Null} The detected manufacturer.
|
||||
*/
|
||||
function getManufacturer(guesses) {
|
||||
return reduce(guesses, function(result, value, key) {
|
||||
// lookup the manufacturer by product or scan the UA for the manufacturer
|
||||
return result || (
|
||||
value[product] ||
|
||||
value[0/*Opera 9.25 fix*/, /^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] ||
|
||||
RegExp('\\b' + (key.pattern || qualify(key)) + '(?:\\b|\\w*\\d)', 'i').exec(ua)
|
||||
) && (key.label || key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the browser name from an array of guesses.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} guesses An array of guesses.
|
||||
* @returns {String|Null} The detected browser name.
|
||||
*/
|
||||
function getName(guesses) {
|
||||
return reduce(guesses, function(result, guess) {
|
||||
return result || RegExp('\\b' + (
|
||||
guess.pattern || qualify(guess)
|
||||
) + '\\b', 'i').exec(ua) && (guess.label || guess);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the OS name from an array of guesses.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} guesses An array of guesses.
|
||||
* @returns {String|Null} The detected OS name.
|
||||
*/
|
||||
function getOS(guesses) {
|
||||
return reduce(guesses, function(result, guess) {
|
||||
var pattern = guess.pattern || qualify(guess);
|
||||
if (!result && (result =
|
||||
RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua))) {
|
||||
// platform tokens defined at
|
||||
// http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
|
||||
// http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
|
||||
data = {
|
||||
'6.2': '8',
|
||||
'6.1': 'Server 2008 R2 / 7',
|
||||
'6.0': 'Server 2008 / Vista',
|
||||
'5.2': 'Server 2003 / XP 64-bit',
|
||||
'5.1': 'XP',
|
||||
'5.01': '2000 SP1',
|
||||
'5.0': '2000',
|
||||
'4.0': 'NT',
|
||||
'4.90': 'ME'
|
||||
};
|
||||
// detect Windows version from platform tokens
|
||||
if (/^Win/i.test(result) &&
|
||||
(data = data[0/*Opera 9.25 fix*/, /[\d.]+$/.exec(result)])) {
|
||||
result = 'Windows ' + data;
|
||||
}
|
||||
// correct character case and cleanup
|
||||
result = format(String(result)
|
||||
.replace(RegExp(pattern, 'i'), guess.label || guess)
|
||||
.replace(/ ce$/i, ' CE')
|
||||
.replace(/hpw/i, 'web')
|
||||
.replace(/Macintosh/, 'Mac OS')
|
||||
.replace(/_PowerPC/i, ' OS')
|
||||
.replace(/(OS X) [^ \d]+/i, '$1')
|
||||
.replace(/\/(\d)/, ' $1')
|
||||
.replace(/_/g, '.')
|
||||
.replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
|
||||
.replace(/x86\.64/gi, 'x86_64')
|
||||
.split(' on ')[0]);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the product name from an array of guesses.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} guesses An array of guesses.
|
||||
* @returns {String|Null} The detected product name.
|
||||
*/
|
||||
function getProduct(guesses) {
|
||||
return reduce(guesses, function(result, guess) {
|
||||
var pattern = guess.pattern || qualify(guess);
|
||||
if (!result && (result =
|
||||
RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) ||
|
||||
RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua)
|
||||
)) {
|
||||
// split by forward slash and append product version if needed
|
||||
if ((result = String(guess.label || result).split('/'))[1] && !/[\d.]+/.test(result[0])) {
|
||||
result[0] += ' ' + result[1];
|
||||
}
|
||||
// correct character case and cleanup
|
||||
guess = guess.label || guess;
|
||||
result = format(result[0]
|
||||
.replace(RegExp(pattern, 'i'), guess)
|
||||
.replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')
|
||||
.replace(RegExp('(' + guess + ')(\\w)', 'i'), '$1 $2'));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the version using an array of UA patterns.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} patterns An array of UA patterns.
|
||||
* @returns {String|Null} The detected version.
|
||||
*/
|
||||
function getVersion(patterns) {
|
||||
return reduce(patterns, function(result, pattern) {
|
||||
return result || (RegExp(pattern +
|
||||
'(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;
|
||||
});
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Return platform description when the platform object is coerced to a string.
|
||||
*
|
||||
* @name toString
|
||||
* @memberOf platform
|
||||
* @type Function
|
||||
* @returns {String} The platform description.
|
||||
*/
|
||||
function toStringPlatform() {
|
||||
return this.description || '';
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// convert layout to an array so we can add extra details
|
||||
layout && (layout = [layout]);
|
||||
|
||||
// detect product names that contain their manufacturer's name
|
||||
if (manufacturer && !product) {
|
||||
product = getProduct([manufacturer]);
|
||||
}
|
||||
// clean up Google TV
|
||||
if ((data = /Google TV/.exec(product))) {
|
||||
product = data[0];
|
||||
}
|
||||
// detect simulators
|
||||
if (/\bSimulator\b/i.test(ua)) {
|
||||
product = (product ? product + ' ' : '') + 'Simulator';
|
||||
}
|
||||
// detect iOS
|
||||
if (/^iP/.test(product)) {
|
||||
name || (name = 'Safari');
|
||||
os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua))
|
||||
? ' ' + data[1].replace(/_/g, '.')
|
||||
: '');
|
||||
}
|
||||
// detect Kubuntu
|
||||
else if (name == 'Konqueror' && !/buntu/i.test(os)) {
|
||||
os = 'Kubuntu';
|
||||
}
|
||||
// detect Android browsers
|
||||
else if (manufacturer && manufacturer != 'Google' &&
|
||||
/Chrome|Vita/.test(name + ';' + product)) {
|
||||
name = 'Android Browser';
|
||||
os = /Android/.test(os) ? os : 'Android';
|
||||
}
|
||||
// detect false positives for Firefox/Safari
|
||||
else if (!name || (data = !/\bMinefield\b/i.test(ua) && /Firefox|Safari/.exec(name))) {
|
||||
// escape the `/` for Firefox 1
|
||||
if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
|
||||
// clear name of false positives
|
||||
name = null;
|
||||
}
|
||||
// reassign a generic name
|
||||
if ((data = product || manufacturer || os) &&
|
||||
(product || manufacturer || /Android|Symbian OS|Tablet OS|webOS/.test(os))) {
|
||||
name = /[a-z]+(?: Hat)?/i.exec(/Android/.test(os) ? os : data) + ' Browser';
|
||||
}
|
||||
}
|
||||
// detect non-Opera versions (order is important)
|
||||
if (!version) {
|
||||
version = getVersion([
|
||||
'(?:Cloud9|CrMo|Opera ?Mini|Raven|Silk(?!/[\\d.]+$))',
|
||||
'Version',
|
||||
qualify(name),
|
||||
'(?:Firefox|Minefield|NetFront)'
|
||||
]);
|
||||
}
|
||||
// detect stubborn layout engines
|
||||
if (layout == 'iCab' && parseFloat(version) > 3) {
|
||||
layout = ['WebKit'];
|
||||
} else if (data =
|
||||
/Opera/.test(name) && 'Presto' ||
|
||||
/\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
|
||||
!layout && /\bMSIE\b/i.test(ua) && (/^Mac/.test(os) ? 'Tasman' : 'Trident')) {
|
||||
layout = [data];
|
||||
}
|
||||
// leverage environment features
|
||||
if (useFeatures) {
|
||||
// detect server-side environments
|
||||
// Rhino has a global function while others have a global object
|
||||
if (isHostType(window, 'global')) {
|
||||
if (java) {
|
||||
data = java.lang.System;
|
||||
arch = data.getProperty('os.arch');
|
||||
os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
|
||||
}
|
||||
if (typeof exports == 'object' && exports) {
|
||||
// if `thisBinding` is the [ModuleScope]
|
||||
if (thisBinding == oldWin && typeof system == 'object' && (data = [system])[0]) {
|
||||
os || (os = data[0].os || null);
|
||||
try {
|
||||
data[1] = require('ringo/engine').version;
|
||||
version = data[1].join('.');
|
||||
name = 'RingoJS';
|
||||
} catch(e) {
|
||||
if (data[0].global == freeGlobal) {
|
||||
name = 'Narwhal';
|
||||
}
|
||||
}
|
||||
} else if (typeof process == 'object' && (data = process)) {
|
||||
name = 'Node.js';
|
||||
arch = data.arch;
|
||||
os = data.platform;
|
||||
version = /[\d.]+/.exec(data.version)[0];
|
||||
}
|
||||
} else if (getClassOf(window.environment) == 'Environment') {
|
||||
name = 'Rhino';
|
||||
}
|
||||
}
|
||||
// detect Adobe AIR
|
||||
else if (getClassOf(data = window.runtime) == 'ScriptBridgingProxyObject') {
|
||||
name = 'Adobe AIR';
|
||||
os = data.flash.system.Capabilities.os;
|
||||
}
|
||||
// detect PhantomJS
|
||||
else if (getClassOf(data = window.phantom) == 'RuntimeObject') {
|
||||
name = 'PhantomJS';
|
||||
version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
|
||||
}
|
||||
// detect IE compatibility modes
|
||||
else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) {
|
||||
// we're in compatibility mode when the Trident version + 4 doesn't
|
||||
// equal the document mode
|
||||
version = [version, doc.documentMode];
|
||||
if ((data = +data[1] + 4) != version[1]) {
|
||||
description.push('IE ' + version[1] + ' mode');
|
||||
layout[1] = '';
|
||||
version[1] = data;
|
||||
}
|
||||
version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
|
||||
}
|
||||
os = os && format(os);
|
||||
}
|
||||
// detect prerelease phases
|
||||
if (version && (data =
|
||||
/(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
|
||||
/(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
|
||||
/\bMinefield\b/i.test(ua) && 'a')) {
|
||||
prerelease = /b/i.test(data) ? 'beta' : 'alpha';
|
||||
version = version.replace(RegExp(data + '\\+?$'), '') +
|
||||
(prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
|
||||
}
|
||||
// rename code name "Fennec"
|
||||
if (name == 'Fennec') {
|
||||
name = 'Firefox Mobile';
|
||||
}
|
||||
// obscure Maxthon's unreliable version
|
||||
else if (name == 'Maxthon' && version) {
|
||||
version = version.replace(/\.[\d.]+/, '.x');
|
||||
}
|
||||
// detect Silk desktop/accelerated modes
|
||||
else if (name == 'Silk') {
|
||||
if (!/Mobi/i.test(ua)) {
|
||||
os = 'Android';
|
||||
description.unshift('desktop mode');
|
||||
}
|
||||
if (/Accelerated *= *true/i.test(ua)) {
|
||||
description.unshift('accelerated');
|
||||
}
|
||||
}
|
||||
// detect Windows Phone desktop mode
|
||||
else if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) {
|
||||
name += ' Mobile';
|
||||
os = 'Windows Phone OS ' + data + '.x';
|
||||
description.unshift('desktop mode');
|
||||
}
|
||||
// add mobile postfix
|
||||
else if ((name == 'IE' || name && !product && !/Browser|Mobi/.test(name)) &&
|
||||
(os == 'Windows CE' || /Mobi/i.test(ua))) {
|
||||
name += ' Mobile';
|
||||
}
|
||||
// detect IE platform preview
|
||||
else if (name == 'IE' && useFeatures && typeof external == 'object' && !external) {
|
||||
description.unshift('platform preview');
|
||||
}
|
||||
// detect BlackBerry OS version
|
||||
// http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
|
||||
else if (/BlackBerry/.test(product) && (data =
|
||||
(RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
|
||||
version)) {
|
||||
os = 'Device Software ' + data;
|
||||
version = null;
|
||||
}
|
||||
// detect Opera identifying/masking itself as another browser
|
||||
// http://www.opera.com/support/kb/view/843/
|
||||
else if (this != forOwn && (
|
||||
(useFeatures && opera) ||
|
||||
(/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
|
||||
(name == 'Firefox' && /OS X (?:\d+\.){2,}/.test(os)) ||
|
||||
(name == 'IE' && (
|
||||
(os && !/^Win/.test(os) && version > 5.5) ||
|
||||
/Windows XP/.test(os) && version > 8 ||
|
||||
version == 8 && !/Trident/.test(ua)
|
||||
))
|
||||
) && !reOpera.test(data = parse.call(forOwn, ua.replace(reOpera, '') + ';')) && data.name) {
|
||||
|
||||
// when "indentifying" the UA contains both Opera and the other browser's name
|
||||
data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
|
||||
if (reOpera.test(name)) {
|
||||
if (/IE/.test(data) && os == 'Mac OS') {
|
||||
os = null;
|
||||
}
|
||||
data = 'identify' + data;
|
||||
}
|
||||
// when "masking" the UA contains only the other browser's name
|
||||
else {
|
||||
data = 'mask' + data;
|
||||
if (operaClass) {
|
||||
name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));
|
||||
} else {
|
||||
name = 'Opera';
|
||||
}
|
||||
if (/IE/.test(data)) {
|
||||
os = null;
|
||||
}
|
||||
if (!useFeatures) {
|
||||
version = null;
|
||||
}
|
||||
}
|
||||
layout = ['Presto'];
|
||||
description.push(data);
|
||||
}
|
||||
// detect WebKit Nightly and approximate Chrome/Safari versions
|
||||
if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
|
||||
// correct build for numeric comparison
|
||||
// (e.g. "532.5" becomes "532.05")
|
||||
data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data];
|
||||
// nightly builds are postfixed with a `+`
|
||||
if (name == 'Safari' && data[1].slice(-1) == '+') {
|
||||
name = 'WebKit Nightly';
|
||||
prerelease = 'alpha';
|
||||
version = data[1].slice(0, -1);
|
||||
}
|
||||
// clear incorrect browser versions
|
||||
else if (version == data[1] ||
|
||||
version == (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1]) {
|
||||
version = null;
|
||||
}
|
||||
// use the full Chrome version when available
|
||||
data = [data[0], (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1]];
|
||||
|
||||
// detect JavaScriptCore
|
||||
// http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi
|
||||
if (!useFeatures || (/internal|\n/i.test(toString.toString()) && !data[1])) {
|
||||
layout[1] = 'like Safari';
|
||||
data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : '5');
|
||||
} else {
|
||||
layout[1] = 'like Chrome';
|
||||
data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 535.21 ? 18 : '19');
|
||||
}
|
||||
// add the postfix of ".x" or "+" for approximate versions
|
||||
layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+');
|
||||
// obscure version for some Safari 1-2 releases
|
||||
if (name == 'Safari' && (!version || parseInt(version) > 45)) {
|
||||
version = data;
|
||||
}
|
||||
}
|
||||
// strip incorrect OS versions
|
||||
if (version && version.indexOf(data = /[\d.]+$/.exec(os)) == 0 &&
|
||||
ua.indexOf('/' + data + '-') > -1) {
|
||||
os = trim(os.replace(data, ''));
|
||||
}
|
||||
// add layout engine
|
||||
if (layout && !/Avant|Nook/.test(name) && (
|
||||
/Browser|Lunascape|Maxthon/.test(name) ||
|
||||
/^(?:Adobe|Arora|Midori|Phantom|Rekonq|Rock|Sleipnir|Web)/.test(name) && layout[1])) {
|
||||
// don't add layout details to description if they are falsey
|
||||
(data = layout[layout.length - 1]) && description.push(data);
|
||||
}
|
||||
// combine contextual information
|
||||
if (description.length) {
|
||||
description = ['(' + description.join('; ') + ')'];
|
||||
}
|
||||
// append manufacturer
|
||||
if (manufacturer && product && product.indexOf(manufacturer) < 0) {
|
||||
description.push('on ' + manufacturer);
|
||||
}
|
||||
// append product
|
||||
if (product) {
|
||||
description.push((/^on /.test(description[description.length -1]) ? '' : 'on ') + product);
|
||||
}
|
||||
// add browser/OS architecture
|
||||
if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i).test(arch) && !/\bi686\b/i.test(arch)) {
|
||||
os = os && os + (data.test(os) ? '' : ' 64-bit');
|
||||
if (name && (/WOW64/i.test(ua) ||
|
||||
(useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform)))) {
|
||||
description.unshift('32-bit');
|
||||
}
|
||||
}
|
||||
|
||||
ua || (ua = null);
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* The platform object.
|
||||
*
|
||||
* @name platform
|
||||
* @type Object
|
||||
*/
|
||||
return {
|
||||
|
||||
/**
|
||||
* The browser/environment version.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'version': name && version && (description.unshift(version), version),
|
||||
|
||||
/**
|
||||
* The name of the browser/environment.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'name': name && (description.unshift(name), name),
|
||||
|
||||
/**
|
||||
* The name of the operating system.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'os': os && (name &&
|
||||
!(os == os.split(' ')[0] && (os == name.split(' ')[0] || product)) &&
|
||||
description.push(product ? '(' + os + ')' : 'on ' + os), os),
|
||||
|
||||
/**
|
||||
* The platform description.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'description': description.length ? description.join(' ') : ua,
|
||||
|
||||
/**
|
||||
* The name of the browser layout engine.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'layout': layout && layout[0],
|
||||
|
||||
/**
|
||||
* The name of the product's manufacturer.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'manufacturer': manufacturer,
|
||||
|
||||
/**
|
||||
* The alpha/beta release indicator.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'prerelease': prerelease,
|
||||
|
||||
/**
|
||||
* The name of the product hosting the browser.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'product': product,
|
||||
|
||||
/**
|
||||
* The browser's user agent string.
|
||||
*
|
||||
* @memberOf platform
|
||||
* @type String|Null
|
||||
*/
|
||||
'ua': ua,
|
||||
|
||||
// parses a user agent string into a platform object
|
||||
'parse': parse,
|
||||
|
||||
// returns the platform description
|
||||
'toString': toStringPlatform
|
||||
};
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
// expose platform
|
||||
// some AMD build optimizers, like r.js, check for specific condition patterns like the following:
|
||||
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
|
||||
// define as an anonymous module so, through path mapping, it can be aliased
|
||||
define(function() {
|
||||
return parse();
|
||||
});
|
||||
}
|
||||
// check for `exports` after `define` in case a build optimizer adds an `exports` object
|
||||
else if (freeExports) {
|
||||
// in Narwhal, Node.js, or RingoJS
|
||||
forOwn(parse(), function(value, key) {
|
||||
freeExports[key] = value;
|
||||
});
|
||||
}
|
||||
// in a browser or Rhino
|
||||
else {
|
||||
// use square bracket notation so Closure Compiler won't munge `platform`
|
||||
// http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
|
||||
window['platform'] = parse();
|
||||
}
|
||||
}(this));
|
||||
Reference in New Issue
Block a user