Update for underscore 1.8.3.

This commit is contained in:
Julia Radzhabova 2017-04-21 11:10:07 +03:00
parent 7c3686a5b3
commit 96712db8cb
87 changed files with 11832 additions and 15219 deletions

View file

@ -547,7 +547,7 @@ define([
'<% _.each(items, function(item) { %>', '<% _.each(items, function(item) { %>',
'<li id="<%= item.id %>" data-value="<%= item.value %>"><a tabindex="-1" type="menuitem"><%= scope.getDisplayValue(item) %></a></li>', '<li id="<%= item.id %>" data-value="<%= item.value %>"><a tabindex="-1" type="menuitem"><%= scope.getDisplayValue(item) %></a></li>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
items: this.store.toJSON(), items: this.store.toJSON(),
scope: this scope: this
})); }));

View file

@ -353,7 +353,7 @@ define([
'<li id="<%= item.id %>">', '<li id="<%= item.id %>">',
'<a class="font-item" tabindex="-1" type="menuitem" style="vertical-align:middle; margin: 0 0 0 -10px; height:<%=scope.getListItemHeight()%>px;"/>', '<a class="font-item" tabindex="-1" type="menuitem" style="vertical-align:middle; margin: 0 0 0 -10px; height:<%=scope.getListItemHeight()%>px;"/>',
'</li>' '</li>'
].join(''), { ].join(''))({
item: item.attributes, item: item.attributes,
scope: this scope: this
})); }));

View file

@ -421,7 +421,7 @@ define([
_.extend(options, { _.extend(options, {
cls: 'alert', cls: 'alert',
onprimary: onKeyDown, onprimary: onKeyDown,
tpl: _.template(template, options) tpl: _.template(template)(options)
}); });
var win = new Common.UI.Window(options), var win = new Common.UI.Window(options),
@ -556,7 +556,7 @@ define([
render : function() { render : function() {
var renderto = this.initConfig.renderTo || document.body; var renderto = this.initConfig.renderTo || document.body;
$(renderto).append( $(renderto).append(
_.template(template, this.initConfig) _.template(template)(this.initConfig)
); );
this.$window = $('#' + this.initConfig.id); this.$window = $('#' + this.initConfig.id);

View file

@ -71,7 +71,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.handler = _options.handler; this.handler = _options.handler;
this.toggleGroup = _options.toggleGroup; this.toggleGroup = _options.toggleGroup;

View file

@ -67,7 +67,7 @@ define([
templateUserList: _.template('<ul>' + templateUserList: _.template('<ul>' +
'<% _.each(users, function(item) { %>' + '<% _.each(users, function(item) { %>' +
'<%= _.template(usertpl, {user: item, scope: scope}) %>' + '<%= _.template(usertpl)({user: item, scope: scope}) %>' +
'<% }); %>' + '<% }); %>' +
'</ul>'), '</ul>'),
@ -82,7 +82,7 @@ define([
templateMsgList: _.template('<ul>' + templateMsgList: _.template('<ul>' +
'<% _.each(messages, function(item) { %>' + '<% _.each(messages, function(item) { %>' +
'<%= _.template(msgtpl, {msg: item, scope: scope}) %>' + '<%= _.template(msgtpl)({msg: item, scope: scope}) %>' +
'<% }); %>' + '<% }); %>' +
'</ul>'), '</ul>'),
@ -162,7 +162,7 @@ define([
_onAddUser: function(m, c, opts) { _onAddUser: function(m, c, opts) {
if (this.panelUsers) { if (this.panelUsers) {
this.panelUsers.find('ul').append(_.template(this.tplUser, {user: m, scope: this})); this.panelUsers.find('ul').append(_.template(this.tplUser)({user: m, scope: this}));
this.panelUsers.scroller.update({minScrollbarLength : 25, alwaysVisibleY: true}); this.panelUsers.scroller.update({minScrollbarLength : 25, alwaysVisibleY: true});
} }
}, },
@ -186,7 +186,7 @@ define([
var content = this.panelMessages.find('ul'); var content = this.panelMessages.find('ul');
if (content && content.length) { if (content && content.length) {
this._prepareMessage(m); this._prepareMessage(m);
content.append(_.template(this.tplMsg, {msg: m, scope: this})); content.append(_.template(this.tplMsg)({msg: m, scope: this}));
// scroll to end // scroll to end

View file

@ -98,7 +98,7 @@ define([
this.store = options.store; this.store = options.store;
this.delegate = options.delegate; this.delegate = options.delegate;
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.arrow = {margin: 20, width: 12, height: 34}; this.arrow = {margin: 20, width: 12, height: 34};
this.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20}; this.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20};

View file

@ -83,7 +83,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -57,7 +57,7 @@ define([
'<div id="id-sharing-placeholder"></div>' '<div id="id-sharing-placeholder"></div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.settingsurl = options.settingsurl || ''; this.settingsurl = options.settingsurl || '';
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);

View file

@ -66,7 +66,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.handler = _options.handler; this.handler = _options.handler;
this._chartData = null; this._chartData = null;

View file

@ -66,7 +66,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.handler = _options.handler; this.handler = _options.handler;
this._mergeData = null; this._mergeData = null;

View file

@ -65,7 +65,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -73,7 +73,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -93,7 +93,7 @@ define([
this.codepages = options.codepages; this.codepages = options.codepages;
this.settings = options.settings; this.settings = options.settings;
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);
}, },

View file

@ -314,7 +314,7 @@ define([
'<% } %>' '<% } %>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.url = options.url || ''; this.url = options.url || '';
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);

View file

@ -66,7 +66,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -80,7 +80,7 @@ define([
this.store = options.store; this.store = options.store;
this.delegate = options.delegate; this.delegate = options.delegate;
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.arrow = {margin: 20, width: 12, height: 34}; this.arrow = {margin: 20, width: 12, height: 34};
this.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20}; this.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20};
@ -616,7 +616,7 @@ define([
}, },
getPanel: function () { getPanel: function () {
this.$el = $(_.template( template, {} )); this.$el = $(_.template(template)( {} ));
if ( this.appConfig.canReview ) { if ( this.appConfig.canReview ) {
this.btnPrev.render(this.$el.find('#btn-change-prev')); this.btnPrev.render(this.$el.find('#btn-change-prev'));

View file

@ -105,7 +105,7 @@
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -81,7 +81,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.api = this.options.api; this.api = this.options.api;
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);

View file

@ -54,7 +54,7 @@ define([
'<div id="id-mail-recepients-placeholder"></div>' '<div id="id-mail-recepients-placeholder"></div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.fileChoiceUrl = options.fileChoiceUrl || ''; this.fileChoiceUrl = options.fileChoiceUrl || '';
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);

View file

@ -55,7 +55,7 @@ define([
'<div id="id-mail-merge-folder-placeholder"></div>' '<div id="id-mail-merge-folder-placeholder"></div>'
].join(''); ].join('');
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
this.mergeFolderUrl = options.mergeFolderUrl || ''; this.mergeFolderUrl = options.mergeFolderUrl || '';
this.mergedFileUrl = options.mergedFileUrl || ''; this.mergedFileUrl = options.mergedFileUrl || '';

View file

@ -89,7 +89,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.spinners = []; this.spinners = [];
this._noApply = false; this._noApply = false;

View file

@ -79,7 +79,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.spinners = []; this.spinners = [];
this._noApply = false; this._noApply = false;

View file

@ -430,7 +430,7 @@ define([
label: this.labelSelect, label: this.labelSelect,
btns: {ok: this.btnOk, cancel: this.btnCancel} btns: {ok: this.btnOk, cancel: this.btnCancel}
}); });
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -70,7 +70,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -195,7 +195,7 @@ define([
{ caption: me.textTabHome, action: 'home', extcls: 'canedit'}, { caption: me.textTabHome, action: 'home', extcls: 'canedit'},
{ caption: me.textTabInsert, action: 'ins', extcls: 'canedit'}, { caption: me.textTabInsert, action: 'ins', extcls: 'canedit'},
{ caption: me.textTabLayout, action: 'layout', extcls: 'canedit'} ]; { caption: me.textTabLayout, action: 'layout', extcls: 'canedit'} ];
config.$dom = $(_.template(template, config)); config.$dom = $(_.template(template)(config));
/** /**
* UI Components * UI Components
@ -2476,7 +2476,7 @@ define([
var _elements = $tabs || config.$dom.find('.tabs'); var _elements = $tabs || config.$dom.find('.tabs');
var $target = _elements.find('a[data-tab=' + _after_action + ']'); var $target = _elements.find('a[data-tab=' + _after_action + ']');
if ( $target.length ) { if ( $target.length ) {
$target.parent().after( _.template(_tplTab, tab) ); $target.parent().after( _.template(_tplTab)(tab) );
if ( panel ) { if ( panel ) {
_elements = $panels || config.$dom.find('.box-panels > .panel'); _elements = $panels || config.$dom.find('.box-panels > .panel');

View file

@ -175,7 +175,7 @@ define([
'</div>', '</div>',
'</label>', '</label>',
'</li>' '</li>'
].join(''), { ].join(''))({
android: Framework7.prototype.device.android, android: Framework7.prototype.device.android,
item: size, item: size,
index: index, index: index,

View file

@ -203,7 +203,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
styles: styles styles: styles
}); });

View file

@ -138,7 +138,7 @@ define([
'</div>', '</div>',
'<% }); %>', '<% }); %>',
'</div>' '</div>'
].join(''), { ].join(''))({
styles: styles styles: styles
}); });

View file

@ -100,7 +100,7 @@ define([
render: function () { render: function () {
var el = $(this.el), var el = $(this.el),
me = this; me = this;
el.html(_.template(this.template, { el.html(_.template(this.template)({
scope: this scope: this
})); }));

View file

@ -103,7 +103,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.slides = this.options.slides; this.slides = this.options.slides;
this.api = this.options.api; this.api = this.options.api;

View file

@ -87,7 +87,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.spinners = []; this.spinners = [];
this._noApply = false; this._noApply = false;

View file

@ -68,7 +68,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.spinners = []; this.spinners = [];
this._noApply = false; this._noApply = false;

View file

@ -251,7 +251,7 @@ define([
{ caption: 'Home', action: 'home', extcls: 'canedit'}, { caption: 'Home', action: 'home', extcls: 'canedit'},
{ caption: 'Insert', action: 'ins', extcls: 'canedit'} ]; { caption: 'Insert', action: 'ins', extcls: 'canedit'} ];
config.$layout = $(_.template(template, config)); config.$layout = $(_.template(template)(config));
me.paragraphControls = []; me.paragraphControls = [];
me.shapeControls = []; me.shapeControls = [];
@ -1795,7 +1795,7 @@ define([
var _elements = $tabs || config.$layout.find('.tabs'); var _elements = $tabs || config.$layout.find('.tabs');
var $target = _elements.find('a[data-tab=' + _after_action + ']'); var $target = _elements.find('a[data-tab=' + _after_action + ']');
if ( $target.length ) { if ( $target.length ) {
$target.parent().after( _.template(_tplTab, tab) ); $target.parent().after( _.template(_tplTab)(tab) );
if ( panel ) { if ( panel ) {
_elements = $panels || config.$layout.find('.box-panels > .panel'); _elements = $panels || config.$layout.find('.box-panels > .panel');

View file

@ -123,7 +123,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
layouts: layouts layouts: layouts
}); });

View file

@ -203,7 +203,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
styles: styles styles: styles
}); });

View file

@ -235,7 +235,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
layouts: layouts layouts: layouts
}); });
@ -268,7 +268,7 @@ define([
'<% }); %>', '<% }); %>',
'</div>', '</div>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
themes: themes themes: themes
}); });
@ -291,7 +291,7 @@ define([
'</label>', '</label>',
'</li>', '</li>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
android : Common.SharedSettings.get('android'), android : Common.SharedSettings.get('android'),
types: _arrCurrentEffectTypes types: _arrCurrentEffectTypes
}); });

View file

@ -138,7 +138,7 @@ define([
'</div>', '</div>',
'<% }); %>', '<% }); %>',
'</div>' '</div>'
].join(''), { ].join(''))({
styles: styles styles: styles
}); });

View file

@ -93,7 +93,7 @@ define([
this.handler = options.handler; this.handler = options.handler;
this.type = options.type || 'number'; this.type = options.type || 'number';
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);
}, },
@ -342,7 +342,7 @@ define([
this.api = options.api; this.api = options.api;
this.handler = options.handler; this.handler = options.handler;
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);
}, },
@ -516,7 +516,7 @@ define([
this.throughIndexes = []; this.throughIndexes = [];
this.filteredIndexes = []; this.filteredIndexes = [];
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);
}, },

View file

@ -69,7 +69,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -92,7 +92,7 @@ define([
this.formulasGroups = options.formulasGroups; this.formulasGroups = options.formulasGroups;
this.handler = options.handler; this.handler = options.handler;
_options.tpl = _.template(this.template, _options); _options.tpl = _.template(this.template)(_options);
Common.UI.Window.prototype.initialize.call(this, _options); Common.UI.Window.prototype.initialize.call(this, _options);
}, },

View file

@ -99,7 +99,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.api = this.options.api; this.api = this.options.api;
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);

View file

@ -67,7 +67,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.startvalue = this.options.startvalue; this.startvalue = this.options.startvalue;
this.maxvalue = this.options.maxvalue; this.maxvalue = this.options.maxvalue;
this.defaultUnit = this.options.defaultUnit; this.defaultUnit = this.options.defaultUnit;

View file

@ -68,7 +68,7 @@ define([
templateUserList: _.template('<ul>' + templateUserList: _.template('<ul>' +
'<% _.each(users, function(item) { %>' + '<% _.each(users, function(item) { %>' +
'<%= _.template(usertpl, {user: item, scope: scope}) %>' + '<%= _.template(usertpl)({user: item, scope: scope}) %>' +
'<% }); %>' + '<% }); %>' +
'</ul>'), '</ul>'),
@ -489,7 +489,7 @@ define([
_onAddUser: function(m, c, opts) { _onAddUser: function(m, c, opts) {
if (this.panelUsersList) { if (this.panelUsersList) {
this.panelUsersList.find('ul').append(_.template(this.tplUser, {user: m, scope: this})); this.panelUsersList.find('ul').append(_.template(this.tplUser)({user: m, scope: this}));
this.panelUsersList.scroller.update({minScrollbarLength : 40, alwaysVisibleY: true}); this.panelUsersList.scroller.update({minScrollbarLength : 40, alwaysVisibleY: true});
} }
}, },
@ -706,7 +706,7 @@ define([
label: this.labelSheetName, label: this.labelSheetName,
btns: {ok: this.okButtonText, cancel: this.cancelButtonText} btns: {ok: this.okButtonText, cancel: this.cancelButtonText}
}); });
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },
@ -823,7 +823,7 @@ define([
label: options.ismove ? this.textMoveBefore : this.textCopyBefore, label: options.ismove ? this.textMoveBefore : this.textCopyBefore,
btns: {ok: this.okButtonText, cancel: this.cancelButtonText} btns: {ok: this.okButtonText, cancel: this.cancelButtonText}
}); });
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
Common.UI.Window.prototype.initialize.call(this, this.options); Common.UI.Window.prototype.initialize.call(this, this.options);
}, },

View file

@ -72,7 +72,7 @@ define([
'</div>' '</div>'
].join(''); ].join('');
this.options.tpl = _.template(this.template, this.options); this.options.tpl = _.template(this.template)(this.options);
this.checkRangeType = Asc.c_oAscSelectionDialogType.FormatTable; this.checkRangeType = Asc.c_oAscSelectionDialogType.FormatTable;
this.selectionType = Asc.c_oAscSelectionType.RangeCells; this.selectionType = Asc.c_oAscSelectionType.RangeCells;

View file

@ -57,7 +57,7 @@ define([
}; };
_.extend(_params, args); _.extend(_params, args);
var $content = $('<div/>').append(_.template(this.template, _params)); var $content = $('<div/>').append(_.template(this.template)(_params));
// Android fix for navigation // Android fix for navigation
if (Framework7.prototype.device.android) { if (Framework7.prototype.device.android) {
@ -121,7 +121,7 @@ define([
'Logical': me.sCatLogical 'Logical': me.sCatLogical
}; };
me.layout = $('<div/>').append(_.template(me.template, { me.layout = $('<div/>').append(_.template(me.template)({
android : Common.SharedSettings.get('android'), android : Common.SharedSettings.get('android'),
phone : Common.SharedSettings.get('phone'), phone : Common.SharedSettings.get('phone'),
textGroups : me.textGroups, textGroups : me.textGroups,

View file

@ -210,7 +210,7 @@ define([
'<% }) %>'; '<% }) %>';
this.layout.find('#add-link-sheet select').html( this.layout.find('#add-link-sheet select').html(
_.template(tpl, { _.template(tpl)({
worksheets: sheets worksheets: sheets
}) })
); );
@ -219,7 +219,7 @@ define([
if ($view.length > 0) { if ($view.length > 0) {
$view.find('#add-link-sheet select').html( $view.find('#add-link-sheet select').html(
_.template(tpl, { _.template(tpl)({
worksheets: sheets worksheets: sheets
}) })
); );

View file

@ -76,8 +76,7 @@ define([
} }
mapNavigation = mapNavigation =
el = _.template(tplNavigation, el = _.template(tplNavigation)({
{
android : Common.SharedSettings.get('android'), android : Common.SharedSettings.get('android'),
phone : Common.SharedSettings.get('phone'), phone : Common.SharedSettings.get('phone'),
textBack : this.textBack, textBack : this.textBack,

View file

@ -133,7 +133,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
styles: styles, styles: styles,
styleSize: styleSize styleSize: styleSize
}); });

View file

@ -160,7 +160,7 @@ define([
'<% }); %>', '<% }); %>',
'</ul>', '</ul>',
'<% }); %>' '<% }); %>'
].join(''), { ].join(''))({
styles: styles styles: styles
}); });

93
vendor/underscore/.eslintrc vendored Normal file
View file

@ -0,0 +1,93 @@
{
"env": {
"browser": true,
"node": true,
"amd": true
},
"rules": {
"array-bracket-spacing": [2],
"block-scoped-var": 1,
"brace-style": [1, "1tbs"],
"camelcase": 2,
"comma-dangle": [2, "never"],
"comma-spacing": 2,
"computed-property-spacing": [2, "never"],
"consistent-return": 1,
"dot-notation": [2, { "allowKeywords": false }],
"eol-last": 2,
"eqeqeq": [2, "smart"],
"indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}],
"key-spacing": 1,
"linebreak-style": 2,
"max-depth": [1, 4],
"max-params": [1, 5],
"new-cap": 2,
"no-alert": 2,
"no-caller": 2,
"no-catch-shadow": 2,
"no-console": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 1,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 1,
"no-empty-character-class": 2,
"no-empty-label": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 1,
"no-extra-semi": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inner-declarations": 2,
"no-irregular-whitespace": 2,
"no-label-var": 2,
"no-lone-blocks": 2,
"no-lonely-if": 2,
"no-multi-spaces": 1,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-negated-in-lhs": 1,
"no-nested-ternary": 2,
"no-new-object": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-proto": 2,
"no-redeclare": 2,
"no-shadow": 2,
"no-spaced-func": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 2,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-expressions": 2,
"no-unused-vars": 2,
"no-use-before-define": [1, "nofunc"],
"no-with": 2,
"object-curly-spacing": [2, "never"],
"quote-props": [1, "as-needed"],
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": 2,
"space-after-keywords": [2, "always"],
"space-before-function-paren": [2, {"anonymous": "never", "named": "never"}],
"space-infix-ops": 2,
"space-return-throw-case": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"use-isnan": 2,
"valid-typeof": 2,
"wrap-iife": 2
}
}

View file

@ -1,2 +1,7 @@
raw raw
node_modules node_modules
*.log
*.idea
*.swp
nyc_output
coverage

View file

@ -1,5 +1,30 @@
language: node_js language: node_js
sudo: false
node_js: node_js:
- 0.8 - "0.8"
- "0.10"
- "0.12"
matrix:
include:
- node_js: "4.0"
env: BROWSER=true
before_install:
- npm install -g npm@2.6
- npm install -g karma-cli
before_script:
- npm install karma-sauce-launcher
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
script:
- npm test
- "[ $BROWSER == false ] || npm run test-browser"
- "[ $BROWSER == false ] || karma start karma.conf-sauce.js"
notifications: notifications:
email: false email: false
env:
global:
- NPM_CONFIG_PROGRESS="false"
- secure: bDZSBQfqr21hCayjcZ20IxrV6+XGhxQPFIfwWqEKLrF93Gu8LLVjZRxXE/mE8I8N4Z5WtDNb4ZHrm/TTzmcPa5MuHgIxEdknQCncobH8oimwc83SHwEPk6okeNKl39VlCjvvnmoe/V/KpnknuYn3Rqghtl/Uv9KLpCwskwjTtcw=
- secure: SRECgXuwcZTcD3GVxTS2bYNgRyye4vq6BLrV2PH9FyNenowsKQR2EwlC/dppc1Q8NWMgv79J/R96q9JOFh+mEH9L5dlBb2yhnGH8amVeM/ChAJHT/F8YktKM453uVpz5fR00QcCQDDUOx6Pvx374ID0OKNpWKAkQBWA9mPTsLnE=
matrix: BROWSER=false
after_success: npm run coveralls

93
vendor/underscore/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,93 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at jashkenas@gmail.com. The project team
will review and investigate all complaints, and will respond in a way that it deems
appropriate to the circumstances. The project team is obligated to maintain
confidentiality with regard to the reporter of an incident.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Moderation
Edits of another user's comment must be clearly marked with "**edit**", the
moderator's username, and a timestamp for each occurrence. The only acceptable
reasons for editing another user's comment are:
1. to edit out violations of Our Pledge. These edits must include a rationale.
2. to direct future readers to a relevant point later in the conversation
(usually the resolution). These edits must be append-only.
Deletion of another user's comment is only acceptable when the comment includes
no original value, such as "+1", ":+1:", or "me too".
## Self-Moderation
Edits of your own comment after someone has responded must be append-only and
clearly marked with "**edit**". Typographical and formatting fixes to your own
comment which do not affect its meaning are exempt from this requirement.
Deletion of your own comment is only acceptable before any later comments have
been posted.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,7 +1,11 @@
## How to contribute to Underscore.js ## How to contribute to Underscore.js
* This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.
* Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/underscore/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one. * Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/underscore/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
* If you're proposing a new feature, make sure it isn't already implemented in [Underscore-Contrib](https://github.com/documentcloud/underscore-contrib).
* Before sending a pull request for a feature, be sure to have [tests](http://underscorejs.org/test/). * Before sending a pull request for a feature, be sure to have [tests](http://underscorejs.org/test/).
* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/underscore/blob/master/underscore.js). * Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/underscore/blob/master/underscore.js).

View file

@ -1,4 +1,4 @@
Copyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Copyright (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative
Reporters & Editors Reporters & Editors
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person

View file

@ -12,9 +12,15 @@ Underscore.js is a utility-belt library for JavaScript that provides
support for the usual functional suspects (each, map, reduce, filter...) support for the usual functional suspects (each, map, reduce, filter...)
without extending any core JavaScript objects. without extending any core JavaScript objects.
This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.
For Docs, License, Tests, and pre-packed downloads, see: For Docs, License, Tests, and pre-packed downloads, see:
http://underscorejs.org http://underscorejs.org
For support and questions, please use
[the gitter channel](https://gitter.im/jashkenas/underscore)
or [stackoverflow](http://stackoverflow.com/search?q=underscore.js)
Underscore is an open-sourced component of DocumentCloud: Underscore is an open-sourced component of DocumentCloud:
https://github.com/documentcloud https://github.com/documentcloud

6
vendor/underscore/bower.json vendored Normal file
View file

@ -0,0 +1,6 @@
{
"name": "underscore",
"main": "underscore.js",
"keywords": ["util", "functional", "server", "client", "browser"],
"ignore" : ["docs", "test", "*.yml", "CNAME", "index.html", "favicon.ico", "CONTRIBUTING.md", ".*", "package.json", "karma.*"]
}

5
vendor/underscore/docs/.eslintrc vendored Normal file
View file

@ -0,0 +1,5 @@
{
"globals": {
"_": true
}
}

View file

@ -21,11 +21,11 @@
} }
@font-face { @font-face {
font-family: 'novecento-bold'; font-family: 'roboto-black';
src: url('public/fonts/novecento-bold.eot'); src: url('public/fonts/roboto-black.eot');
src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'),
url('public/fonts/novecento-bold.woff') format('woff'), url('public/fonts/roboto-black.woff') format('woff'),
url('public/fonts/novecento-bold.ttf') format('truetype'); url('public/fonts/roboto-black.ttf') format('truetype');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -51,15 +51,23 @@ b, strong {
font-family: "aller-bold"; font-family: "aller-bold";
} }
p, ul, ol { p {
margin: 15px 0 0px; margin: 15px 0 0px;
} }
.annotation ul, .annotation ol {
margin: 25px 0;
}
.annotation ul li, .annotation ol li {
font-size: 14px;
line-height: 18px;
margin: 10px 0;
}
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
color: #112233; color: #112233;
line-height: 1em; line-height: 1em;
font-weight: normal; font-weight: normal;
font-family: "novecento-bold"; font-family: "roboto-black";
text-transform: uppercase; text-transform: uppercase;
margin: 30px 0 15px 0; margin: 30px 0 15px 0;
} }
@ -67,10 +75,13 @@ h1, h2, h3, h4, h5, h6 {
h1 { h1 {
margin-top: 40px; margin-top: 40px;
} }
h2 {
font-size: 1.26em;
}
hr { hr {
border: 0; border: 0;
background: 1px solid #ddd; background: 1px #ddd;
height: 1px; height: 1px;
margin: 20px 0; margin: 20px 0;
} }
@ -172,9 +183,18 @@ ul.sections > li > div {
display: block; display: block;
} }
#jump_page_wrapper{
position: fixed;
right: 0;
top: 0;
bottom: 0;
}
#jump_page { #jump_page {
padding: 5px 0 3px; padding: 5px 0 3px;
margin: 0 0 25px 25px; margin: 0 0 25px 25px;
max-height: 100%;
overflow: auto;
} }
#jump_page .source { #jump_page .source {
@ -205,7 +225,6 @@ ul.sections > li > div {
} }
ul.sections > li > div.content { ul.sections > li > div.content {
background: #f5f5ff;
overflow-x:auto; overflow-x:auto;
-webkit-box-shadow: inset 0 0 5px #e5e5ee; -webkit-box-shadow: inset 0 0 5px #e5e5ee;
box-shadow: inset 0 0 5px #e5e5ee; box-shadow: inset 0 0 5px #e5e5ee;
@ -306,7 +325,6 @@ ul.sections > li > div {
ul.sections > li > div.content { ul.sections > li > div.content {
padding: 13px; padding: 13px;
vertical-align: top; vertical-align: top;
background: #f5f5ff;
border: none; border: none;
-webkit-box-shadow: none; -webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
@ -376,125 +394,125 @@ pre code {
background: #f8f8ff background: #f8f8ff
} }
pre .comment, pre .hljs-comment,
pre .template_comment, pre .hljs-template_comment,
pre .diff .header, pre .hljs-diff .hljs-header,
pre .javadoc { pre .hljs-javadoc {
color: #408080; color: #408080;
font-style: italic font-style: italic
} }
pre .keyword, pre .hljs-keyword,
pre .assignment, pre .hljs-assignment,
pre .literal, pre .hljs-literal,
pre .css .rule .keyword, pre .hljs-css .hljs-rule .hljs-keyword,
pre .winutils, pre .hljs-winutils,
pre .javascript .title, pre .hljs-javascript .hljs-title,
pre .lisp .title, pre .hljs-lisp .hljs-title,
pre .subst { pre .hljs-subst {
color: #954121; color: #954121;
/*font-weight: bold*/ /*font-weight: bold*/
} }
pre .number, pre .hljs-number,
pre .hexcolor { pre .hljs-hexcolor {
color: #40a070 color: #40a070
} }
pre .string, pre .hljs-string,
pre .tag .value, pre .hljs-tag .hljs-value,
pre .phpdoc, pre .hljs-phpdoc,
pre .tex .formula { pre .hljs-tex .hljs-formula {
color: #219161; color: #219161;
} }
pre .title, pre .hljs-title,
pre .id { pre .hljs-id {
color: #19469D; color: #19469D;
} }
pre .params { pre .hljs-params {
color: #00F; color: #00F;
} }
pre .javascript .title, pre .hljs-javascript .hljs-title,
pre .lisp .title, pre .hljs-lisp .hljs-title,
pre .subst { pre .hljs-subst {
font-weight: normal font-weight: normal
} }
pre .class .title, pre .hljs-class .hljs-title,
pre .haskell .label, pre .hljs-haskell .hljs-label,
pre .tex .command { pre .hljs-tex .hljs-command {
color: #458; color: #458;
font-weight: bold font-weight: bold
} }
pre .tag, pre .hljs-tag,
pre .tag .title, pre .hljs-tag .hljs-title,
pre .rules .property, pre .hljs-rules .hljs-property,
pre .django .tag .keyword { pre .hljs-django .hljs-tag .hljs-keyword {
color: #000080; color: #000080;
font-weight: normal font-weight: normal
} }
pre .attribute, pre .hljs-attribute,
pre .variable, pre .hljs-variable,
pre .instancevar, pre .hljs-instancevar,
pre .lisp .body { pre .hljs-lisp .hljs-body {
color: #008080 color: #008080
} }
pre .regexp { pre .hljs-regexp {
color: #B68 color: #B68
} }
pre .class { pre .hljs-class {
color: #458; color: #458;
font-weight: bold font-weight: bold
} }
pre .symbol, pre .hljs-symbol,
pre .ruby .symbol .string, pre .hljs-ruby .hljs-symbol .hljs-string,
pre .ruby .symbol .keyword, pre .hljs-ruby .hljs-symbol .hljs-keyword,
pre .ruby .symbol .keymethods, pre .hljs-ruby .hljs-symbol .hljs-keymethods,
pre .lisp .keyword, pre .hljs-lisp .hljs-keyword,
pre .tex .special, pre .hljs-tex .hljs-special,
pre .input_number { pre .hljs-input_number {
color: #990073 color: #990073
} }
pre .builtin, pre .hljs-builtin,
pre .constructor, pre .hljs-constructor,
pre .built_in, pre .hljs-built_in,
pre .lisp .title { pre .hljs-lisp .hljs-title {
color: #0086b3 color: #0086b3
} }
pre .preprocessor, pre .hljs-preprocessor,
pre .pi, pre .hljs-pi,
pre .doctype, pre .hljs-doctype,
pre .shebang, pre .hljs-shebang,
pre .cdata { pre .hljs-cdata {
color: #999; color: #999;
font-weight: bold font-weight: bold
} }
pre .deletion { pre .hljs-deletion {
background: #fdd background: #fdd
} }
pre .addition { pre .hljs-addition {
background: #dfd background: #dfd
} }
pre .diff .change { pre .hljs-diff .hljs-change {
background: #0086b3 background: #0086b3
} }
pre .chunk { pre .hljs-chunk {
color: #aaa color: #aaa
} }
pre .tex .formula { pre .hljs-tex .hljs-formula {
opacity: 0.5; opacity: 0.5;
} }

63
vendor/underscore/docs/main.js vendored Normal file
View file

@ -0,0 +1,63 @@
(function() {
var functions = document.querySelectorAll('[data-name]');
var sections = document.querySelectorAll('.searchable_section');
var searchInput = document.getElementById('function_filter');
function searchValue() {
return searchInput.value.trim().replace(/^_\.?/, '');
}
function strIn(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
return b.indexOf(a) >= 0;
}
function doesMatch(element) {
var name = element.getAttribute('data-name');
var aliases = element.getAttribute('data-aliases') || '';
var value = searchValue();
return strIn(value, name) || strIn(value, aliases);
}
function filterElement(element) {
element.style.display = doesMatch(element) ? '' : 'none';
}
function filterToc() {
_.each(functions, filterElement);
var emptySearch = searchValue() === '';
// Hide the titles of empty sections
_.each(sections, function(section) {
var sectionFunctions = section.querySelectorAll('[data-name]');
var showSection = emptySearch || _.some(sectionFunctions, doesMatch);
section.style.display = showSection ? '' : 'none';
});
}
function gotoFirst() {
var firstFunction = _.find(functions, doesMatch);
if (firstFunction) {
window.location.hash = firstFunction.getAttribute('data-name');
searchInput.focus();
}
}
searchInput.addEventListener('input', filterToc, false);
// Press "Enter" to jump to the first matching function
searchInput.addEventListener('keypress', function(e) {
if (e.which === 13) {
gotoFirst();
}
});
// Press "/" to search
document.body.addEventListener('keyup', function(event) {
if (191 === event.which) {
searchInput.focus();
}
});
}());

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

93
vendor/underscore/karma.conf-sauce.js vendored Normal file
View file

@ -0,0 +1,93 @@
var _ = require('./');
// Browsers to run on Sauce Labs platforms
var sauceBrowsers = _.reduce([
['firefox', '35'],
['firefox', '30'],
['firefox', '21'],
['firefox', '11'],
['firefox', '4'],
['chrome', '40'],
['chrome', '39'],
['chrome', '31'],
['chrome', '26'],
['microsoftedge', '20.10240', 'Windows 10'],
['internet explorer', '11', 'Windows 10'],
['internet explorer', '10', 'Windows 8'],
['internet explorer', '9', 'Windows 7'],
// Currently disabled due to karma-sauce issues
// ['internet explorer', '8'],
// ['internet explorer', '7'],
// ['internet explorer', '6'],
['opera', '12'],
['opera', '11'],
['android', '5'],
['android', '4.4'],
['android', '4.3'],
['android', '4.0'],
['safari', '8.0', 'OS X 10.10'],
['safari', '7'],
['safari', '6'],
['safari', '5']
], function(memo, platform) {
// internet explorer -> ie
var label = platform[0].split(' ');
if (label.length > 1) {
label = _.invoke(label, 'charAt', 0)
}
label = (label.join("") + '_v' + platform[1]).replace(' ', '_').toUpperCase();
memo[label] = _.pick({
'base': 'SauceLabs',
'browserName': platform[0],
'version': platform[1],
'platform': platform[2]
}, Boolean);
return memo;
}, {});
module.exports = function(config) {
if ( !process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY ) {
console.log('Sauce environments not set --- Skipping');
return process.exit(0);
}
config.set({
basePath: '',
frameworks: ['qunit'],
singleRun: true,
// list of files / patterns to load in the browser
files: [
'test/vendor/qunit-extras.js',
'test/qunit-setup.js',
'underscore.js',
'test/*.js'
],
// Number of sauce tests to start in parallel
concurrency: 9,
// test results reporter to use
reporters: ['dots', 'saucelabs'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
sauceLabs: {
build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
startConnect: true,
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
},
captureTimeout: 120000,
customLaunchers: sauceBrowsers,
// Browsers to launch, commented out to prevent karma from starting
// too many concurrent browsers and timing sauce out.
browsers: _.keys(sauceBrowsers)
});
};

35
vendor/underscore/karma.conf.js vendored Normal file
View file

@ -0,0 +1,35 @@
// Note some browser launchers should be installed before using karma start.
// For example:
// $ npm install karma-firefox-launcher
// $ karma start --browser=Firefox
// See http://karma-runner.github.io/0.8/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['qunit'],
logLevel: config.LOG_INFO,
port: 9876,
// list of files / patterns to load in the browser
files: [
'test/vendor/qunit-extras.js',
'test/qunit-setup.js',
'underscore.js',
'test/*.js'
],
// Test results reporter to use
// https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true
});
};

View file

@ -1,23 +1,50 @@
{ {
"name" : "underscore", "name": "underscore",
"description" : "JavaScript's functional programming helper library.", "description": "JavaScript's functional programming helper library.",
"homepage" : "http://underscorejs.org", "homepage": "http://underscorejs.org",
"keywords" : ["util", "functional", "server", "client", "browser"], "keywords": [
"author" : "Jeremy Ashkenas <jeremy@documentcloud.org>", "util",
"repository" : {"type": "git", "url": "git://github.com/jashkenas/underscore.git"}, "functional",
"main" : "underscore.js", "server",
"version" : "1.5.2", "client",
"browser"
],
"author": "Jeremy Ashkenas <jeremy@documentcloud.org>",
"repository": {
"type": "git",
"url": "git://github.com/jashkenas/underscore.git"
},
"main": "underscore.js",
"version": "1.8.3",
"devDependencies": { "devDependencies": {
"phantomjs": "1.9.0-1" "coveralls": "^2.11.2",
"docco": "*",
"eslint": "1.10.x",
"gzip-size-cli": "^1.0.0",
"karma": "^0.13.13",
"karma-qunit": "~0.1.4",
"nyc": "^2.1.3",
"pretty-bytes-cli": "^1.0.0",
"qunit-cli": "~0.2.0",
"qunitjs": "^1.18.0",
"uglify-js": "2.4.x"
}, },
"scripts": { "scripts": {
"test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true" "test": "npm run test-node && npm run lint",
"coverage": "nyc npm run test-node && nyc report",
"coveralls": "nyc npm run test-node && nyc report --reporter=text-lcov | coveralls",
"lint": "eslint underscore.js test/*.js",
"test-node": "qunit-cli test/*.js",
"test-browser": "npm i karma-phantomjs-launcher && karma start",
"minify": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m",
"build": "npm run minify -- --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js",
"doc": "docco underscore.js",
"weight": "npm run minify | gzip-size | pretty-bytes"
}, },
"licenses": [ "license": "MIT",
{ "files": [
"type": "MIT", "underscore.js",
"url": "https://raw.github.com/jashkenas/underscore/master/LICENSE" "underscore-min.js",
} "underscore-min.map"
], ]
"files" : ["underscore.js", "underscore-min.js", "LICENSE"]
} }

13
vendor/underscore/test/.eslintrc vendored Normal file
View file

@ -0,0 +1,13 @@
{
"env": {
"browser": true
},
"globals": {
"QUnit": false
},
"rules": {
"brace-style": 0,
"no-new-wrappers": 0,
"no-extend-native": 0
}
}

View file

@ -1,224 +1,563 @@
$(document).ready(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module("Arrays"); QUnit.module('Arrays');
test("first", function() { QUnit.test('first', function(assert) {
equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); assert.strictEqual(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); assert.strictEqual(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements');
var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
equal(result, 4, 'works on an arguments object.'); var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
result = _.map([[1,2,3],[1,2,3]], _.first); assert.strictEqual(result, 4, 'works on an arguments object');
equal(result.join(','), '1,1', 'works well with _.map'); result = _.map([[1, 2, 3], [1, 2, 3]], _.first);
result = (function() { return _.take([1,2,3], 2); })(); assert.deepEqual(result, [1, 1], 'works well with _.map');
equal(result.join(','), '1,2', 'aliased as take'); assert.strictEqual(_.first(null), void 0, 'returns undefined when called on null');
equal(_.first(null), undefined, 'handles nulls'); Array.prototype[0] = 'boo';
assert.strictEqual(_.first([]), void 0, 'return undefined when called on a empty array');
delete Array.prototype[0];
}); });
test("rest", function() { QUnit.test('head', function(assert) {
assert.strictEqual(_.head, _.first, 'is an alias for first');
});
QUnit.test('take', function(assert) {
assert.strictEqual(_.take, _.first, 'is an alias for first');
});
QUnit.test('rest', function(assert) {
var numbers = [1, 2, 3, 4]; var numbers = [1, 2, 3, 4];
equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element');
equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0');
equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns elements starting at the given index');
var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); assert.deepEqual(result, [2, 3, 4], 'works on an arguments object');
result = _.map([[1,2,3],[1,2,3]], _.rest); result = _.map([[1, 2, 3], [1, 2, 3]], _.rest);
equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map');
result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4);
equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object');
}); });
test("initial", function() { QUnit.test('tail', function(assert) {
equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); assert.strictEqual(_.tail, _.rest, 'is an alias for rest');
equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index');
var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4);
equal(result.join(", "), "1, 2, 3", 'initial works on arguments object');
result = _.map([[1,2,3],[1,2,3]], _.initial);
equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map');
}); });
test("last", function() { QUnit.test('drop', function(assert) {
equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); assert.strictEqual(_.drop, _.rest, 'is an alias for rest');
equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last');
equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last');
equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last');
var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4);
equal(result, 4, 'works on an arguments object');
result = _.map([[1,2,3],[1,2,3]], _.last);
equal(result.join(','), '3,3', 'works well with _.map');
equal(_.last(null), undefined, 'handles nulls');
}); });
test("compact", function() { QUnit.test('initial', function(assert) {
equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element');
var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3); assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements');
equal(result, 3, 'works on an arguments object'); assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length');
var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4));
assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.initial);
assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map');
}); });
test("flatten", function() { QUnit.test('last', function(assert) {
assert.strictEqual(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
assert.strictEqual(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"');
assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements');
assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
assert.strictEqual(result, 4, 'works on an arguments object');
result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
assert.deepEqual(result, [3, 3], 'works well with _.map');
assert.strictEqual(_.last(null), void 0, 'returns undefined when called on null');
var arr = [];
arr[-1] = 'boo';
assert.strictEqual(_.last(arr), void 0, 'return undefined when called on a empty array');
});
QUnit.test('compact', function(assert) {
assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values');
var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3));
assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
result = _.map([[1, false, false], [false, false, 3]], _.compact);
assert.deepEqual(result, [[1], [3]], 'works well with _.map');
});
QUnit.test('flatten', function(assert) {
assert.deepEqual(_.flatten(null), [], 'supports null');
assert.deepEqual(_.flatten(void 0), [], 'supports undefined');
assert.deepEqual(_.flatten([[], [[]], []]), [], 'supports empty arrays');
assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'can shallowly flatten empty arrays');
var list = [1, [2], [3, [[[4]]]]]; var list = [1, [2], [3, [[[4]]]]];
deepEqual(_.flatten(list), [1,2,3,4], 'can flatten nested arrays'); assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
deepEqual(_.flatten(list, true), [1,2,3,[[[4]]]], 'can shallowly flatten nested arrays'); assert.deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]]));
deepEqual(result, [1,2,3,4], 'works on an arguments object'); assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
list = [[1], [2], [3], [[4]]]; list = [[1], [2], [3], [[4]]];
deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays');
assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays');
assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays');
assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode');
var x = _.range(100000);
for (var i = 0; i < 1000; i++) x = [x];
assert.deepEqual(_.flatten(x), _.range(100000), 'can handle very deep arrays');
assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode');
}); });
test("without", function() { QUnit.test('without', function(assert) {
var list = [1, 2, 1, 0, 3, 1, 4]; var list = [1, 2, 1, 0, 3, 1, 4];
equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values');
var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4));
equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); assert.deepEqual(result, [2, 3, 4], 'works on an arguments object');
list = [{one : 1}, {two : 2}]; list = [{one: 1}, {two: 2}];
ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); assert.deepEqual(_.without(list, {one: 1}), list, 'compares objects by reference (value case)');
ok(_.without(list, list[0]).length == 1, 'ditto.'); assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)');
}); });
test("uniq", function() { QUnit.test('sortedIndex', function(assert) {
var numbers = [10, 20, 30, 40, 50];
var indexFor35 = _.sortedIndex(numbers, 35);
assert.strictEqual(indexFor35, 3, 'finds the index at which a value should be inserted to retain order');
var indexFor30 = _.sortedIndex(numbers, 30);
assert.strictEqual(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order');
var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
var iterator = function(obj){ return obj.x; };
assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2, 'uses the result of `iterator` for order comparisons');
assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3, 'when `iterator` is a string, uses that key for order comparisons');
var context = {1: 2, 2: 3, 3: 4};
iterator = function(obj){ return this[obj]; };
assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1, 'can execute its iterator in the given context');
var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287,
1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647];
var largeArray = Array(Math.pow(2, 32) - 1);
var length = values.length;
// Sparsely populate `array`
while (length--) {
largeArray[values[length]] = values[length];
}
assert.strictEqual(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes');
});
QUnit.test('uniq', function(assert) {
var list = [1, 2, 1, 3, 1, 4]; var list = [1, 2, 1, 3, 1, 4];
equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array');
list = [1, 1, 1, 2, 2, 3]; list = [1, 1, 1, 2, 2, 3];
equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster');
list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; list = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}, {name: 'Curly'}];
var iterator = function(value) { return value.name; }; var expected = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}];
equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); var iterator = function(stooge) { return stooge.name; };
assert.deepEqual(_.uniq(list, false, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (unsorted case)');
assert.deepEqual(_.uniq(list, iterator), expected, '`sorted` argument defaults to false when omitted');
assert.deepEqual(_.uniq(list, 'name'), expected, 'when `iterator` is a string, uses that key for comparisons (unsorted case)');
equal(_.map(_.uniq(list, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); list = [{score: 8}, {score: 10}, {score: 10}];
expected = [{score: 8}, {score: 10}];
iterator = function(item) { return item.score; };
assert.deepEqual(_.uniq(list, true, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (sorted case)');
assert.deepEqual(_.uniq(list, true, 'score'), expected, 'when `iterator` is a string, uses that key for comparisons (sorted case)');
iterator = function(value) { return value +1; }; assert.deepEqual(_.uniq([{0: 1}, {0: 1}, {0: 1}, {0: 2}], 0), [{0: 1}, {0: 2}], 'can use falsy pluck like iterator');
list = [1, 2, 2, 3, 4, 4];
equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4));
assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
var a = {}, b = {}, c = {};
assert.deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered');
assert.deepEqual(_.uniq(null), [], 'returns an empty array when `array` is not iterable');
var context = {};
list = [3];
_.uniq(list, function(value, index, array) {
assert.strictEqual(this, context, 'executes its iterator in the given context');
assert.strictEqual(value, 3, 'passes its iterator the value');
assert.strictEqual(index, 0, 'passes its iterator the index');
assert.strictEqual(array, list, 'passes its iterator the entire array');
}, context);
var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4);
equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object');
}); });
test("intersection", function() { QUnit.test('unique', function(assert) {
assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq');
});
QUnit.test('intersection', function(assert) {
var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can find the set intersection of two arrays');
equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
equal(result.join(''), 'moe', 'works on an arguments object'); assert.deepEqual(result, ['moe'], 'works on an arguments object');
var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry']; var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry'];
equal(_.intersection(theSixStooges, leaders).join(''), 'moe', 'returns a duplicate-free array'); assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
result = _.intersection([2, 4, 3, 1], [1, 2, 3]);
assert.deepEqual(result, [2, 3, 1], 'preserves the order of the first array');
result = _.intersection(null, [1, 2, 3]);
assert.deepEqual(result, [], 'returns an empty array when passed null as the first argument');
result = _.intersection([1, 2, 3], null);
assert.deepEqual(result, [], 'returns an empty array when passed null as an argument beyond the first');
}); });
test("union", function() { QUnit.test('union', function(assert) {
var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);
equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); assert.deepEqual(result, [1, 2, 3, 30, 40], 'can find the union of a list of arrays');
result = _([1, 2, 3]).union([2, 30, 1], [1, 40]);
assert.deepEqual(result, [1, 2, 3, 30, 40], 'can perform an OO-style union');
result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]);
equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'can find the union of a list of nested arrays');
var args = null; result = _.union([10, 20], [1, 30, 10], [0, 40]);
(function(){ args = arguments; })(1, 2, 3); assert.deepEqual(result, [10, 20, 1, 30, 0, 40], 'orders values by their first encounter');
result = _.union(args, [2, 30, 1], [1, 40]);
equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays');
result = _.union(null, [1, 2, 3]); result = (function(){ return _.union(arguments, [2, 30, 1], [1, 40]); }(1, 2, 3));
deepEqual(result, [null, 1, 2, 3]); assert.deepEqual(result, [1, 2, 3, 30, 40], 'works on an arguments object');
assert.deepEqual(_.union([1, 2, 3], 4), [1, 2, 3], 'restricts the union to arrays only');
}); });
test("difference", function() { QUnit.test('difference', function(assert) {
var result = _.difference([1, 2, 3], [2, 30, 40]); var result = _.difference([1, 2, 3], [2, 30, 40]);
equal(result.join(' '), '1 3', 'takes the difference of two arrays'); assert.deepEqual(result, [1, 3], 'can find the difference of two arrays');
result = _([1, 2, 3]).difference([2, 30, 40]);
assert.deepEqual(result, [1, 3], 'can perform an OO-style difference');
result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]);
equal(result.join(' '), '3 4', 'takes the difference of three arrays'); assert.deepEqual(result, [3, 4], 'can find the difference of three arrays');
result = _.difference([8, 9, 3, 1], [3, 8]);
assert.deepEqual(result, [9, 1], 'preserves the order of the first array');
result = (function(){ return _.difference(arguments, [2, 30, 40]); }(1, 2, 3));
assert.deepEqual(result, [1, 3], 'works on an arguments object');
result = _.difference([1, 2, 3], 1);
assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only');
}); });
test('zip', function() { QUnit.test('zip', function(assert) {
var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true];
var stooges = _.zip(names, ages, leaders); assert.deepEqual(_.zip(names, ages, leaders), [
equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); ['moe', 30, true],
['larry', 40, void 0],
['curly', 50, void 0]
], 'zipped together arrays of different lengths');
stooges = _.zip(['moe',30, 'stooge 1'],['larry',40, 'stooge 2'],['curly',50, 'stooge 3']); var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']);
deepEqual(stooges, [['moe','larry','curly'],[30,40,50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs');
// In the case of difference lengths of the tuples undefineds // In the case of different lengths of the tuples, undefined values
// should be used as placeholder // should be used as placeholder
stooges = _.zip(['moe',30],['larry',40],['curly',50, 'extra data']); stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
deepEqual(stooges, [['moe','larry','curly'],[30,40,50], [undefined, undefined, 'extra data']], 'zipped pairs with empties'); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties');
var empty = _.zip([]); var empty = _.zip([]);
deepEqual(empty, [], 'unzipped empty'); assert.deepEqual(empty, [], 'unzipped empty');
assert.deepEqual(_.zip(null), [], 'handles null');
assert.deepEqual(_.zip(), [], '_.zip() returns []');
}); });
test('object', function() { QUnit.test('unzip', function(assert) {
assert.deepEqual(_.unzip(null), [], 'handles null');
assert.deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]);
// complements zip
var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
assert.deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]);
zipped = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
assert.deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array');
});
QUnit.test('object', function(assert) {
var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
var shouldBe = {moe: 30, larry: 40, curly: 50}; var shouldBe = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object');
result = _.object([['one', 1], ['two', 2], ['three', 3]]); result = _.object([['one', 1], ['two', 2], ['three', 3]]);
shouldBe = {one: 1, two: 2, three: 3}; shouldBe = {one: 1, two: 2, three: 3};
ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object'); assert.deepEqual(result, shouldBe, 'an array of pairs zipped together into an object');
var stooges = {moe: 30, larry: 40, curly: 50}; var stooges = {moe: 30, larry: 40, curly: 50};
ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object'); assert.deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object');
ok(_.isEqual(_.object(null), {}), 'handles nulls'); assert.deepEqual(_.object(null), {}, 'handles nulls');
}); });
test("indexOf", function() { QUnit.test('indexOf', function(assert) {
var numbers = [1, 2, 3]; var numbers = [1, 2, 3];
numbers.indexOf = null; assert.strictEqual(_.indexOf(numbers, 2), 1, 'can compute indexOf');
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); assert.strictEqual(result, 1, 'works on an arguments object');
equal(result, 1, 'works on an arguments object');
equal(_.indexOf(null, 2), -1, 'handles nulls properly'); _.each([null, void 0, [], false], function(val) {
var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
assert.strictEqual(_.indexOf(val, 2), -1, msg);
assert.strictEqual(_.indexOf(val, 2, -1), -1, msg);
assert.strictEqual(_.indexOf(val, 2, -20), -1, msg);
assert.strictEqual(_.indexOf(val, 2, 15), -1, msg);
});
var num = 35; var num = 35;
numbers = [10, 20, 30, 40, 50]; numbers = [10, 20, 30, 40, 50];
var index = _.indexOf(numbers, num, true); var index = _.indexOf(numbers, num, true);
equal(index, -1, '35 is not in the list'); assert.strictEqual(index, -1, '35 is not in the list');
numbers = [10, 20, 30, 40, 50]; num = 40; numbers = [10, 20, 30, 40, 50]; num = 40;
index = _.indexOf(numbers, num, true); index = _.indexOf(numbers, num, true);
equal(index, 3, '40 is in the list'); assert.strictEqual(index, 3, '40 is in the list');
numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
index = _.indexOf(numbers, num, true); assert.strictEqual(_.indexOf(numbers, num, true), 1, '40 is in the list');
equal(index, 1, '40 is in the list'); assert.strictEqual(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
assert.strictEqual(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t use binary search');
assert.ok(_.every(['1', [], {}, null], function() {
return _.indexOf(numbers, num, {}) === 1;
}), 'non-nums as fromIndex make indexOf assume sorted');
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
index = _.indexOf(numbers, 2, 5); index = _.indexOf(numbers, 2, 5);
equal(index, 7, 'supports the fromIndex argument'); assert.strictEqual(index, 7, 'supports the fromIndex argument');
index = _.indexOf([,,, 0], void 0);
assert.strictEqual(index, 0, 'treats sparse arrays as if they were dense');
var array = [1, 2, 3, 1, 2, 3];
assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
assert.strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index');
assert.strictEqual(_.indexOf(array, 2, -3), 4);
_.each([-6, -8, -Infinity], function(fromIndex) {
assert.strictEqual(_.indexOf(array, 1, fromIndex), 0);
});
assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
index = _.indexOf([], void 0, true);
assert.strictEqual(index, -1, 'empty array with truthy `isSorted` returns -1');
}); });
test("lastIndexOf", function() { QUnit.test('indexOf with NaN', function(assert) {
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN');
assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result');
assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result');
(function() {
assert.strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
});
QUnit.test('indexOf with +- 0', function(assert) {
_.each([-0, +0], function(val) {
assert.strictEqual(_.indexOf([1, 2, val, val], val), 2);
assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2);
});
});
QUnit.test('lastIndexOf', function(assert) {
var numbers = [1, 0, 1]; var numbers = [1, 0, 1];
equal(_.lastIndexOf(numbers, 1), 2); var falsy = [void 0, '', 0, false, NaN, null, void 0];
assert.strictEqual(_.lastIndexOf(numbers, 1), 2);
numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
numbers.lastIndexOf = null; numbers.lastIndexOf = null;
equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); assert.strictEqual(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); assert.strictEqual(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0));
equal(result, 5, 'works on an arguments object'); assert.strictEqual(result, 5, 'works on an arguments object');
equal(_.lastIndexOf(null, 2), -1, 'handles nulls properly');
_.each([null, void 0, [], false], function(val) {
var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
assert.strictEqual(_.lastIndexOf(val, 2), -1, msg);
assert.strictEqual(_.lastIndexOf(val, 2, -1), -1, msg);
assert.strictEqual(_.lastIndexOf(val, 2, -20), -1, msg);
assert.strictEqual(_.lastIndexOf(val, 2, 15), -1, msg);
});
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
var index = _.lastIndexOf(numbers, 2, 2); var index = _.lastIndexOf(numbers, 2, 2);
equal(index, 1, 'supports the fromIndex argument'); assert.strictEqual(index, 1, 'supports the fromIndex argument');
var array = [1, 2, 3, 1, 2, 3];
assert.strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx');
assert.strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value');
assert.strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value');
assert.strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
_.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
assert.strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1);
assert.strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
assert.strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
});
var expected = _.map(falsy, function(value) {
return typeof value == 'number' ? -1 : 5;
});
var actual = _.map(falsy, function(fromIndex) {
return _.lastIndexOf(array, 3, fromIndex);
});
assert.deepEqual(actual, expected, 'should treat falsy `fromIndex` values, except `0` and `NaN`, as `array.length`');
assert.strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`');
assert.strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`');
assert.strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`');
assert.strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
assert.deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
return _.lastIndexOf(array, 1, fromIndex);
}), [0, -1, -1]);
}); });
test("range", function() { QUnit.test('lastIndexOf with NaN', function(assert) {
equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN');
equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
equal(_.range(8, 5).join(''), '', 'range with two arguments a &amp; b, b&lt;a generates an empty array'); assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result');
equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c'); assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result');
equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); (function() {
equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); assert.strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN');
}(1, 2, NaN, NaN));
}); });
}); QUnit.test('lastIndexOf with +- 0', function(assert) {
_.each([-0, +0], function(val) {
assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3);
assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3);
assert.strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1);
});
});
QUnit.test('findIndex', function(assert) {
var objects = [
{a: 0, b: 0},
{a: 1, b: 1},
{a: 2, b: 2},
{a: 0, b: 0}
];
assert.strictEqual(_.findIndex(objects, function(obj) {
return obj.a === 0;
}), 0);
assert.strictEqual(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
assert.strictEqual(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
assert.strictEqual(_.findIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
assert.strictEqual(_.findIndex(null, _.noop), -1);
assert.strictEqual(_.findIndex(objects, function(a) {
return a.foo === null;
}), -1);
_.findIndex([{a: 1}], function(a, key, obj) {
assert.strictEqual(key, 0);
assert.deepEqual(obj, [{a: 1}]);
assert.strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {a: 2, b: 2};
assert.strictEqual(_.findIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
var array = [1, 2, 3, 4];
array.match = 55;
assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
QUnit.test('findLastIndex', function(assert) {
var objects = [
{a: 0, b: 0},
{a: 1, b: 1},
{a: 2, b: 2},
{a: 0, b: 0}
];
assert.strictEqual(_.findLastIndex(objects, function(obj) {
return obj.a === 0;
}), 3);
assert.strictEqual(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 4;
}), 2);
assert.strictEqual(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
assert.strictEqual(_.findLastIndex(objects, function(obj) {
return obj.b * obj.a === 5;
}), -1);
assert.strictEqual(_.findLastIndex(null, _.noop), -1);
assert.strictEqual(_.findLastIndex(objects, function(a) {
return a.foo === null;
}), -1);
_.findLastIndex([{a: 1}], function(a, key, obj) {
assert.strictEqual(key, 0);
assert.deepEqual(obj, [{a: 1}]);
assert.strictEqual(this, objects, 'called with context');
}, objects);
var sparse = [];
sparse[20] = {a: 2, b: 2};
assert.strictEqual(_.findLastIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
var array = [1, 2, 3, 4];
array.match = 55;
assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
});
QUnit.test('range', function(assert) {
assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array');
assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');
assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0');
assert.deepEqual(_.range(8, 5), [8, 7, 6], 'negative range generates descending array');
assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array');
});
QUnit.test('chunk', function(assert) {
assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
assert.deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
assert.deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
assert.deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
assert.deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
assert.deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
});
}());

View file

@ -1,13 +1,14 @@
$(document).ready(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module("Chaining"); QUnit.module('Chaining');
test("map/flatten/reduce", function() { QUnit.test('map/flatten/reduce', function(assert) {
var lyrics = [ var lyrics = [
"I'm a lumberjack and I'm okay", 'I\'m a lumberjack and I\'m okay',
"I sleep all night and I work all day", 'I sleep all night and I work all day',
"He's a lumberjack and he's okay", 'He\'s a lumberjack and he\'s okay',
"He sleeps all night and he works all day" 'He sleeps all night and he works all day'
]; ];
var counts = _(lyrics).chain() var counts = _(lyrics).chain()
.map(function(line) { return line.split(''); }) .map(function(line) { return line.split(''); })
@ -16,12 +17,14 @@ $(document).ready(function() {
hash[l] = hash[l] || 0; hash[l] = hash[l] || 0;
hash[l]++; hash[l]++;
return hash; return hash;
}, {}).value(); }, {})
ok(counts.a == 16 && counts.e == 10, 'counted all the letters in the song'); .value();
assert.strictEqual(counts.a, 16, 'counted all the letters in the song');
assert.strictEqual(counts.e, 10, 'counted all the letters in the song');
}); });
test("select/reject/sortBy", function() { QUnit.test('select/reject/sortBy', function(assert) {
var numbers = [1,2,3,4,5,6,7,8,9,10]; var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers = _(numbers).chain().select(function(n) { numbers = _(numbers).chain().select(function(n) {
return n % 2 === 0; return n % 2 === 0;
}).reject(function(n) { }).reject(function(n) {
@ -29,11 +32,11 @@ $(document).ready(function() {
}).sortBy(function(n) { }).sortBy(function(n) {
return -n; return -n;
}).value(); }).value();
equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
}); });
test("select/reject/sortBy in functional style", function() { QUnit.test('select/reject/sortBy in functional style', function(assert) {
var numbers = [1,2,3,4,5,6,7,8,9,10]; var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers = _.chain(numbers).select(function(n) { numbers = _.chain(numbers).select(function(n) {
return n % 2 === 0; return n % 2 === 0;
}).reject(function(n) { }).reject(function(n) {
@ -41,11 +44,11 @@ $(document).ready(function() {
}).sortBy(function(n) { }).sortBy(function(n) {
return -n; return -n;
}).value(); }).value();
equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers');
}); });
test("reverse/concat/unshift/pop/map", function() { QUnit.test('reverse/concat/unshift/pop/map', function(assert) {
var numbers = [1,2,3,4,5]; var numbers = [1, 2, 3, 4, 5];
numbers = _(numbers).chain() numbers = _(numbers).chain()
.reverse() .reverse()
.concat([5, 5, 5]) .concat([5, 5, 5])
@ -53,13 +56,44 @@ $(document).ready(function() {
.pop() .pop()
.map(function(n){ return n * 2; }) .map(function(n){ return n * 2; })
.value(); .value();
equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); assert.deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.');
}); });
test("chaining works in small stages", function() { QUnit.test('splice', function(assert) {
var instance = _([1, 2, 3, 4, 5]).chain();
assert.deepEqual(instance.splice(1, 3).value(), [1, 5]);
assert.deepEqual(instance.splice(1, 0).value(), [1, 5]);
assert.deepEqual(instance.splice(1, 1).value(), [1]);
assert.deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array');
});
QUnit.test('shift', function(assert) {
var instance = _([1, 2, 3]).chain();
assert.deepEqual(instance.shift().value(), [2, 3]);
assert.deepEqual(instance.shift().value(), [3]);
assert.deepEqual(instance.shift().value(), [], '#397 Can create empty array');
});
QUnit.test('pop', function(assert) {
var instance = _([1, 2, 3]).chain();
assert.deepEqual(instance.pop().value(), [1, 2]);
assert.deepEqual(instance.pop().value(), [1]);
assert.deepEqual(instance.pop().value(), [], '#397 Can create empty array');
});
QUnit.test('chaining works in small stages', function(assert) {
var o = _([1, 2, 3, 4]).chain(); var o = _([1, 2, 3, 4]).chain();
deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); assert.deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]);
deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); assert.deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]);
}); });
}); QUnit.test('#1562: Engine proxies for chained functions', function(assert) {
var wrapped = _(512);
assert.strictEqual(wrapped.toJSON(), 512);
assert.strictEqual(wrapped.valueOf(), 512);
assert.strictEqual(+wrapped, 512);
assert.strictEqual(wrapped.toString(), '512');
assert.strictEqual('' + wrapped, '512');
});
}());

File diff suppressed because it is too large Load diff

142
vendor/underscore/test/cross-document.js vendored Normal file
View file

@ -0,0 +1,142 @@
(function() {
if (typeof document == 'undefined') return;
var _ = typeof require == 'function' ? require('..') : window._;
QUnit.module('Cross Document');
/* global iObject, iElement, iArguments, iFunction, iArray, iError, iString, iNumber, iBoolean, iDate, iRegExp, iNaN, iNull, iUndefined, ActiveXObject */
// Setup remote variables for iFrame tests.
var iframe = document.createElement('iframe');
iframe.frameBorder = iframe.height = iframe.width = 0;
document.body.appendChild(iframe);
var iframeContent = iframe.contentDocument || iframe.contentWindow;
var iDoc = iframeContent.document || iframeContent;
iDoc.write(
[
'<script>',
'parent.iElement = document.createElement("div");',
'parent.iArguments = (function(){ return arguments; })(1, 2, 3);',
'parent.iArray = [1, 2, 3];',
'parent.iString = new String("hello");',
'parent.iNumber = new Number(100);',
'parent.iFunction = (function(){});',
'parent.iDate = new Date();',
'parent.iRegExp = /hi/;',
'parent.iNaN = NaN;',
'parent.iNull = null;',
'parent.iBoolean = new Boolean(false);',
'parent.iUndefined = undefined;',
'parent.iObject = {};',
'parent.iError = new Error();',
'</script>'
].join('\n')
);
iDoc.close();
QUnit.test('isEqual', function(assert) {
assert.notOk(_.isEqual(iNumber, 101));
assert.ok(_.isEqual(iNumber, 100));
// Objects from another frame.
assert.ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal');
// Array from another frame.
assert.ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal');
});
QUnit.test('isEmpty', function(assert) {
assert.notOk(_([iNumber]).isEmpty(), '[1] is not empty');
assert.notOk(_.isEmpty(iArray), '[] is empty');
assert.ok(_.isEmpty(iObject), '{} is empty');
});
QUnit.test('isElement', function(assert) {
assert.notOk(_.isElement('div'), 'strings are not dom elements');
assert.ok(_.isElement(document.body), 'the body tag is a DOM element');
assert.ok(_.isElement(iElement), 'even from another frame');
});
QUnit.test('isArguments', function(assert) {
assert.ok(_.isArguments(iArguments), 'even from another frame');
});
QUnit.test('isObject', function(assert) {
assert.ok(_.isObject(iElement), 'even from another frame');
assert.ok(_.isObject(iFunction), 'even from another frame');
});
QUnit.test('isArray', function(assert) {
assert.ok(_.isArray(iArray), 'even from another frame');
});
QUnit.test('isString', function(assert) {
assert.ok(_.isString(iString), 'even from another frame');
});
QUnit.test('isNumber', function(assert) {
assert.ok(_.isNumber(iNumber), 'even from another frame');
});
QUnit.test('isBoolean', function(assert) {
assert.ok(_.isBoolean(iBoolean), 'even from another frame');
});
QUnit.test('isFunction', function(assert) {
assert.ok(_.isFunction(iFunction), 'even from another frame');
});
QUnit.test('isDate', function(assert) {
assert.ok(_.isDate(iDate), 'even from another frame');
});
QUnit.test('isRegExp', function(assert) {
assert.ok(_.isRegExp(iRegExp), 'even from another frame');
});
QUnit.test('isNaN', function(assert) {
assert.ok(_.isNaN(iNaN), 'even from another frame');
});
QUnit.test('isNull', function(assert) {
assert.ok(_.isNull(iNull), 'even from another frame');
});
QUnit.test('isUndefined', function(assert) {
assert.ok(_.isUndefined(iUndefined), 'even from another frame');
});
QUnit.test('isError', function(assert) {
assert.ok(_.isError(iError), 'even from another frame');
});
if (typeof ActiveXObject != 'undefined') {
QUnit.test('IE host objects', function(assert) {
var xml = new ActiveXObject('Msxml2.DOMDocument.3.0');
assert.notOk(_.isNumber(xml));
assert.notOk(_.isBoolean(xml));
assert.notOk(_.isNaN(xml));
assert.notOk(_.isFunction(xml));
assert.notOk(_.isNull(xml));
assert.notOk(_.isUndefined(xml));
});
QUnit.test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) {
var fn = function() {};
var xml = new ActiveXObject('Msxml2.DOMDocument.3.0');
var div = document.createElement('div');
// JIT the function
var count = 200;
while (count--) {
_.isFunction(fn);
}
assert.strictEqual(_.isFunction(xml), false);
assert.strictEqual(_.isFunction(div), false);
assert.strictEqual(_.isFunction(fn), true);
});
}
}());

View file

@ -1,162 +1,265 @@
$(document).ready(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
module("Functions"); QUnit.module('Functions');
QUnit.config.asyncRetries = 3;
test("bind", function() { QUnit.test('bind', function(assert) {
var context = {name : 'moe'}; var context = {name: 'moe'};
var func = function(arg) { return "name: " + (this.name || arg); }; var func = function(arg) { return 'name: ' + (this.name || arg); };
var bound = _.bind(func, context); var bound = _.bind(func, context);
equal(bound(), 'name: moe', 'can bind a function to a context'); assert.strictEqual(bound(), 'name: moe', 'can bind a function to a context');
bound = _(func).bind(context); bound = _(func).bind(context);
equal(bound(), 'name: moe', 'can do OO-style binding'); assert.strictEqual(bound(), 'name: moe', 'can do OO-style binding');
bound = _.bind(func, null, 'curly'); bound = _.bind(func, null, 'curly');
equal(bound(), 'name: curly', 'can bind without specifying a context'); var result = bound();
// Work around a PhantomJS bug when applying a function with null|undefined.
assert.ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context');
func = function(salutation, name) { return salutation + ': ' + name; }; func = function(salutation, name) { return salutation + ': ' + name; };
func = _.bind(func, this, 'hello'); func = _.bind(func, this, 'hello');
equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); assert.strictEqual(func('moe'), 'hello: moe', 'the function was partially applied in advance');
func = _.bind(func, this, 'curly'); func = _.bind(func, this, 'curly');
equal(func(), 'hello: curly', 'the function was completely applied in advance'); assert.strictEqual(func(), 'hello: curly', 'the function was completely applied in advance');
func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
func = _.bind(func, this, 'hello', 'moe', 'curly'); func = _.bind(func, this, 'hello', 'moe', 'curly');
equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); assert.strictEqual(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
func = function(context, message) { equal(this, context, message); }; func = function() { return this; };
_.bind(func, 0, 0, 'can bind a function to `0`')(); assert.strictEqual(typeof _.bind(func, 0)(), 'object', 'binding a primitive to `this` returns a wrapped primitive');
_.bind(func, '', '', 'can bind a function to an empty string')();
_.bind(func, false, false, 'can bind a function to `false`')(); assert.strictEqual(_.bind(func, 0)().valueOf(), 0, 'can bind a function to `0`');
assert.strictEqual(_.bind(func, '')().valueOf(), '', 'can bind a function to an empty string');
assert.strictEqual(_.bind(func, false)().valueOf(), false, 'can bind a function to `false`');
// These tests are only meaningful when using a browser without a native bind function // These tests are only meaningful when using a browser without a native bind function
// To test this with a modern browser, set underscore's nativeBind to undefined // To test this with a modern browser, set underscore's nativeBind to undefined
var F = function () { return this; }; var F = function() { return this; };
var Boundf = _.bind(F, {hello: "moe curly"}); var boundf = _.bind(F, {hello: 'moe curly'});
var Boundf = boundf; // make eslint happy.
var newBoundf = new Boundf(); var newBoundf = new Boundf();
equal(newBoundf.hello, undefined, "function should not be bound to the context, to comply with ECMAScript 5"); assert.strictEqual(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5');
equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); assert.strictEqual(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
ok(newBoundf instanceof F, "a bound instance is an instance of the original function"); assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
assert.raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
}); });
test("partial", function() { QUnit.test('partial', function(assert) {
var obj = {name: 'moe'}; var obj = {name: 'moe'};
var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); };
obj.func = _.partial(func, 'a', 'b'); obj.func = _.partial(func, 'a', 'b');
equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); assert.strictEqual(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
obj.func = _.partial(func, _, 'b', _, 'd');
assert.strictEqual(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders');
func = _.partial(function() { return arguments.length; }, _, 'b', _, 'd');
assert.strictEqual(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders');
assert.strictEqual(func('a'), 4, 'accepts fewer arguments than the number of placeholders');
func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd');
assert.strictEqual(func('a'), 'undefined', 'unfilled placeholders are undefined');
// passes context
function MyWidget(name, options) {
this.name = name;
this.options = options;
}
MyWidget.prototype.get = function() {
return this.name;
};
var MyWidgetWithCoolOpts = _.partial(MyWidget, _, {a: 1});
var widget = new MyWidgetWithCoolOpts('foo');
assert.ok(widget instanceof MyWidget, 'Can partially bind a constructor');
assert.strictEqual(widget.get(), 'foo', 'keeps prototype');
assert.deepEqual(widget.options, {a: 1});
_.partial.placeholder = obj;
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
assert.strictEqual(func('a'), 4, 'allows the placeholder to be swapped out');
_.partial.placeholder = {};
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
assert.strictEqual(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
_.partial.placeholder = _;
}); });
test("bindAll", function() { QUnit.test('bindAll', function(assert) {
var curly = {name : 'curly'}, moe = { var curly = {name: 'curly'};
name : 'moe', var moe = {
getName : function() { return 'name: ' + this.name; }, name: 'moe',
sayHi : function() { return 'hi: ' + this.name; } getName: function() { return 'name: ' + this.name; },
sayHi: function() { return 'hi: ' + this.name; }
}; };
curly.getName = moe.getName; curly.getName = moe.getName;
_.bindAll(moe, 'getName', 'sayHi'); _.bindAll(moe, 'getName', 'sayHi');
curly.sayHi = moe.sayHi; curly.sayHi = moe.sayHi;
equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); assert.strictEqual(curly.getName(), 'name: curly', 'unbound function is bound to current object');
equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); assert.strictEqual(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
curly = {name : 'curly'}; curly = {name: 'curly'};
moe = { moe = {
name : 'moe', name: 'moe',
getName : function() { return 'name: ' + this.name; }, getName: function() { return 'name: ' + this.name; },
sayHi : function() { return 'hi: ' + this.name; } sayHi: function() { return 'hi: ' + this.name; },
sayLast: function() { return this.sayHi(_.last(arguments)); }
}; };
raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); assert.raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
assert.raises(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined');
assert.raises(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
_.bindAll(moe, 'sayHi'); _.bindAll(moe, 'sayHi', 'sayLast');
curly.sayHi = moe.sayHi; curly.sayHi = moe.sayHi;
equal(curly.sayHi(), 'hi: moe'); assert.strictEqual(curly.sayHi(), 'hi: moe');
var sayLast = moe.sayLast;
assert.strictEqual(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
_.bindAll(moe, ['getName']);
var getName = moe.getName;
assert.strictEqual(getName(), 'name: moe', 'flattens arguments into a single list');
}); });
test("memoize", function() { QUnit.test('memoize', function(assert) {
var fib = function(n) { var fib = function(n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2); return n < 2 ? n : fib(n - 1) + fib(n - 2);
}; };
equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results');
fib = _.memoize(fib); // Redefine `fib` for memoization fib = _.memoize(fib); // Redefine `fib` for memoization
equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results');
var o = function(str) { var o = function(str) {
return str; return str;
}; };
var fastO = _.memoize(o); var fastO = _.memoize(o);
equal(o('toString'), 'toString', 'checks hasOwnProperty'); assert.strictEqual(o('toString'), 'toString', 'checks hasOwnProperty');
equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); assert.strictEqual(fastO('toString'), 'toString', 'checks hasOwnProperty');
// Expose the cache.
var upper = _.memoize(function(s) {
return s.toUpperCase();
});
assert.strictEqual(upper('foo'), 'FOO');
assert.strictEqual(upper('bar'), 'BAR');
assert.deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'});
upper.cache = {foo: 'BAR', bar: 'FOO'};
assert.strictEqual(upper('foo'), 'BAR');
assert.strictEqual(upper('bar'), 'FOO');
var hashed = _.memoize(function(key) {
//https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209
assert.ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys');
return key;
}, function(key) {
return key.toUpperCase();
});
hashed('yep');
assert.deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher');
// Test that the hash function can be used to swizzle the key.
var objCacher = _.memoize(function(value, key) {
return {key: key, value: value};
}, function(value, key) {
return key;
});
var myObj = objCacher('a', 'alpha');
var myObjAlias = objCacher('b', 'alpha');
assert.notStrictEqual(myObj, void 0, 'object is created if second argument used as key');
assert.strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key');
assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key');
}); });
asyncTest("delay", 2, function() { QUnit.test('delay', function(assert) {
assert.expect(2);
var done = assert.async();
var delayed = false; var delayed = false;
_.delay(function(){ delayed = true; }, 100); _.delay(function(){ delayed = true; }, 100);
setTimeout(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50); setTimeout(function(){ assert.notOk(delayed, "didn't delay the function quite yet"); }, 50);
setTimeout(function(){ ok(delayed, 'delayed the function'); start(); }, 150); setTimeout(function(){ assert.ok(delayed, 'delayed the function'); done(); }, 150);
}); });
asyncTest("defer", 1, function() { QUnit.test('defer', function(assert) {
assert.expect(1);
var done = assert.async();
var deferred = false; var deferred = false;
_.defer(function(bool){ deferred = bool; }, true); _.defer(function(bool){ deferred = bool; }, true);
_.delay(function(){ ok(deferred, "deferred the function"); start(); }, 50); _.delay(function(){ assert.ok(deferred, 'deferred the function'); done(); }, 50);
}); });
asyncTest("throttle", 2, function() { QUnit.test('throttle', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32); var throttledIncr = _.throttle(incr, 32);
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
equal(counter, 1, "incr was called immediately"); assert.strictEqual(counter, 1, 'incr was called immediately');
_.delay(function(){ equal(counter, 2, "incr was throttled"); start(); }, 64); _.delay(function(){ assert.strictEqual(counter, 2, 'incr was throttled'); done(); }, 64);
}); });
asyncTest("throttle arguments", 2, function() { QUnit.test('throttle arguments', function(assert) {
assert.expect(2);
var done = assert.async();
var value = 0; var value = 0;
var update = function(val){ value = val; }; var update = function(val){ value = val; };
var throttledUpdate = _.throttle(update, 32); var throttledUpdate = _.throttle(update, 32);
throttledUpdate(1); throttledUpdate(2); throttledUpdate(1); throttledUpdate(2);
_.delay(function(){ throttledUpdate(3); }, 64); _.delay(function(){ throttledUpdate(3); }, 64);
equal(value, 1, "updated to latest value"); assert.strictEqual(value, 1, 'updated to latest value');
_.delay(function(){ equal(value, 3, "updated to latest value"); start(); }, 96); _.delay(function(){ assert.strictEqual(value, 3, 'updated to latest value'); done(); }, 96);
}); });
asyncTest("throttle once", 2, function() { QUnit.test('throttle once', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ return ++counter; }; var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 32); var throttledIncr = _.throttle(incr, 32);
var result = throttledIncr(); var result = throttledIncr();
_.delay(function(){ _.delay(function(){
equal(result, 1, "throttled functions return their value"); assert.strictEqual(result, 1, 'throttled functions return their value');
equal(counter, 1, "incr was called once"); start(); assert.strictEqual(counter, 1, 'incr was called once'); done();
}, 64); }, 64);
}); });
asyncTest("throttle twice", 1, function() { QUnit.test('throttle twice', function(assert) {
assert.expect(1);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32); var throttledIncr = _.throttle(incr, 32);
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
_.delay(function(){ equal(counter, 2, "incr was called twice"); start(); }, 64); _.delay(function(){ assert.strictEqual(counter, 2, 'incr was called twice'); done(); }, 64);
}); });
asyncTest("more throttling", 3, function() { QUnit.test('more throttling', function(assert) {
assert.expect(3);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 30); var throttledIncr = _.throttle(incr, 30);
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
ok(counter == 1); assert.strictEqual(counter, 1);
_.delay(function(){ _.delay(function(){
ok(counter == 2); assert.strictEqual(counter, 2);
throttledIncr(); throttledIncr();
ok(counter == 3); assert.strictEqual(counter, 3);
start(); done();
}, 85); }, 85);
}); });
asyncTest("throttle repeatedly with results", 6, function() { QUnit.test('throttle repeatedly with results', function(assert) {
assert.expect(6);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ return ++counter; }; var incr = function(){ return ++counter; };
var throttledIncr = _.throttle(incr, 100); var throttledIncr = _.throttle(incr, 100);
@ -168,50 +271,56 @@ $(document).ready(function() {
_.delay(saveResult, 160); _.delay(saveResult, 160);
_.delay(saveResult, 230); _.delay(saveResult, 230);
_.delay(function() { _.delay(function() {
equal(results[0], 1, "incr was called once"); assert.strictEqual(results[0], 1, 'incr was called once');
equal(results[1], 1, "incr was throttled"); assert.strictEqual(results[1], 1, 'incr was throttled');
equal(results[2], 1, "incr was throttled"); assert.strictEqual(results[2], 1, 'incr was throttled');
equal(results[3], 2, "incr was called twice"); assert.strictEqual(results[3], 2, 'incr was called twice');
equal(results[4], 2, "incr was throttled"); assert.strictEqual(results[4], 2, 'incr was throttled');
equal(results[5], 3, "incr was called trailing"); assert.strictEqual(results[5], 3, 'incr was called trailing');
start(); done();
}, 300); }, 300);
}); });
asyncTest("throttle triggers trailing call when invoked repeatedly", 2, function() { QUnit.test('throttle triggers trailing call when invoked repeatedly', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var limit = 48; var limit = 48;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32); var throttledIncr = _.throttle(incr, 32);
var stamp = new Date; var stamp = new Date;
while ((new Date - stamp) < limit) { while (new Date - stamp < limit) {
throttledIncr(); throttledIncr();
} }
var lastCount = counter; var lastCount = counter;
ok(counter > 1); assert.ok(counter > 1);
_.delay(function() { _.delay(function() {
ok(counter > lastCount); assert.ok(counter > lastCount);
start(); done();
}, 96); }, 96);
}); });
asyncTest("throttle does not trigger leading call when leading is set to false", 2, function() { QUnit.test('throttle does not trigger leading call when leading is set to false', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 60, {leading: false}); var throttledIncr = _.throttle(incr, 60, {leading: false});
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
ok(counter === 0); assert.strictEqual(counter, 0);
_.delay(function() { _.delay(function() {
ok(counter == 1); assert.strictEqual(counter, 1);
start(); done();
}, 96); }, 96);
}); });
asyncTest("more throttle does not trigger leading call when leading is set to false", 3, function() { QUnit.test('more throttle does not trigger leading call when leading is set to false', function(assert) {
assert.expect(3);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100, {leading: false}); var throttledIncr = _.throttle(incr, 100, {leading: false});
@ -220,133 +329,325 @@ $(document).ready(function() {
_.delay(throttledIncr, 50); _.delay(throttledIncr, 50);
_.delay(throttledIncr, 60); _.delay(throttledIncr, 60);
_.delay(throttledIncr, 200); _.delay(throttledIncr, 200);
ok(counter === 0); assert.strictEqual(counter, 0);
_.delay(function() { _.delay(function() {
ok(counter == 1); assert.strictEqual(counter, 1);
}, 250); }, 250);
_.delay(function() { _.delay(function() {
ok(counter == 2); assert.strictEqual(counter, 2);
start(); done();
}, 350); }, 350);
}); });
asyncTest("one more throttle with leading: false test", 2, function() { QUnit.test('one more throttle with leading: false test', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100, {leading: false}); var throttledIncr = _.throttle(incr, 100, {leading: false});
var time = new Date; var time = new Date;
while (new Date - time < 350) throttledIncr(); while (new Date - time < 350) throttledIncr();
ok(counter <= 3); assert.ok(counter <= 3);
_.delay(function() { _.delay(function() {
ok(counter <= 4); assert.ok(counter <= 4);
start(); done();
}, 200); }, 200);
}); });
asyncTest("throttle does not trigger trailing call when trailing is set to false", 4, function() { QUnit.test('throttle does not trigger trailing call when trailing is set to false', function(assert) {
assert.expect(4);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 60, {trailing: false}); var throttledIncr = _.throttle(incr, 60, {trailing: false});
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
ok(counter === 1); assert.strictEqual(counter, 1);
_.delay(function() { _.delay(function() {
ok(counter == 1); assert.strictEqual(counter, 1);
throttledIncr(); throttledIncr(); throttledIncr(); throttledIncr();
ok(counter == 2); assert.strictEqual(counter, 2);
_.delay(function() { _.delay(function() {
ok(counter == 2); assert.strictEqual(counter, 2);
start(); done();
}, 96); }, 96);
}, 96); }, 96);
}); });
asyncTest("debounce", 1, function() { QUnit.test('throttle continues to function after system time is set backwards', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 100);
var origNowFunc = _.now;
throttledIncr();
assert.strictEqual(counter, 1);
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
};
_.delay(function() {
throttledIncr();
assert.strictEqual(counter, 2);
done();
_.now = origNowFunc;
}, 200);
});
QUnit.test('throttle re-entrant', function(assert) {
assert.expect(2);
var done = assert.async();
var sequence = [
['b1', 'b2'],
['c1', 'c2']
];
var value = '';
var throttledAppend;
var append = function(arg){
value += this + arg;
var args = sequence.pop();
if (args) {
throttledAppend.call(args[0], args[1]);
}
};
throttledAppend = _.throttle(append, 32);
throttledAppend.call('a1', 'a2');
assert.strictEqual(value, 'a1a2');
_.delay(function(){
assert.strictEqual(value, 'a1a2c1c2b1b2', 'append was throttled successfully');
done();
}, 100);
});
QUnit.test('throttle cancel', function(assert) {
var done = assert.async();
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32);
throttledIncr();
throttledIncr.cancel();
throttledIncr();
throttledIncr();
assert.strictEqual(counter, 2, 'incr was called immediately');
_.delay(function(){ assert.strictEqual(counter, 3, 'incr was throttled'); done(); }, 64);
});
QUnit.test('throttle cancel with leading: false', function(assert) {
var done = assert.async();
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 32, {leading: false});
throttledIncr();
throttledIncr.cancel();
assert.strictEqual(counter, 0, 'incr was throttled');
_.delay(function(){ assert.strictEqual(counter, 0, 'incr was throttled'); done(); }, 64);
});
QUnit.test('debounce', function(assert) {
assert.expect(1);
var done = assert.async();
var counter = 0; var counter = 0;
var incr = function(){ counter++; }; var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 32); var debouncedIncr = _.debounce(incr, 32);
debouncedIncr(); debouncedIncr(); debouncedIncr(); debouncedIncr();
_.delay(debouncedIncr, 16); _.delay(debouncedIncr, 16);
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 96); _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96);
}); });
asyncTest("debounce asap", 4, function() { QUnit.test('debounce cancel', function(assert) {
var a, b; assert.expect(1);
var done = assert.async();
var counter = 0;
var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 32);
debouncedIncr();
debouncedIncr.cancel();
_.delay(function(){ assert.strictEqual(counter, 0, 'incr was not called'); done(); }, 96);
});
QUnit.test('debounce asap', function(assert) {
assert.expect(6);
var done = assert.async();
var a, b, c;
var counter = 0; var counter = 0;
var incr = function(){ return ++counter; }; var incr = function(){ return ++counter; };
var debouncedIncr = _.debounce(incr, 64, true); var debouncedIncr = _.debounce(incr, 64, true);
a = debouncedIncr(); a = debouncedIncr();
b = debouncedIncr(); b = debouncedIncr();
equal(a, 1); assert.strictEqual(a, 1);
equal(b, 1); assert.strictEqual(b, 1);
equal(counter, 1, 'incr was called immediately'); assert.strictEqual(counter, 1, 'incr was called immediately');
_.delay(debouncedIncr, 16); _.delay(debouncedIncr, 16);
_.delay(debouncedIncr, 32); _.delay(debouncedIncr, 32);
_.delay(debouncedIncr, 48); _.delay(debouncedIncr, 48);
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 128); _.delay(function(){
assert.strictEqual(counter, 1, 'incr was debounced');
c = debouncedIncr();
assert.strictEqual(c, 2);
assert.strictEqual(counter, 2, 'incr was called again');
done();
}, 128);
}); });
asyncTest("debounce asap recursively", 2, function() { QUnit.test('debounce asap cancel', function(assert) {
assert.expect(4);
var done = assert.async();
var a, b;
var counter = 0;
var incr = function(){ return ++counter; };
var debouncedIncr = _.debounce(incr, 64, true);
a = debouncedIncr();
debouncedIncr.cancel();
b = debouncedIncr();
assert.strictEqual(a, 1);
assert.strictEqual(b, 2);
assert.strictEqual(counter, 2, 'incr was called immediately');
_.delay(debouncedIncr, 16);
_.delay(debouncedIncr, 32);
_.delay(debouncedIncr, 48);
_.delay(function(){ assert.strictEqual(counter, 2, 'incr was debounced'); done(); }, 128);
});
QUnit.test('debounce asap recursively', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0; var counter = 0;
var debouncedIncr = _.debounce(function(){ var debouncedIncr = _.debounce(function(){
counter++; counter++;
if (counter < 10) debouncedIncr(); if (counter < 10) debouncedIncr();
}, 32, true); }, 32, true);
debouncedIncr(); debouncedIncr();
equal(counter, 1, "incr was called immediately"); assert.strictEqual(counter, 1, 'incr was called immediately');
_.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 96); _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96);
}); });
test("once", function() { QUnit.test('debounce after system time is set backwards', function(assert) {
assert.expect(2);
var done = assert.async();
var counter = 0;
var origNowFunc = _.now;
var debouncedIncr = _.debounce(function(){
counter++;
}, 100, true);
debouncedIncr();
assert.strictEqual(counter, 1, 'incr was called immediately');
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
};
_.delay(function() {
debouncedIncr();
assert.strictEqual(counter, 2, 'incr was debounced successfully');
done();
_.now = origNowFunc;
}, 200);
});
QUnit.test('debounce re-entrant', function(assert) {
assert.expect(2);
var done = assert.async();
var sequence = [
['b1', 'b2']
];
var value = '';
var debouncedAppend;
var append = function(arg){
value += this + arg;
var args = sequence.pop();
if (args) {
debouncedAppend.call(args[0], args[1]);
}
};
debouncedAppend = _.debounce(append, 32);
debouncedAppend.call('a1', 'a2');
assert.strictEqual(value, '');
_.delay(function(){
assert.strictEqual(value, 'a1a2b1b2', 'append was debounced successfully');
done();
}, 100);
});
QUnit.test('once', function(assert) {
var num = 0; var num = 0;
var increment = _.once(function(){ num++; }); var increment = _.once(function(){ return ++num; });
increment(); increment();
increment(); increment();
equal(num, 1); assert.strictEqual(num, 1);
assert.strictEqual(increment(), 1, 'stores a memo to the last value');
}); });
test("Recursive onced function.", 1, function() { QUnit.test('Recursive onced function.', function(assert) {
assert.expect(1);
var f = _.once(function(){ var f = _.once(function(){
ok(true); assert.ok(true);
f(); f();
}); });
f(); f();
}); });
test("wrap", function() { QUnit.test('wrap', function(assert) {
var greet = function(name){ return "hi: " + name; }; var greet = function(name){ return 'hi: ' + name; };
var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });
equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); assert.strictEqual(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
var inner = function(){ return "Hello "; }; var inner = function(){ return 'Hello '; };
var obj = {name : "Moe"}; var obj = {name: 'Moe'};
obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
equal(obj.hi(), "Hello Moe"); assert.strictEqual(obj.hi(), 'Hello Moe');
var noop = function(){}; var noop = function(){};
var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); });
var ret = wrapped(['whats', 'your'], 'vector', 'victor'); var ret = wrapped(['whats', 'your'], 'vector', 'victor');
deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); assert.deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
}); });
test("compose", function() { QUnit.test('negate', function(assert) {
var greet = function(name){ return "hi: " + name; }; var isOdd = function(n){ return n & 1; };
assert.strictEqual(_.negate(isOdd)(2), true, 'should return the complement of the given function');
assert.strictEqual(_.negate(isOdd)(3), false, 'should return the complement of the given function');
});
QUnit.test('compose', function(assert) {
var greet = function(name){ return 'hi: ' + name; };
var exclaim = function(sentence){ return sentence + '!'; }; var exclaim = function(sentence){ return sentence + '!'; };
var composed = _.compose(exclaim, greet); var composed = _.compose(exclaim, greet);
equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); assert.strictEqual(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
composed = _.compose(greet, exclaim); composed = _.compose(greet, exclaim);
equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); assert.strictEqual(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
// f(g(h(x, y, z)))
function h(x, y, z) {
assert.strictEqual(arguments.length, 3, 'First function called with multiple args');
return z * y;
}
function g(x) {
assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument');
return x;
}
function f(x) {
assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument');
return x * 2;
}
composed = _.compose(f, g, h);
assert.strictEqual(composed(1, 2, 3), 12);
}); });
test("after", function() { QUnit.test('after', function(assert) {
var testAfter = function(afterAmount, timesCalled) { var testAfter = function(afterAmount, timesCalled) {
var afterCalled = 0; var afterCalled = 0;
var after = _.after(afterAmount, function() { var after = _.after(afterAmount, function() {
@ -356,10 +657,114 @@ $(document).ready(function() {
return afterCalled; return afterCalled;
}; };
equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); assert.strictEqual(testAfter(5, 5), 1, 'after(N) should fire after being called N times');
equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); assert.strictEqual(testAfter(5, 4), 0, 'after(N) should not fire unless called N times');
equal(testAfter(0, 0), 0, "after(0) should not fire immediately"); assert.strictEqual(testAfter(0, 0), 0, 'after(0) should not fire immediately');
equal(testAfter(0, 1), 1, "after(0) should fire when first invoked"); assert.strictEqual(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
}); });
}); QUnit.test('before', function(assert) {
var testBefore = function(beforeAmount, timesCalled) {
var beforeCalled = 0;
var before = _.before(beforeAmount, function() { beforeCalled++; });
while (timesCalled--) before();
return beforeCalled;
};
assert.strictEqual(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
assert.strictEqual(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
assert.strictEqual(testBefore(0, 0), 0, 'before(0) should not fire immediately');
assert.strictEqual(testBefore(0, 1), 0, 'before(0) should not fire when first invoked');
var context = {num: 0};
var increment = _.before(3, function(){ return ++this.num; });
_.times(10, increment, context);
assert.strictEqual(increment(), 2, 'stores a memo to the last value');
assert.strictEqual(context.num, 2, 'provides context');
});
QUnit.test('iteratee', function(assert) {
var identity = _.iteratee();
assert.strictEqual(identity, _.identity, '_.iteratee is exposed as an external function.');
function fn() {
return arguments;
}
_.each([_.iteratee(fn), _.iteratee(fn, {})], function(cb) {
assert.strictEqual(cb().length, 0);
assert.deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4));
assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11));
});
var deepProperty = _.iteratee(['a', 'b']);
assert.strictEqual(deepProperty({a: {b: 2}}), 2, 'treats an array as a deep property accessor');
// Test custom iteratee
var builtinIteratee = _.iteratee;
_.iteratee = function(value) {
// RegEx values return a function that returns the number of matches
if (_.isRegExp(value)) return function(obj) {
return (obj.match(value) || []).length;
};
return value;
};
var collection = ['foo', 'bar', 'bbiz'];
// Test all methods that claim to be transformed through `_.iteratee`
assert.deepEqual(_.countBy(collection, /b/g), {0: 1, 1: 1, 2: 1});
assert.strictEqual(_.every(collection, /b/g), false);
assert.deepEqual(_.filter(collection, /b/g), ['bar', 'bbiz']);
assert.strictEqual(_.find(collection, /b/g), 'bar');
assert.strictEqual(_.findIndex(collection, /b/g), 1);
assert.strictEqual(_.findKey(collection, /b/g), '1');
assert.strictEqual(_.findLastIndex(collection, /b/g), 2);
assert.deepEqual(_.groupBy(collection, /b/g), {0: ['foo'], 1: ['bar'], 2: ['bbiz']});
assert.deepEqual(_.indexBy(collection, /b/g), {0: 'foo', 1: 'bar', 2: 'bbiz'});
assert.deepEqual(_.map(collection, /b/g), [0, 1, 2]);
assert.strictEqual(_.max(collection, /b/g), 'bbiz');
assert.strictEqual(_.min(collection, /b/g), 'foo');
assert.deepEqual(_.partition(collection, /b/g), [['bar', 'bbiz'], ['foo']]);
assert.deepEqual(_.reject(collection, /b/g), ['foo']);
assert.strictEqual(_.some(collection, /b/g), true);
assert.deepEqual(_.sortBy(collection, /b/g), ['foo', 'bar', 'bbiz']);
assert.strictEqual(_.sortedIndex(collection, 'blah', /b/g), 1);
assert.deepEqual(_.uniq(collection, /b/g), ['foo', 'bar', 'bbiz']);
var objCollection = {a: 'foo', b: 'bar', c: 'bbiz'};
assert.deepEqual(_.mapObject(objCollection, /b/g), {a: 0, b: 1, c: 2});
// Restore the builtin iteratee
_.iteratee = builtinIteratee;
});
QUnit.test('restArgs', function(assert) {
assert.expect(10);
_.restArgs(function(a, args) {
assert.strictEqual(a, 1);
assert.deepEqual(args, [2, 3], 'collects rest arguments into an array');
})(1, 2, 3);
_.restArgs(function(a, args) {
assert.strictEqual(a, void 0);
assert.deepEqual(args, [], 'passes empty array if there are not enough arguments');
})();
_.restArgs(function(a, b, c, args) {
assert.strictEqual(arguments.length, 4);
assert.deepEqual(args, [4, 5], 'works on functions with many named parameters');
})(1, 2, 3, 4, 5);
var obj = {};
_.restArgs(function() {
assert.strictEqual(this, obj, 'invokes function with this context');
}).call(obj);
_.restArgs(function(array, iteratee, context) {
assert.deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter');
assert.strictEqual(iteratee, void 0);
assert.strictEqual(context, void 0);
}, 0)(1, 2, 3, 4);
});
}());

View file

@ -3,42 +3,21 @@
<head> <head>
<title>Underscore Test Suite</title> <title>Underscore Test Suite</title>
<link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen"> <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen">
<script src="vendor/jquery.js"></script> <link rel="icon" href="../favicon.ico">
</head>
<body>
<div id="qunit"></div>
<div id="exports" style="display: none"></div>
<script src="vendor/qunit.js"></script> <script src="vendor/qunit.js"></script>
<script src="vendor/jslitmus.js"></script> <script src="vendor/qunit-extras.js"></script>
<script src="../underscore.js"></script> <script src="../underscore.js"></script>
<script src="qunit-setup.js"></script>
<script src="collections.js"></script> <script src="collections.js"></script>
<script src="arrays.js"></script> <script src="arrays.js"></script>
<script src="functions.js"></script> <script src="functions.js"></script>
<script src="objects.js"></script> <script src="objects.js"></script>
<script src="cross-document.js"></script>
<script src="utility.js"></script> <script src="utility.js"></script>
<script src="chaining.js"></script> <script src="chaining.js"></script>
<script src="speed.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<div id="map-test">
<div id="id1"></div>
<div id="id2"></div>
</div>
</div>
<br>
<h1 class="qunit-header">Underscore Speed Suite</h1>
<p>
A representative sample of the functions are benchmarked here, to provide
a sense of how fast they might run in different browsers.
Each iteration runs on an array of 1000 elements.<br /><br />
For example, the 'intersection' test measures the number of times you can
find the intersection of two thousand-element arrays in one second.
</p>
<br>
<script type="text/html" id="template">
<%
// a comment
if (data) { data += 12345; }; %>
<li><%= data %></li>
</script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load diff

3
vendor/underscore/test/qunit-setup.js vendored Normal file
View file

@ -0,0 +1,3 @@
(function() {
QUnit.config.noglobals = true;
}());

View file

@ -1,272 +1,452 @@
$(document).ready(function() { (function() {
var _ = typeof require == 'function' ? require('..') : window._;
var templateSettings; var templateSettings;
module("Utility", { QUnit.module('Utility', {
setup: function() { beforeEach: function() {
templateSettings = _.clone(_.templateSettings); templateSettings = _.clone(_.templateSettings);
}, },
teardown: function() { afterEach: function() {
_.templateSettings = templateSettings; _.templateSettings = templateSettings;
} }
}); });
test("#750 - Return _ instance.", 2, function() { if (typeof this == 'object') {
QUnit.test('noConflict', function(assert) {
var underscore = _.noConflict();
assert.strictEqual(underscore.identity(1), 1);
if (typeof require != 'function') {
assert.strictEqual(this._, void 0, 'global underscore is removed');
this._ = underscore;
} else if (typeof global !== 'undefined') {
delete global._;
}
});
}
if (typeof require == 'function') {
QUnit.test('noConflict (node vm)', function(assert) {
assert.expect(2);
var done = assert.async();
var fs = require('fs');
var vm = require('vm');
var filename = __dirname + '/../underscore.js';
fs.readFile(filename, function(err, content){
var sandbox = vm.createScript(
content + 'this.underscore = this._.noConflict();',
filename
);
var context = {_: 'oldvalue'};
sandbox.runInNewContext(context);
assert.strictEqual(context._, 'oldvalue');
assert.strictEqual(context.underscore.VERSION, _.VERSION);
done();
});
});
}
QUnit.test('#750 - Return _ instance.', function(assert) {
assert.expect(2);
var instance = _([]); var instance = _([]);
ok(_(instance) === instance); assert.strictEqual(_(instance), instance);
ok(new _(instance) === instance); assert.strictEqual(new _(instance), instance);
}); });
test("identity", function() { QUnit.test('identity', function(assert) {
var moe = {name : 'moe'}; var stooge = {name: 'moe'};
equal(_.identity(moe), moe, 'moe is the same as his identity'); assert.strictEqual(_.identity(stooge), stooge, 'stooge is the same as his identity');
}); });
test("random", function() { QUnit.test('constant', function(assert) {
var stooge = {name: 'moe'};
assert.strictEqual(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
});
QUnit.test('noop', function(assert) {
assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined');
});
QUnit.test('random', function(assert) {
var array = _.range(1000); var array = _.range(1000);
var min = Math.pow(2, 31); var min = Math.pow(2, 31);
var max = Math.pow(2, 62); var max = Math.pow(2, 62);
ok(_.every(array, function() { assert.ok(_.every(array, function() {
return _.random(min, max) >= min; return _.random(min, max) >= min;
}), "should produce a random number greater than or equal to the minimum number"); }), 'should produce a random number greater than or equal to the minimum number');
ok(_.some(array, function() { assert.ok(_.some(array, function() {
return _.random(Number.MAX_VALUE) > 0; return _.random(Number.MAX_VALUE) > 0;
}), "should produce a random number when passed `Number.MAX_VALUE`"); }), 'should produce a random number when passed `Number.MAX_VALUE`');
}); });
test("uniqueId", function() { QUnit.test('now', function(assert) {
var diff = _.now() - new Date().getTime();
assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms
});
QUnit.test('uniqueId', function(assert) {
var ids = [], i = 0; var ids = [], i = 0;
while(i++ < 100) ids.push(_.uniqueId()); while (i++ < 100) ids.push(_.uniqueId());
equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); assert.strictEqual(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
}); });
test("times", function() { QUnit.test('times', function(assert) {
var vals = []; var vals = [];
_.times(3, function (i) { vals.push(i); }); _.times(3, function(i) { vals.push(i); });
ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed');
// //
vals = []; vals = [];
_(3).times(function(i) { vals.push(i); }); _(3).times(function(i) { vals.push(i); });
ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); assert.deepEqual(vals, [0, 1, 2], 'works as a wrapper');
// collects return values // collects return values
ok(_.isEqual([0, 1, 2], _.times(3, function(i) { return i; })), "collects return values"); assert.deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values');
deepEqual(_.times(0, _.identity), []); assert.deepEqual(_.times(0, _.identity), []);
deepEqual(_.times(-1, _.identity), []); assert.deepEqual(_.times(-1, _.identity), []);
deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []);
}); });
test("mixin", function() { QUnit.test('mixin', function(assert) {
_.mixin({ var ret = _.mixin({
myReverse: function(string) { myReverse: function(string) {
return string.split('').reverse().join(''); return string.split('').reverse().join('');
} }
}); });
equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); assert.strictEqual(ret, _, 'returns the _ object to facilitate chaining');
equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); assert.strictEqual(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
assert.strictEqual(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
}); });
test("_.escape", function() { QUnit.test('_.escape', function(assert) {
equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); assert.strictEqual(_.escape(null), '');
equal(_.escape('<a href="http://moe.com">Curly & Moe\'s</a>'), '&lt;a href=&quot;http://moe.com&quot;&gt;Curly &amp; Moe&#x27;s&lt;/a&gt;');
equal(_.escape("Curly &amp; Moe"), "Curly &amp;amp; Moe");
equal(_.escape(null), '');
}); });
test("_.unescape", function() { QUnit.test('_.unescape', function(assert) {
var string = "Curly & Moe"; var string = 'Curly & Moe';
equal(_.unescape("Curly &amp; Moe"), string); assert.strictEqual(_.unescape(null), '');
equal(_.unescape('&lt;a href=&quot;http://moe.com&quot;&gt;Curly &amp; Moe&#x27;s&lt;/a&gt;'), '<a href="http://moe.com">Curly & Moe\'s</a>'); assert.strictEqual(_.unescape(_.escape(string)), string);
equal(_.unescape("Curly &amp;amp; Moe"), "Curly &amp; Moe"); assert.strictEqual(_.unescape(string), string, 'don\'t unescape unnecessarily');
equal(_.unescape(null), '');
equal(_.unescape(_.escape(string)), string);
}); });
test("template", function() { // Don't care what they escape them to just that they're escaped and can be unescaped
QUnit.test('_.escape & unescape', function(assert) {
// test & (&amp;) separately obviously
var escapeCharacters = ['<', '>', '"', '\'', '`'];
_.each(escapeCharacters, function(escapeChar) {
var s = 'a ' + escapeChar + ' string escaped';
var e = _.escape(s);
assert.notEqual(s, e, escapeChar + ' is escaped');
assert.strictEqual(s, _.unescape(e), escapeChar + ' can be unescaped');
s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
e = _.escape(s);
assert.strictEqual(e.indexOf(escapeChar), -1, 'can escape multiple occurrences of ' + escapeChar);
assert.strictEqual(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
});
// handles multiple escape characters at once
var joiner = ' other stuff ';
var allEscaped = escapeCharacters.join(joiner);
allEscaped += allEscaped;
assert.ok(_.every(escapeCharacters, function(escapeChar) {
return allEscaped.indexOf(escapeChar) !== -1;
}), 'handles multiple characters');
assert.ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time');
// test & -> &amp;
var str = 'some string & another string & yet another';
var escaped = _.escape(str);
assert.notStrictEqual(escaped.indexOf('&'), -1, 'handles & aka &amp;');
assert.strictEqual(_.unescape(str), str, 'can unescape &amp;');
});
QUnit.test('template', function(assert) {
var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");
var result = basicTemplate({thing : 'This'}); var result = basicTemplate({thing: 'This'});
equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); assert.strictEqual(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
var sansSemicolonTemplate = _.template("A <% this %> B"); var sansSemicolonTemplate = _.template('A <% this %> B');
equal(sansSemicolonTemplate(), "A B"); assert.strictEqual(sansSemicolonTemplate(), 'A B');
var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); var backslashTemplate = _.template('<%= thing %> is \\ridanculous');
equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); assert.strictEqual(backslashTemplate({thing: 'This'}), 'This is \\ridanculous');
var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); assert.strictEqual(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
var fancyTemplate = _.template("<ul><% \ var fancyTemplate = _.template('<ul><% ' +
for (var key in people) { \ ' for (var key in people) { ' +
%><li><%= people[key] %></li><% } %></ul>"); '%><li><%= people[key] %></li><% } %></ul>');
result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates'); assert.strictEqual(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var escapedCharsInJavascriptTemplate = _.template("<ul><% _.each(numbers.split('\\n'), function(item) { %><li><%= item %></li><% }) %></ul>"); var escapedCharsInJavaScriptTemplate = _.template('<ul><% _.each(numbers.split("\\n"), function(item) { %><li><%= item %></li><% }) %></ul>');
result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); result = escapedCharsInJavaScriptTemplate({numbers: 'one\ntwo\nthree\nfour'});
equal(result, "<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>", 'Can use escaped characters (e.g. \\n) in Javascript'); assert.strictEqual(result, '<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>', 'Can use escaped characters (e.g. \\n) in JavaScript');
var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class=\"thumbnail\" rel=\"<%= p %>\"></div><% }); %>"); var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class="thumbnail" rel="<%= p %>"></div><% }); %>');
result = namespaceCollisionTemplate({ result = namespaceCollisionTemplate({
pageCount: 3, pageCount: 3,
thumbnails: { thumbnails: {
1: "p1-thumbnail.gif", 1: 'p1-thumbnail.gif',
2: "p2-thumbnail.gif", 2: 'p2-thumbnail.gif',
3: "p3-thumbnail.gif" 3: 'p3-thumbnail.gif'
} }
}); });
equal(result, "3 p3-thumbnail.gif <div class=\"thumbnail\" rel=\"p1-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p2-thumbnail.gif\"></div><div class=\"thumbnail\" rel=\"p3-thumbnail.gif\"></div>"); assert.strictEqual(result, '3 p3-thumbnail.gif <div class="thumbnail" rel="p1-thumbnail.gif"></div><div class="thumbnail" rel="p2-thumbnail.gif"></div><div class="thumbnail" rel="p3-thumbnail.gif"></div>');
var noInterpolateTemplate = _.template("<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>"); var noInterpolateTemplate = _.template('<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>');
result = noInterpolateTemplate(); result = noInterpolateTemplate();
equal(result, "<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>"); assert.strictEqual(result, '<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>');
var quoteTemplate = _.template("It's its, not it's"); var quoteTemplate = _.template("It's its, not it's");
equal(quoteTemplate({}), "It's its, not it's"); assert.strictEqual(quoteTemplate({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("<%\ var quoteInStatementAndBody = _.template('<% ' +
if(foo == 'bar'){ \ " if(foo == 'bar'){ " +
%>Statement quotes and 'quotes'.<% } %>"); "%>Statement quotes and 'quotes'.<% } %>");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); assert.strictEqual(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.');
equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); assert.strictEqual(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
var template = _.template("<i><%- value %></i>"); var template = _.template('<i><%- value %></i>');
var result = template({value: "<script>"}); result = template({value: '<script>'});
equal(result, '<i>&lt;script&gt;</i>'); assert.strictEqual(result, '<i>&lt;script&gt;</i>');
var stooge = { var stooge = {
name: "Moe", name: 'Moe',
template: _.template("I'm <%= this.name %>") template: _.template("I'm <%= this.name %>")
}; };
equal(stooge.template(), "I'm Moe"); assert.strictEqual(stooge.template(), "I'm Moe");
if (!$.browser.msie) { template = _.template('\n ' +
var fromHTML = _.template($('#template').html()); ' <%\n ' +
equal(fromHTML({data : 12345}).replace(/\s/g, ''), '<li>24690</li>'); ' // a comment\n ' +
} ' if (data) { data += 12345; }; %>\n ' +
' <li><%= data %></li>\n '
);
assert.strictEqual(template({data: 12345}).replace(/\s/g, ''), '<li>24690</li>');
_.templateSettings = { _.templateSettings = {
evaluate : /\{\{([\s\S]+?)\}\}/g, evaluate: /\{\{([\s\S]+?)\}\}/g,
interpolate : /\{\{=([\s\S]+?)\}\}/g interpolate: /\{\{=([\s\S]+?)\}\}/g
}; };
var custom = _.template("<ul>{{ for (var key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>"); var custom = _.template('<ul>{{ for (var key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>');
result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); result = custom({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates'); assert.strictEqual(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customQuote = _.template("It's its, not it's"); var customQuote = _.template("It's its, not it's");
equal(customQuote({}), "It's its, not it's"); assert.strictEqual(customQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}"); quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); assert.strictEqual(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = { _.templateSettings = {
evaluate : /<\?([\s\S]+?)\?>/g, evaluate: /<\?([\s\S]+?)\?>/g,
interpolate : /<\?=([\s\S]+?)\?>/g interpolate: /<\?=([\s\S]+?)\?>/g
}; };
var customWithSpecialChars = _.template("<ul><? for (var key in people) { ?><li><?= people[key] ?></li><? } ?></ul>"); var customWithSpecialChars = _.template('<ul><? for (var key in people) { ?><li><?= people[key] ?></li><? } ?></ul>');
result = customWithSpecialChars({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); result = customWithSpecialChars({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, "<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>", 'can run arbitrary javascript in templates'); assert.strictEqual(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customWithSpecialCharsQuote = _.template("It's its, not it's"); var customWithSpecialCharsQuote = _.template("It's its, not it's");
equal(customWithSpecialCharsQuote({}), "It's its, not it's"); assert.strictEqual(customWithSpecialCharsQuote({}), "It's its, not it's");
var quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>"); quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); assert.strictEqual(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = { _.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g interpolate: /\{\{(.+?)\}\}/g
}; };
var mustache = _.template("Hello {{planet}}!"); var mustache = _.template('Hello {{planet}}!');
equal(mustache({planet : "World"}), "Hello World!", "can mimic mustache.js"); assert.strictEqual(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js');
var templateWithNull = _.template("a null undefined {{planet}}"); var templateWithNull = _.template('a null undefined {{planet}}');
equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings"); assert.strictEqual(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings');
}); });
test('_.template provides the generated function source, when a SyntaxError occurs', function() { QUnit.test('_.template provides the generated function source, when a SyntaxError occurs', function(assert) {
var source;
try { try {
_.template('<b><%= if x %></b>'); _.template('<b><%= if x %></b>');
} catch (ex) { } catch (ex) {
var source = ex.source; source = ex.source;
} }
ok(/__p/.test(source)); assert.ok(/__p/.test(source));
}); });
test('_.template handles \\u2028 & \\u2029', function() { QUnit.test('_.template handles \\u2028 & \\u2029', function(assert) {
var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>'); var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>');
strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>'); assert.strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>');
}); });
test('result calls functions and returns primitives', function() { QUnit.test('result calls functions and returns primitives', function(assert) {
var obj = {w: '', x: 'x', y: function(){ return this.x; }}; var obj = {w: '', x: 'x', y: function(){ return this.x; }};
strictEqual(_.result(obj, 'w'), ''); assert.strictEqual(_.result(obj, 'w'), '');
strictEqual(_.result(obj, 'x'), 'x'); assert.strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x'); assert.strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), undefined); assert.strictEqual(_.result(obj, 'z'), void 0);
strictEqual(_.result(null, 'x'), undefined); assert.strictEqual(_.result(null, 'x'), void 0);
}); });
test('_.templateSettings.variable', function() { QUnit.test('result returns a default value if object is null or undefined', function(assert) {
assert.strictEqual(_.result(null, 'b', 'default'), 'default');
assert.strictEqual(_.result(void 0, 'c', 'default'), 'default');
assert.strictEqual(_.result(''.match('missing'), 1, 'default'), 'default');
});
QUnit.test('result returns a default value if property of object is missing', function(assert) {
assert.strictEqual(_.result({d: null}, 'd', 'default'), null);
assert.strictEqual(_.result({e: false}, 'e', 'default'), false);
});
QUnit.test('result only returns the default value if the object does not have the property or is undefined', function(assert) {
assert.strictEqual(_.result({}, 'b', 'default'), 'default');
assert.strictEqual(_.result({d: void 0}, 'd', 'default'), 'default');
});
QUnit.test('result does not return the default if the property of an object is found in the prototype', function(assert) {
var Foo = function(){};
Foo.prototype.bar = 1;
assert.strictEqual(_.result(new Foo, 'bar', 2), 1);
});
QUnit.test('result does use the fallback when the result of invoking the property is undefined', function(assert) {
var obj = {a: function() {}};
assert.strictEqual(_.result(obj, 'a', 'failed'), void 0);
});
QUnit.test('result fallback can use a function', function(assert) {
var obj = {a: [1, 2, 3]};
assert.strictEqual(_.result(obj, 'b', _.constant(5)), 5);
assert.strictEqual(_.result(obj, 'b', function() {
return this.a;
}), obj.a, 'called with context');
});
QUnit.test('result can accept an array of properties for deep access', function(assert) {
var func = function() { return 'f'; };
var context = function() { return this; };
assert.strictEqual(_.result({a: 1}, 'a'), 1, 'can get a direct property');
assert.strictEqual(_.result({a: {b: 2}}, ['a', 'b']), 2, 'can get a nested property');
assert.strictEqual(_.result({a: 1}, 'b', 2), 2, 'uses the fallback value when property is missing');
assert.strictEqual(_.result({a: 1}, ['b', 'c'], 2), 2, 'uses the fallback value when any property is missing');
assert.strictEqual(_.result({a: void 0}, ['a'], 1), 1, 'uses the fallback when value is undefined');
assert.strictEqual(_.result({a: false}, ['a'], 'foo'), false, 'can fetch falsy values');
assert.strictEqual(_.result({a: func}, 'a'), 'f', 'can get a direct method');
assert.strictEqual(_.result({a: {b: func}}, ['a', 'b']), 'f', 'can get a nested method');
assert.strictEqual(_.result(), void 0, 'returns undefined if obj is not passed');
assert.strictEqual(_.result(void 1, 'a', 2), 2, 'returns default if obj is not passed');
assert.strictEqual(_.result(void 1, 'a', func), 'f', 'executes default if obj is not passed');
assert.strictEqual(_.result({}, void 0, 2), 2, 'returns default if prop is not passed');
assert.strictEqual(_.result({}, void 0, func), 'f', 'executes default if prop is not passed');
var childObj = {c: context};
var obj = {a: context, b: childObj};
assert.strictEqual(_.result(obj, 'a'), obj, 'uses the parent object as context');
assert.strictEqual(_.result(obj, 'e', context), obj, 'uses the object as context when executing the fallback');
assert.strictEqual(_.result(obj, ['a', 'x'], context), obj, 'uses the object as context when executing the fallback');
assert.strictEqual(_.result(obj, ['b', 'c']), childObj, 'uses the parent as context when accessing deep methods');
assert.strictEqual(_.result({}, [], 'a'), 'a', 'returns the default when prop is empty');
assert.strictEqual(_.result(obj, [], context), obj, 'uses the object as context when path is empty');
var nested = {
d: function() {
return {
e: function() {
return obj;
},
f: context
};
}
};
assert.strictEqual(_.result(nested, ['d', 'e']), obj, 'can unpack nested function calls');
assert.strictEqual(_.result(nested, ['d', 'f']).e(), obj, 'uses parent as context for nested function calls');
assert.strictEqual(_.result(nested, ['d', 'x'], context).e(), obj, 'uses last valid child as context for fallback');
if (typeof Symbol !== 'undefined') {
var x = Symbol('x');
var symbolObject = {};
symbolObject[x] = 'foo';
assert.strictEqual(_.result(symbolObject, x), 'foo', 'can use symbols as keys');
var y = Symbol('y');
symbolObject[y] = {};
symbolObject[y][x] = 'bar';
assert.strictEqual(_.result(symbolObject, [y, x]), 'bar', 'can use symbols as keys for deep matching');
}
});
QUnit.test('_.templateSettings.variable', function(assert) {
var s = '<%=data.x%>'; var s = '<%=data.x%>';
var data = {x: 'x'}; var data = {x: 'x'};
strictEqual(_.template(s, data, {variable: 'data'}), 'x'); var tmp = _.template(s, {variable: 'data'});
assert.strictEqual(tmp(data), 'x');
_.templateSettings.variable = 'data'; _.templateSettings.variable = 'data';
strictEqual(_.template(s)(data), 'x'); assert.strictEqual(_.template(s)(data), 'x');
}); });
test('#547 - _.templateSettings is unchanged by custom settings.', function() { QUnit.test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) {
ok(!_.templateSettings.variable); assert.notOk(_.templateSettings.variable);
_.template('', {}, {variable: 'x'}); _.template('', {}, {variable: 'x'});
ok(!_.templateSettings.variable); assert.notOk(_.templateSettings.variable);
}); });
test('#556 - undefined template variables.', function() { QUnit.test('#556 - undefined template variables.', function(assert) {
var template = _.template('<%=x%>'); var template = _.template('<%=x%>');
strictEqual(template({x: null}), ''); assert.strictEqual(template({x: null}), '');
strictEqual(template({x: undefined}), ''); assert.strictEqual(template({x: void 0}), '');
var templateEscaped = _.template('<%-x%>'); var templateEscaped = _.template('<%-x%>');
strictEqual(templateEscaped({x: null}), ''); assert.strictEqual(templateEscaped({x: null}), '');
strictEqual(templateEscaped({x: undefined}), ''); assert.strictEqual(templateEscaped({x: void 0}), '');
var templateWithProperty = _.template('<%=x.foo%>'); var templateWithProperty = _.template('<%=x.foo%>');
strictEqual(templateWithProperty({x: {} }), ''); assert.strictEqual(templateWithProperty({x: {}}), '');
strictEqual(templateWithProperty({x: {} }), ''); assert.strictEqual(templateWithProperty({x: {}}), '');
var templateWithPropertyEscaped = _.template('<%-x.foo%>'); var templateWithPropertyEscaped = _.template('<%-x.foo%>');
strictEqual(templateWithPropertyEscaped({x: {} }), ''); assert.strictEqual(templateWithPropertyEscaped({x: {}}), '');
strictEqual(templateWithPropertyEscaped({x: {} }), ''); assert.strictEqual(templateWithPropertyEscaped({x: {}}), '');
}); });
test('interpolate evaluates code only once.', 2, function() { QUnit.test('interpolate evaluates code only once.', function(assert) {
assert.expect(2);
var count = 0; var count = 0;
var template = _.template('<%= f() %>'); var template = _.template('<%= f() %>');
template({f: function(){ ok(!(count++)); }}); template({f: function(){ assert.notOk(count++); }});
var countEscaped = 0; var countEscaped = 0;
var templateEscaped = _.template('<%- f() %>'); var templateEscaped = _.template('<%- f() %>');
templateEscaped({f: function(){ ok(!(countEscaped++)); }}); templateEscaped({f: function(){ assert.notOk(countEscaped++); }});
}); });
test('#746 - _.template settings are not modified.', 1, function() { QUnit.test('#746 - _.template settings are not modified.', function(assert) {
assert.expect(1);
var settings = {}; var settings = {};
_.template('', null, settings); _.template('', null, settings);
deepEqual(settings, {}); assert.deepEqual(settings, {});
}); });
test('#779 - delimeters are applied to unescaped text.', 1, function() { QUnit.test('#779 - delimiters are applied to unescaped text.', function(assert) {
assert.expect(1);
var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g});
strictEqual(template(), '<<\nx\n>>'); assert.strictEqual(template(), '<<\nx\n>>');
}); });
}); }());

File diff suppressed because it is too large Load diff

View file

@ -1,670 +0,0 @@
// JSLitmus.js
//
// History:
// 2008-10-27: Initial release
// 2008-11-09: Account for iteration loop overhead
// 2008-11-13: Added OS detection
// 2009-02-25: Create tinyURL automatically, shift-click runs tests in reverse
//
// Copyright (c) 2008-2009, Robert Kieffer
// All Rights Reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the
// Software), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
(function() {
// Private methods and state
// Get platform info but don't go crazy trying to recognize everything
// that's out there. This is just for the major platforms and OSes.
var platform = 'unknown platform', ua = navigator.userAgent;
// Detect OS
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
// Detect browser
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
// Detect version
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
/**
* A smattering of methods that are needed to implement the JSLitmus testbed.
*/
var jsl = {
/**
* Enhanced version of escape()
*/
escape: function(s) {
s = s.replace(/,/g, '\\,');
s = escape(s);
s = s.replace(/\+/g, '%2b');
s = s.replace(/ /g, '+');
return s;
},
/**
* Get an element by ID.
*/
$: function(id) {
return document.getElementById(id);
},
/**
* Null function
*/
F: function() {},
/**
* Set the status shown in the UI
*/
status: function(msg) {
var el = jsl.$('jsl_status');
if (el) el.innerHTML = msg || '';
},
/**
* Convert a number to an abbreviated string like, "15K" or "10M"
*/
toLabel: function(n) {
if (n == Infinity) {
return 'Infinity';
} else if (n > 1e9) {
n = Math.round(n/1e8);
return n/10 + 'B';
} else if (n > 1e6) {
n = Math.round(n/1e5);
return n/10 + 'M';
} else if (n > 1e3) {
n = Math.round(n/1e2);
return n/10 + 'K';
}
return n;
},
/**
* Copy properties from src to dst
*/
extend: function(dst, src) {
for (var k in src) dst[k] = src[k]; return dst;
},
/**
* Like Array.join(), but for the key-value pairs in an object
*/
join: function(o, delimit1, delimit2) {
if (o.join) return o.join(delimit1); // If it's an array
var pairs = [];
for (var k in o) pairs.push(k + delimit1 + o[k]);
return pairs.join(delimit2);
},
/**
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
*/
indexOf: function(arr, o) {
if (arr.indexOf) return arr.indexOf(o);
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
return -1;
}
};
/**
* Test manages a single test (created with
* JSLitmus.test())
*
* @private
*/
var Test = function (name, f) {
if (!f) throw new Error('Undefined test function');
if (!(/function[^\(]*\(([^,\)]*)/).test(f.toString())) {
throw new Error('"' + name + '" test: Test is not a valid Function object');
}
this.loopArg = RegExp.$1;
this.name = name;
this.f = f;
};
jsl.extend(Test, /** @lends Test */ {
/** Calibration tests for establishing iteration loop overhead */
CALIBRATIONS: [
new Test('calibrating loop', function(count) {while (count--);}),
new Test('calibrating function', jsl.F)
],
/**
* Run calibration tests. Returns true if calibrations are not yet
* complete (in which case calling code should run the tests yet again).
* onCalibrated - Callback to invoke when calibrations have finished
*/
calibrate: function(onCalibrated) {
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
var cal = Test.CALIBRATIONS[i];
if (cal.running) return true;
if (!cal.count) {
cal.isCalibration = true;
cal.onStop = onCalibrated;
//cal.MIN_TIME = .1; // Do calibrations quickly
cal.run(2e4);
return true;
}
}
return false;
}
});
jsl.extend(Test.prototype, {/** @lends Test.prototype */
/** Initial number of iterations */
INIT_COUNT: 10,
/** Max iterations allowed (i.e. used to detect bad looping functions) */
MAX_COUNT: 1e9,
/** Minimum time a test should take to get valid results (secs) */
MIN_TIME: .5,
/** Callback invoked when test state changes */
onChange: jsl.F,
/** Callback invoked when test is finished */
onStop: jsl.F,
/**
* Reset test state
*/
reset: function() {
delete this.count;
delete this.time;
delete this.running;
delete this.error;
},
/**
* Run the test (in a timeout). We use a timeout to make sure the browser
* has a chance to finish rendering any UI changes we've made, like
* updating the status message.
*/
run: function(count) {
count = count || this.INIT_COUNT;
jsl.status(this.name + ' x ' + count);
this.running = true;
var me = this;
setTimeout(function() {me._run(count);}, 200);
},
/**
* The nuts and bolts code that actually runs a test
*/
_run: function(count) {
var me = this;
// Make sure calibration tests have run
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
this.error = null;
try {
var start, f = this.f, now, i = count;
// Start the timer
start = new Date();
// Now for the money shot. If this is a looping function ...
if (this.loopArg) {
// ... let it do the iteration itself
f(count);
} else {
// ... otherwise do the iteration for it
while (i--) f();
}
// Get time test took (in secs)
this.time = Math.max(1,new Date() - start)/1000;
// Store iteration count and per-operation time taken
this.count = count;
this.period = this.time/count;
// Do we need to do another run?
this.running = this.time <= this.MIN_TIME;
// ... if so, compute how many times we should iterate
if (this.running) {
// Bump the count to the nearest power of 2
var x = this.MIN_TIME/this.time;
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
count *= pow;
if (count > this.MAX_COUNT) {
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
}
}
} catch (e) {
// Exceptions are caught and displayed in the test UI
this.reset();
this.error = e;
}
// Figure out what to do next
if (this.running) {
me.run(count);
} else {
jsl.status('');
me.onStop(me);
}
// Finish up
this.onChange(this);
},
/**
* Get the number of operations per second for this test.
*
* @param normalize if true, iteration loop overhead taken into account
*/
getHz: function(/**Boolean*/ normalize) {
var p = this.period;
// Adjust period based on the calibration test time
if (normalize && !this.isCalibration) {
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
// If the period is within 20% of the calibration time, then zero the
// it out
p = p < cal.period*1.2 ? 0 : p - cal.period;
}
return Math.round(1/p);
},
/**
* Get a friendly string describing the test
*/
toString: function() {
return this.name + ' - ' + this.time/this.count + ' secs';
}
});
// CSS we need for the UI
var STYLESHEET = '<style> \
#jslitmus {font-family:sans-serif; font-size: 12px;} \
#jslitmus a {text-decoration: none;} \
#jslitmus a:hover {text-decoration: underline;} \
#jsl_status { \
margin-top: 10px; \
font-size: 10px; \
color: #888; \
} \
A IMG {border:none} \
#test_results { \
margin-top: 10px; \
font-size: 12px; \
font-family: sans-serif; \
border-collapse: collapse; \
border-spacing: 0px; \
} \
#test_results th, #test_results td { \
border: solid 1px #ccc; \
vertical-align: top; \
padding: 3px; \
} \
#test_results th { \
vertical-align: bottom; \
background-color: #ccc; \
padding: 1px; \
font-size: 10px; \
} \
#test_results #test_platform { \
color: #444; \
text-align:center; \
} \
#test_results .test_row { \
color: #006; \
cursor: pointer; \
} \
#test_results .test_nonlooping { \
border-left-style: dotted; \
border-left-width: 2px; \
} \
#test_results .test_looping { \
border-left-style: solid; \
border-left-width: 2px; \
} \
#test_results .test_name {white-space: nowrap;} \
#test_results .test_pending { \
} \
#test_results .test_running { \
font-style: italic; \
} \
#test_results .test_done {} \
#test_results .test_done { \
text-align: right; \
font-family: monospace; \
} \
#test_results .test_error {color: #600;} \
#test_results .test_error .error_head {font-weight:bold;} \
#test_results .test_error .error_body {font-size:85%;} \
#test_results .test_row:hover td { \
background-color: #ffc; \
text-decoration: underline; \
} \
#chart { \
margin: 10px 0px; \
width: 250px; \
} \
#chart img { \
border: solid 1px #ccc; \
margin-bottom: 5px; \
} \
#chart #tiny_url { \
height: 40px; \
width: 250px; \
} \
#jslitmus_credit { \
font-size: 10px; \
color: #888; \
margin-top: 8px; \
} \
</style>';
// HTML markup for the UI
var MARKUP = '<div id="jslitmus"> \
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
<br \> \
<br \> \
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
<table id="test_results"> \
<colgroup> \
<col /> \
<col width="100" /> \
</colgroup> \
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
<tr><th>Test</th><th>Ops/sec</th></tr> \
<tr id="test_row_template" class="test_row" style="display:none"> \
<td class="test_name"></td> \
<td class="test_result">Ready</td> \
</tr> \
</table> \
<div id="jsl_status"></div> \
<div id="chart" style="display:none"> \
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
TinyURL (for chart): \
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
</div> \
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
</div>';
/**
* The public API for creating and running tests
*/
window.JSLitmus = {
/** The list of all tests that have been registered with JSLitmus.test */
_tests: [],
/** The queue of tests that need to be run */
_queue: [],
/**
* The parsed query parameters the current page URL. This is provided as a
* convenience for test functions - it's not used by JSLitmus proper
*/
params: {},
/**
* Initialize
*/
_init: function() {
// Parse query params into JSLitmus.params[] hash
var match = (location + '').match(/([^?#]*)(#.*)?$/);
if (match) {
var pairs = match[1].split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if (pair.length > 1) {
var key = pair.shift();
var value = pair.length > 1 ? pair.join('=') : pair[0];
this.params[key] = value;
}
}
}
// Write out the stylesheet. We have to do this here because IE
// doesn't honor sheets written after the document has loaded.
document.write(STYLESHEET);
// Setup the rest of the UI once the document is loaded
if (window.addEventListener) {
window.addEventListener('load', this._setup, false);
} else if (document.addEventListener) {
document.addEventListener('load', this._setup, false);
} else if (window.attachEvent) {
window.attachEvent('onload', this._setup);
}
return this;
},
/**
* Set up the UI
*/
_setup: function() {
var el = jsl.$('jslitmus_container');
if (!el) document.body.appendChild(el = document.createElement('div'));
el.innerHTML = MARKUP;
// Render the UI for all our tests
for (var i=0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
},
/**
* (Re)render all the test results
*/
renderAll: function() {
for (var i = 0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
JSLitmus.renderChart();
},
/**
* (Re)render the chart graphics
*/
renderChart: function() {
var url = JSLitmus.chartUrl();
jsl.$('chart_link').href = url;
jsl.$('chart_image').src = url;
jsl.$('chart').style.display = '';
// Update the tiny URL
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
},
/**
* (Re)render the results for a specific test
*/
renderTest: function(test) {
// Make a new row if needed
if (!test._row) {
var trow = jsl.$('test_row_template');
if (!trow) return;
test._row = trow.cloneNode(true);
test._row.style.display = '';
test._row.id = '';
test._row.onclick = function() {JSLitmus._queueTest(test);};
test._row.title = 'Run ' + test.name + ' test';
trow.parentNode.appendChild(test._row);
test._row.cells[0].innerHTML = test.name;
}
var cell = test._row.cells[1];
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
if (test.error) {
cns.push('test_error');
cell.innerHTML =
'<div class="error_head">' + test.error + '</div>' +
'<ul class="error_body"><li>' +
jsl.join(test.error, ': ', '</li><li>') +
'</li></ul>';
} else {
if (test.running) {
cns.push('test_running');
cell.innerHTML = 'running';
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
cns.push('test_pending');
cell.innerHTML = 'pending';
} else if (test.count) {
cns.push('test_done');
var hz = test.getHz(jsl.$('test_normalize').checked);
cell.innerHTML = hz != Infinity ? hz : '&infin;';
} 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();
})();

View file

@ -0,0 +1,776 @@
/*!
* QUnit Extras v1.4.1
* Copyright 2011-2015 John-David Dalton <http://allyoucanleet.com/>
* Based on a gist by Jörn Zaefferer <https://gist.github.com/722381>
* Available under MIT license <http://mths.be/mit>
*/
;(function() {
/** Used as a safe reference for `undefined` in pre ES5 environments. */
var undefined;
/** Used as a horizontal rule in console output. */
var hr = '----------------------------------------';
/** Used for native method references. */
var arrayProto = Array.prototype;
/** Native method shortcut. */
var push = arrayProto.push,
unshift = arrayProto.unshift;
/** Used to match HTML entities. */
var reEscapedHtml = /(&amp;|&lt;|&gt;|&quot;|&#39;)/g;
/** Used to match parts of the assert message. */
var reDied = /^Died on test #\d+/,
reMessage = /^<span class='test-message'>([\s\S]*?)<\/span>/;
/** Used to associate color names with their corresponding codes. */
var ansiCodes = {
'bold': 1,
'green': 32,
'magenta': 35,
'red': 31
};
/** Used to convert HTML entities to characters. */
var htmlUnescapes = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'"
};
/** Used to determine if values are of the language type Object. */
var objectTypes = {
'function': true,
'object': true
};
/** Used as a reference to the global object. */
var root = (objectTypes[typeof window] && window) || this;
/** Detect free variable `exports`. */
var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
/** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */
var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
root = freeGlobal;
}
/*--------------------------------------------------------------------------*/
/**
* Checks if a given value is present in an array using strict equality
* for comparisons, i.e. `===`.
*
* @private
* @param {Array} array The array to iterate over.
* @param {*} value The value to check for.
* @returns {boolean} Returns `true` if the `value` is found, else `false`.
*/
function contains(array, value) {
var index = -1,
length = array ? array.length : 0;
while (++index < length) {
if (array[index] === value) {
return true;
}
}
return false;
}
/**
* Checks if `value` is the language type of `Object`.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
*/
function isObject(value) {
var type = typeof value;
return type == 'function' || (value && type == 'object') || false;
}
/**
* Creates a string with `text` repeated `n` number of times.
*
* @private
* @param {string} text The text to repeat.
* @param {number} n The number of times to repeat `text`.
* @returns {string} The created string.
*/
function repeat(text, n) {
return Array(n + 1).join(text);
}
/**
* Resolves the value of property `key` on `object`.
*
* @private
* @param {Object} object The object to inspect.
* @param {string} key The name of the property to resolve.
* @returns {*} Returns the resolved value.
*/
function result(object, key) {
return object == null ? undefined : object[key];
}
/**
* Converts the HTML entities `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;`
* in `string` to their corresponding characters.
*
* @private
* @param {string} string The string to unescape.
* @returns {string} Returns the unescaped string.
*/
function unescape(string) {
return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
}
/**
* Used by `unescape` to convert HTML entities to characters.
*
* @private
* @param {string} match The matched character to unescape.
* @returns {string} Returns the unescaped character.
*/
function unescapeHtmlChar(match) {
return htmlUnescapes[match];
}
/**
* Creates a function that provides `value` to the wrapper function as its
* first argument. Additional arguments provided to the function are appended
* to those provided to the wrapper function. The wrapper is executed with
* the `this` binding of the created function.
*
* @private
* @param {*} value The value to wrap.
* @param {Function} wrapper The wrapper function.
* @returns {Function} Returns the new function.
*/
function wrap(value, wrapper) {
return function() {
var args = [value];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
}
/*--------------------------------------------------------------------------*/
/**
* Installs the QUnit additions on the given `context` object.
*
* @memberOf exports
* @param {Object} context The context object.
*/
function runInContext(context) {
/** Object references. */
var phantom = context.phantom,
define = context.define,
document = !phantom && context.document,
process = phantom || context.process,
amd = define && define.amd,
console = context.console,
java = !document && context.java,
print = context.print,
require = context.require;
/** Detects if running on Node.js. */
var isNode = isObject(process) && typeof process.on == 'function';
/** Detects if running in a PhantomJS web page. */
var isPhantomPage = typeof context.callPhantom == 'function';
/** Detects if QUnit Extras should log to the console. */
var isSilent = document && !isPhantomPage;
/** Used to indicate if running in Windows. */
var isWindows = isNode && process.platform == 'win32';
/** Used to indicate if ANSI escape codes are supported. */
var isAnsiSupported = (function() {
if (isNode && process.stdout && !process.stdout.isTTY) {
return false;
}
if (isWindows || getEnv('COLORTERM')) {
return true;
}
return /^(?:ansi|cygwin|linux|screen|xterm|vt100)$|color/i.test(getEnv('TERM'));
}());
/** Used to display the wait throbber. */
var throbberDelay = 500,
waitCount = -1;
/** Shorten `context.QUnit.QUnit` to `context.QUnit`. */
var QUnit = context.QUnit = context.QUnit.QUnit || context.QUnit;
/*------------------------------------------------------------------------*/
/**
* Schedules timer-based callbacks.
*
* @private
* @param {Function|string} fn The function to call.
* @param {number} delay The number of milliseconds to delay the `fn` call.
* @param {Array} args Arguments to invoke `fn` with.
* @param {boolean} repeated A flag to specify whether `fn` is called repeatedly.
* @returns {number} The ID of the timeout.
*/
function schedule(fn, delay, args, repeated) {
// Rhino 1.7RC4 will error assigning `task` below.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=775566.
var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, {
'run': function() {
fn.apply(context, args);
}
});
// Support non-functions.
if (typeof fn != 'function') {
fn = (function(code) {
code = String(code);
return function() { eval(code); };
}(fn));
}
// Used by `setInterval`.
if (repeated) {
timer.schedule(task, delay, delay);
}
// Used by `setTimeout`.
else {
timer.schedule(task, delay);
}
return counter;
}
/**
* Clears the delay set by `setInterval` or `setTimeout`.
*
* @memberOf context
* @param {number} id The ID of the timeout to be cleared.
*/
function clearTimer(id) {
if (ids[id]) {
ids[id].cancel();
timer.purge();
delete ids[id];
}
}
/**
* Executes a code snippet or function repeatedly, with a delay between each call.
*
* @memberOf context
* @param {Function|string} fn The function to call or string to evaluate.
* @param {number} delay The number of milliseconds to delay each `fn` call.
* @param {...*} [args] Arguments to invoke `fn` with.
* @returns {number} The ID of the timeout.
*/
function setInterval(fn, delay) {
return schedule(fn, delay, slice.call(arguments, 2), true);
}
/**
* Executes a code snippet or a function after specified delay.
*
* @memberOf context
* @param {Function|string} fn The function to call or string to evaluate.
* @param {number} delay The number of milliseconds to delay the `fn` call.
* @param {...*} [args] Arguments to invoke `fn` with.
* @returns {number} The ID of the timeout.
*/
function setTimeout(fn, delay) {
return schedule(fn, delay, slice.call(arguments, 2));
}
/*------------------------------------------------------------------------*/
/**
* Gets the environment variable value by a given name.
*
* @private
* @param {string} name The name of the environment variable to get.
* @returns {*} Returns the environment variable value.
*/
function getEnv(name) {
if (isNode) {
return process.env[name];
}
if (java) {
return java.lang.System.getenv(name);
}
if (!amd && typeof require == 'function') {
try {
return require('system').env[name];
} catch(e) {}
}
}
/**
* Adds text color to the terminal output of `string`.
*
* @private
* @param {string} colorName The name of the color to add.
* @param {string} string The string to add colors to.
* @returns {string} Returns the colored string.
*/
function color(colorName, string) {
return isAnsiSupported
? ('\x1B[' + ansiCodes[colorName] + 'm' + string + '\x1B[0m')
: string;
}
/**
* Writes an inline message to standard output.
*
* @private
* @param {string} [text=''] The text to log.
*/
var logInline = (function() {
if (!isNode || isWindows) {
return function() {};
}
// Cleanup any inline logs when exited via `ctrl+c`.
process.on('SIGINT', function() {
logInline();
process.exit();
});
var prevLine = '';
return function(text) {
var blankLine = repeat(' ', prevLine.length);
if (text == null) {
text = '';
}
if (text.length > hr.length) {
text = text.slice(0, hr.length - 3) + '...';
}
prevLine = text;
process.stdout.write(text + blankLine.slice(text.length) + '\r');
}
}());
/**
* Writes the wait throbber to standard output.
*
* @private
*/
function logThrobber() {
logInline('Please wait' + repeat('.', (++waitCount % 3) + 1));
}
/*------------------------------------------------------------------------*/
/**
* The number of retries async tests have to succeed.
*
* @memberOf QUnit.config
* @type number
*/
QUnit.config.asyncRetries = 0;
/**
* An object of excused tests and assertions.
*
* @memberOf QUnit.config
* @type Object
*/
QUnit.config.excused = {};
/**
* An object used to hold "extras" information about the current running test.
*
* @memberOf QUnit.config
* @type Object
*/
QUnit.config.extrasData = {
/**
* The data object for the active test module.
*
* @memberOf QUnit.config.extrasData
* @type Object
*/
'module': {},
/**
* The data object for Sauce Labs.
*
* @memberOf QUnit.config.extrasData
* @type Object
*/
'sauce': {
/**
* An array of failed test details.
*
* @memberOf QUnit.config.extrasData.sauce
* @type Array
*/
'tests': []
}
};
/**
* Converts an object into a string representation.
*
* @memberOf QUnit
* @type Function
* @param {Object} object The object to stringify.
* @returns {string} The result string.
*/
QUnit.jsDump.parsers.object = (function() {
var func = QUnit.jsDump.parsers.object;
if (isSilent) {
return func;
}
return function(object) {
if (typeof object.rhinoException != 'object') {
return func(object);
}
return object.name +
' { message: "' + object.message +
'", fileName: "' + object.fileName +
'", lineNumber: ' + object.lineNumber + ' }';
};
}());
/*------------------------------------------------------------------------*/
// Add a callback to be triggered after every assertion.
QUnit.log(function(details) {
QUnit.config.extrasData.module.logs.push(details);
});
// Add a callback to be triggered at the start of every test module.
QUnit.moduleStart(function(details) {
var module = QUnit.config.extrasData.module;
module.name = details.name;
module.logs = [];
module.printed = false;
});
// Wrap old API to intercept `expected` and `message`.
if (QUnit.push) {
QUnit.push = wrap(QUnit.push, function(push, result, actual, expected, message) {
push.call(this, result, actual, expected, message);
var asserts = QUnit.config.current.assertions,
item = asserts[asserts.length - 1];
item.expected = QUnit.jsDump.parse(expected);
item.text = message;
});
}
// Wrap old API to intercept `message`.
if (QUnit.pushFailure) {
QUnit.pushFailure = wrap(QUnit.pushFailure, function(pushFailure, message, source, actual) {
pushFailure.call(this, message, source, actual);
var asserts = QUnit.config.current.assertions,
item = asserts[asserts.length - 1];
item.expected = '';
item.text = message;
});
}
// Wrap to flag tests using `assert.async`.
if (QUnit.assert.async) {
QUnit.assert.async = wrap(QUnit.assert.async, function(async) {
this.test.usesAsync = true;
return async.call(this);
});
}
// Add a callback to be triggered at the start of every test.
QUnit.testStart(function(details) {
var config = QUnit.config,
test = config.current;
var excused = config.excused || {},
excusedTests = excused[details.module],
excusedAsserts = excusedTests && excusedTests[details.name];
// Allow async tests to retry.
if (!test.retries) {
test.retries = 0;
test.finish = wrap(test.finish, function(finish) {
if (this.async || this.usesAsync) {
var asserts = this.assertions,
config = QUnit.config,
index = -1,
length = asserts.length,
logs = config.extrasData.module.logs,
queue = config.queue;
while (++index < length) {
var assert = asserts[index];
if (!assert.result && this.retries < config.asyncRetries) {
var oldLength = queue.length;
logs.length -= asserts.length;
asserts.length = 0;
this.retries++;
this.queue();
unshift.apply(queue, queue.splice(oldLength, queue.length - oldLength));
return;
}
}
}
finish.call(this);
});
}
// Exit early when there is nothing to excuse.
if (!excusedAsserts) {
return;
}
// Excuse the entire test.
if (excusedAsserts === true) {
test.async = test.usesAsync = false;
test.callback = function() {};
test.expected = 0;
return;
}
// Wrap to intercept `expected` and `message`.
if (test.push) {
test.push = wrap(test.push, function(push, result, actual, expected, message) {
push.call(this, result, actual, expected, message);
var item = this.assertions[this.assertions.length - 1];
item.expected = QUnit.jsDump.parse(expected);
item.text = message;
});
}
// Wrap to intercept `message`.
if (test.pushFailure) {
test.pushFailure = wrap(test.pushFailure, function(pushFailure, message, source, actual) {
pushFailure.call(this, message, source, actual);
var item = this.assertions[this.assertions.length - 1];
item.expected = '';
item.text = message;
});
}
// Wrap to excuse specific assertions.
test.finish = wrap(test.finish, function(finish) {
var asserts = this.assertions,
config = QUnit.config,
expected = this.expected,
items = asserts.slice(),
length = items.length;
if (expected == null) {
if (config.requireExpects) {
expected = length;
items.push('Expected number of assertions to be defined, but expect() was not called.');
} else if (!length) {
expected = 1;
items.push('Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.');
}
} else if (expected != length) {
items.push('Expected ' + expected + ' assertions, but ' + length + ' were run');
}
var index = -1;
length = items.length;
while (++index < length) {
var assert = items[index],
isStr = typeof assert == 'string';
var assertMessage = isStr ? assert : assert.text || unescape(result(reMessage.exec(assert.message), 1)),
assertValue = isStr ? assert : assert.expected,
assertDied = result(reDied.exec(assertMessage), 0);
if ((assertMessage && contains(excusedAsserts, assertMessage)) ||
(assertDied && contains(excusedAsserts, assertDied)) ||
(assertValue && (
contains(excusedAsserts, assertValue) ||
contains(excusedAsserts, assertValue.replace(/\s+/g, ''))
))) {
if (isStr) {
while (asserts.length < expected) {
asserts.push({ 'result': true });
}
asserts.length = expected;
}
else {
assert.result = true;
}
}
}
finish.call(this);
});
});
// Add a callback to be triggered after a test is completed.
QUnit.testDone(function(details) {
var config = QUnit.config,
data = config.extrasData,
failures = details.failed,
hidepassed = config.hidepassed,
module = data.module,
moduleLogs = module.logs,
sauceTests = data.sauce.tests;
if (hidepassed && !failures) {
return;
}
if (!isSilent) {
logInline();
if (!module.printed) {
module.printed = true;
console.log(hr);
console.log(color('bold', module.name));
console.log(hr);
}
console.log(' ' + (failures ? color('red', 'FAIL') : color('green', 'PASS')) + ' - ' + details.name);
}
if (!failures) {
return;
}
var index = -1,
length = moduleLogs.length;
while(++index < length) {
var entry = moduleLogs[index];
if (hidepassed && entry.result) {
continue;
}
var expected = entry.expected,
result = entry.result,
type = typeof expected != 'undefined' ? 'EQ' : 'OK';
var message = [
result ? color('green', 'PASS') : color('red', 'FAIL'),
type,
entry.message || 'ok'
];
if (!result && type == 'EQ') {
message.push(color('magenta', 'Expected: ' + expected + ', Actual: ' + entry.actual));
}
if (!isSilent) {
console.log(' ' + message.join(' | '));
}
if (!entry.result) {
sauceTests.push(entry);
}
}
});
// Add a callback to be triggered when all testing has completed.
QUnit.done(function(details) {
var failures = details.failed,
statusColor = failures ? 'magenta' : 'green';
if (!isSilent) {
logInline();
console.log(hr);
console.log(color(statusColor, ' PASS: ' + details.passed + ' FAIL: ' + failures + ' TOTAL: ' + details.total));
console.log(color(statusColor, ' Finished in ' + details.runtime + ' milliseconds.'));
console.log(hr);
}
// Exit out of Node.js or PhantomJS.
try {
if (failures) {
process.exit(1);
} else {
process.exit(0);
}
} catch(e) {}
// Exit out of Narwhal, Rhino, or RingoJS.
try {
if (failures) {
java.lang.System.exit(1);
} else {
quit();
}
} catch(e) {}
// Assign results to `global_test_results` for Sauce Labs.
details.tests = QUnit.config.extrasData.sauce.tests;
context.global_test_results = details;
});
/*------------------------------------------------------------------------*/
// Replace poisoned `raises` method.
context.raises = QUnit.raises = QUnit['throws'] || QUnit.raises;
// Add CLI extras.
if (!document) {
// Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
// See https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
// and http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout.
try {
var counter = 0,
ids = {},
slice = Array.prototype.slice,
timer = new java.util.Timer;
(function() {
var getDescriptor = Object.getOwnPropertyDescriptor || function() {
return { 'writable': true };
};
var descriptor;
if ((!context.clearInterval || ((descriptor = getDescriptor(context, 'clearInterval')) && (descriptor.writable || descriptor.set))) &&
(!context.setInterval || ((descriptor = getDescriptor(context, 'setInterval')) && (descriptor.writable || descriptor.set)))) {
context.clearInterval = clearTimer;
context.setInterval = setInterval;
}
if ((!context.clearTimeout || ((descriptor = getDescriptor(context, 'clearTimeout')) && (descriptor.writable || descriptor.set))) &&
(!context.setTimeout || ((descriptor = getDescriptor(context, 'setTimeout')) && (descriptor.writable || descriptor.set)))) {
context.clearTimeout = clearTimer;
context.setTimeout = setTimeout;
}
}());
} catch(e) {}
// Expose QUnit API on `context`.
// Exclude `module` because some environments have it as a built-in object.
('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' +
'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) {
context[methodName] = QUnit[methodName];
});
// Add `console.log` support to Narwhal, Rhino, and RingoJS.
if (!console) {
console = context.console = { 'log': function() {} };
}
// RingoJS removes ANSI escape codes in `console.log`, but not in `print`.
if (java && typeof print == 'function') {
console.log = print;
}
// Start log throbber.
if (!isSilent) {
context.setInterval(logThrobber, throbberDelay);
}
// Must call `QUnit.start` in the test file if not loaded in a browser.
QUnit.config.autostart = false;
QUnit.init();
}
}
/*--------------------------------------------------------------------------*/
// Export QUnit Extras.
if (freeExports) {
freeExports.runInContext = runInContext;
} else {
runInContext(root);
}
}.call(this));

View file

@ -1,11 +1,12 @@
/** /*!
* QUnit v1.12.0 - A JavaScript Unit Testing Framework * QUnit 1.18.0
* http://qunitjs.com/
* *
* http://qunitjs.com * Copyright jQuery Foundation and other contributors
* * Released under the MIT license
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license * http://jquery.org/license
*
* Date: 2015-04-03T10:23Z
*/ */
/** Font Family and Sizes */ /** Font Family and Sizes */
@ -31,32 +32,29 @@
#qunit-header { #qunit-header {
padding: 0.5em 0 0.5em 1em; padding: 0.5em 0 0.5em 1em;
color: #8699a4; color: #8699A4;
background-color: #0d3349; background-color: #0D3349;
font-size: 1.5em; font-size: 1.5em;
line-height: 1em; line-height: 1em;
font-weight: normal; font-weight: 400;
border-radius: 5px 5px 0 0; border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
} }
#qunit-header a { #qunit-header a {
text-decoration: none; text-decoration: none;
color: #c2ccd1; color: #C2CCD1;
} }
#qunit-header a:hover, #qunit-header a:hover,
#qunit-header a:focus { #qunit-header a:focus {
color: #fff; color: #FFF;
} }
#qunit-testrunner-toolbar label { #qunit-testrunner-toolbar label {
display: inline-block; display: inline-block;
padding: 0 .5em 0 .1em; padding: 0 0.5em 0 0.1em;
} }
#qunit-banner { #qunit-banner {
@ -64,21 +62,33 @@
} }
#qunit-testrunner-toolbar { #qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em; padding: 0.5em 1em 0.5em 1em;
color: #5E740B; color: #5E740B;
background-color: #eee; background-color: #EEE;
overflow: hidden; overflow: hidden;
} }
#qunit-userAgent { #qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em; padding: 0.5em 1em 0.5em 1em;
background-color: #2b81af; background-color: #2B81AF;
color: #fff; color: #FFF;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
} }
#qunit-modulefilter-container { #qunit-modulefilter-container {
float: right; float: right;
padding: 0.2em;
}
.qunit-url-config {
display: inline-block;
padding: 0.1em;
}
.qunit-filter {
display: block;
float: right;
margin-left: 1em;
} }
/** Tests: Pass/Fail */ /** Tests: Pass/Fail */
@ -88,24 +98,51 @@
} }
#qunit-tests li { #qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em; padding: 0.4em 1em 0.4em 1em;
border-bottom: 1px solid #fff; border-bottom: 1px solid #FFF;
list-style-position: inside; list-style-position: inside;
} }
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { #qunit-tests > li {
display: none; display: none;
} }
#qunit-tests li.running,
#qunit-tests li.pass,
#qunit-tests li.fail,
#qunit-tests li.skipped {
display: list-item;
}
#qunit-tests.hidepass li.running,
#qunit-tests.hidepass li.pass {
visibility: hidden;
position: absolute;
width: 0px;
height: 0px;
padding: 0;
border: 0;
margin: 0;
}
#qunit-tests li strong { #qunit-tests li strong {
cursor: pointer; cursor: pointer;
} }
#qunit-tests li.skipped strong {
cursor: default;
}
#qunit-tests li a { #qunit-tests li a {
padding: 0.5em; padding: 0.5em;
color: #c2ccd1; color: #C2CCD1;
text-decoration: none; text-decoration: none;
} }
#qunit-tests li p a {
padding: 0.25em;
color: #6B6464;
}
#qunit-tests li a:hover, #qunit-tests li a:hover,
#qunit-tests li a:focus { #qunit-tests li a:focus {
color: #000; color: #000;
@ -120,11 +157,9 @@
margin-top: 0.5em; margin-top: 0.5em;
padding: 0.5em; padding: 0.5em;
background-color: #fff; background-color: #FFF;
border-radius: 5px; border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
} }
.qunit-collapsed { .qunit-collapsed {
@ -133,13 +168,13 @@
#qunit-tests table { #qunit-tests table {
border-collapse: collapse; border-collapse: collapse;
margin-top: .2em; margin-top: 0.2em;
} }
#qunit-tests th { #qunit-tests th {
text-align: right; text-align: right;
vertical-align: top; vertical-align: top;
padding: 0 .5em 0 0; padding: 0 0.5em 0 0;
} }
#qunit-tests td { #qunit-tests td {
@ -153,26 +188,26 @@
} }
#qunit-tests del { #qunit-tests del {
background-color: #e0f2be; background-color: #E0F2BE;
color: #374e0c; color: #374E0C;
text-decoration: none; text-decoration: none;
} }
#qunit-tests ins { #qunit-tests ins {
background-color: #ffcaca; background-color: #FFCACA;
color: #500; color: #500;
text-decoration: none; text-decoration: none;
} }
/*** Test Counts */ /*** Test Counts */
#qunit-tests b.counts { color: black; } #qunit-tests b.counts { color: #000; }
#qunit-tests b.passed { color: #5E740B; } #qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; } #qunit-tests b.failed { color: #710909; }
#qunit-tests li li { #qunit-tests li li {
padding: 5px; padding: 5px;
background-color: #fff; background-color: #FFF;
border-bottom: none; border-bottom: none;
list-style-position: inside; list-style-position: inside;
} }
@ -180,8 +215,8 @@
/*** Passing Styles */ /*** Passing Styles */
#qunit-tests li li.pass { #qunit-tests li li.pass {
color: #3c510c; color: #3C510C;
background-color: #fff; background-color: #FFF;
border-left: 10px solid #C6E746; border-left: 10px solid #C6E746;
} }
@ -189,7 +224,7 @@
#qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual, #qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; } #qunit-tests .pass .test-expected { color: #999; }
#qunit-banner.qunit-pass { background-color: #C6E746; } #qunit-banner.qunit-pass { background-color: #C6E746; }
@ -197,40 +232,52 @@
#qunit-tests li li.fail { #qunit-tests li li.fail {
color: #710909; color: #710909;
background-color: #fff; background-color: #FFF;
border-left: 10px solid #EE5757; border-left: 10px solid #EE5757;
white-space: pre; white-space: pre;
} }
#qunit-tests > li:last-child { #qunit-tests > li:last-child {
border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
} }
#qunit-tests .fail { color: #000000; background-color: #EE5757; } #qunit-tests .fail { color: #000; background-color: #EE5757; }
#qunit-tests .fail .test-name, #qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; } #qunit-tests .fail .module-name { color: #000; }
#qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; } #qunit-tests .fail .test-expected { color: #008000; }
#qunit-banner.qunit-fail { background-color: #EE5757; } #qunit-banner.qunit-fail { background-color: #EE5757; }
/*** Skipped tests */
#qunit-tests .skipped {
background-color: #EBECE9;
}
#qunit-tests .qunit-skipped-label {
background-color: #F4FF77;
display: inline-block;
font-style: normal;
color: #366097;
line-height: 1.8em;
padding: 0 0.5em;
margin: -0.4em 0.4em -0.4em 0;
}
/** Result */ /** Result */
#qunit-testresult { #qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em; padding: 0.5em 1em 0.5em 1em;
color: #2b81af; color: #2B81AF;
background-color: #D2E0E6; background-color: #D2E0E6;
border-bottom: 1px solid white; border-bottom: 1px solid #FFF;
} }
#qunit-testresult .module-name { #qunit-testresult .module-name {
font-weight: bold; font-weight: 700;
} }
/** Fixture */ /** Fixture */

File diff suppressed because it is too large Load diff

View file

@ -1,127 +0,0 @@
/*
* QtWebKit-powered headless test runner using PhantomJS
*
* PhantomJS binaries: http://phantomjs.org/download.html
* Requires PhantomJS 1.6+ (1.7+ recommended)
*
* Run with:
* phantomjs runner.js [url-of-your-qunit-testsuite]
*
* e.g.
* phantomjs runner.js http://localhost/qunit/test/index.html
*/
/*jshint latedef:false */
/*global phantom:false, require:false, console:false, window:false, QUnit:false */
(function() {
'use strict';
var args = require('system').args;
// arg[0]: scriptName, args[1...]: arguments
if (args.length !== 2) {
console.error('Usage:\n phantomjs runner.js [url-of-your-qunit-testsuite]');
phantom.exit(1);
}
var url = args[1],
page = require('webpage').create();
// Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`)
page.onConsoleMessage = function(msg) {
console.log(msg);
};
page.onInitialized = function() {
page.evaluate(addLogging);
};
page.onCallback = function(message) {
var result,
failed;
if (message) {
if (message.name === 'QUnit.done') {
result = message.data;
failed = !result || result.failed;
phantom.exit(failed ? 1 : 0);
}
}
};
page.open(url, function(status) {
if (status !== 'success') {
console.error('Unable to access network: ' + status);
phantom.exit(1);
} else {
// Cannot do this verification with the 'DOMContentLoaded' handler because it
// will be too late to attach it if a page does not have any script tags.
var qunitMissing = page.evaluate(function() { return (typeof QUnit === 'undefined' || !QUnit); });
if (qunitMissing) {
console.error('The `QUnit` object is not present on this page.');
phantom.exit(1);
}
// Do nothing... the callback mechanism will handle everything!
}
});
function addLogging() {
window.document.addEventListener('DOMContentLoaded', function() {
var current_test_assertions = [];
QUnit.log(function(details) {
var response;
// Ignore passing assertions
if (details.result) {
return;
}
response = details.message || '';
if (typeof details.expected !== 'undefined') {
if (response) {
response += ', ';
}
response += 'expected: ' + details.expected + ', but was: ' + details.actual;
if (details.source) {
response += "\n" + details.source;
}
}
current_test_assertions.push('Failed assertion: ' + response);
});
QUnit.testDone(function(result) {
var i,
len,
name = result.module + ': ' + result.name;
if (result.failed) {
console.log('Test failed: ' + name);
for (i = 0, len = current_test_assertions.length; i < len; i++) {
console.log(' ' + current_test_assertions[i]);
}
}
current_test_assertions.length = 0;
});
QUnit.done(function(result) {
console.log('Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.');
if (typeof window.callPhantom === 'function') {
window.callPhantom({
'name': 'QUnit.done',
'data': result
});
}
});
}, false);
}
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff