diff --git a/apps/common/main/lib/component/ComboBox.js b/apps/common/main/lib/component/ComboBox.js index d45a3054c..d24a0181c 100644 --- a/apps/common/main/lib/component/ComboBox.js +++ b/apps/common/main/lib/component/ComboBox.js @@ -547,7 +547,7 @@ define([ '<% _.each(items, function(item) { %>', '
  • <%= scope.getDisplayValue(item) %>
  • ', '<% }); %>' - ].join(''), { + ].join(''))({ items: this.store.toJSON(), scope: this })); diff --git a/apps/common/main/lib/component/ComboBoxFonts.js b/apps/common/main/lib/component/ComboBoxFonts.js index af7cd2351..e58f26d70 100644 --- a/apps/common/main/lib/component/ComboBoxFonts.js +++ b/apps/common/main/lib/component/ComboBoxFonts.js @@ -353,7 +353,7 @@ define([ '
  • ', '', '
  • ' - ].join(''), { + ].join(''))({ item: item.attributes, scope: this })); diff --git a/apps/common/main/lib/component/Window.js b/apps/common/main/lib/component/Window.js index b9750e2ad..5ee828a96 100644 --- a/apps/common/main/lib/component/Window.js +++ b/apps/common/main/lib/component/Window.js @@ -421,7 +421,7 @@ define([ _.extend(options, { cls: 'alert', onprimary: onKeyDown, - tpl: _.template(template, options) + tpl: _.template(template)(options) }); var win = new Common.UI.Window(options), @@ -556,7 +556,7 @@ define([ render : function() { var renderto = this.initConfig.renderTo || document.body; $(renderto).append( - _.template(template, this.initConfig) + _.template(template)(this.initConfig) ); this.$window = $('#' + this.initConfig.id); diff --git a/apps/common/main/lib/view/AdvancedSettingsWindow.js b/apps/common/main/lib/view/AdvancedSettingsWindow.js index 9b87f1b71..5c8ece182 100644 --- a/apps/common/main/lib/view/AdvancedSettingsWindow.js +++ b/apps/common/main/lib/view/AdvancedSettingsWindow.js @@ -71,7 +71,7 @@ define([ '' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.handler = _options.handler; this.toggleGroup = _options.toggleGroup; diff --git a/apps/common/main/lib/view/Chat.js b/apps/common/main/lib/view/Chat.js index 308854791..d84448032 100644 --- a/apps/common/main/lib/view/Chat.js +++ b/apps/common/main/lib/view/Chat.js @@ -67,7 +67,7 @@ define([ templateUserList: _.template(''), @@ -82,7 +82,7 @@ define([ templateMsgList: _.template(''), @@ -162,7 +162,7 @@ define([ _onAddUser: function(m, c, opts) { 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}); } }, @@ -186,7 +186,7 @@ define([ var content = this.panelMessages.find('ul'); if (content && content.length) { this._prepareMessage(m); - content.append(_.template(this.tplMsg, {msg: m, scope: this})); + content.append(_.template(this.tplMsg)({msg: m, scope: this})); // scroll to end diff --git a/apps/common/main/lib/view/Comments.js b/apps/common/main/lib/view/Comments.js index 7b9389e4a..e238a5eb3 100644 --- a/apps/common/main/lib/view/Comments.js +++ b/apps/common/main/lib/view/Comments.js @@ -98,7 +98,7 @@ define([ this.store = options.store; 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.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20}; diff --git a/apps/common/main/lib/view/CopyWarningDialog.js b/apps/common/main/lib/view/CopyWarningDialog.js index b98fc3e78..7d8fff28c 100644 --- a/apps/common/main/lib/view/CopyWarningDialog.js +++ b/apps/common/main/lib/view/CopyWarningDialog.js @@ -83,7 +83,7 @@ define([ '' ].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); }, diff --git a/apps/common/main/lib/view/DocumentAccessDialog.js b/apps/common/main/lib/view/DocumentAccessDialog.js index e79ce6bf1..703f331e1 100644 --- a/apps/common/main/lib/view/DocumentAccessDialog.js +++ b/apps/common/main/lib/view/DocumentAccessDialog.js @@ -57,7 +57,7 @@ define([ '
    ' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.settingsurl = options.settingsurl || ''; Common.UI.Window.prototype.initialize.call(this, _options); diff --git a/apps/common/main/lib/view/ExternalDiagramEditor.js b/apps/common/main/lib/view/ExternalDiagramEditor.js index fa55e24b0..5f917b156 100644 --- a/apps/common/main/lib/view/ExternalDiagramEditor.js +++ b/apps/common/main/lib/view/ExternalDiagramEditor.js @@ -66,7 +66,7 @@ define([ '' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.handler = _options.handler; this._chartData = null; diff --git a/apps/common/main/lib/view/ExternalMergeEditor.js b/apps/common/main/lib/view/ExternalMergeEditor.js index dab4427a6..25138e485 100644 --- a/apps/common/main/lib/view/ExternalMergeEditor.js +++ b/apps/common/main/lib/view/ExternalMergeEditor.js @@ -66,7 +66,7 @@ define([ '' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.handler = _options.handler; this._mergeData = null; diff --git a/apps/common/main/lib/view/ImageFromUrlDialog.js b/apps/common/main/lib/view/ImageFromUrlDialog.js index 46888b3dd..992257873 100644 --- a/apps/common/main/lib/view/ImageFromUrlDialog.js +++ b/apps/common/main/lib/view/ImageFromUrlDialog.js @@ -65,7 +65,7 @@ define([ '' ].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); }, diff --git a/apps/common/main/lib/view/InsertTableDialog.js b/apps/common/main/lib/view/InsertTableDialog.js index c24d11ea3..22655700a 100644 --- a/apps/common/main/lib/view/InsertTableDialog.js +++ b/apps/common/main/lib/view/InsertTableDialog.js @@ -73,7 +73,7 @@ define([ '' ].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); }, diff --git a/apps/common/main/lib/view/OpenDialog.js b/apps/common/main/lib/view/OpenDialog.js index cf70abcb3..cbac99dd7 100644 --- a/apps/common/main/lib/view/OpenDialog.js +++ b/apps/common/main/lib/view/OpenDialog.js @@ -93,7 +93,7 @@ define([ this.codepages = options.codepages; this.settings = options.settings; - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); Common.UI.Window.prototype.initialize.call(this, _options); }, diff --git a/apps/common/main/lib/view/Plugins.js b/apps/common/main/lib/view/Plugins.js index 76fa9acc2..803ab6aaa 100644 --- a/apps/common/main/lib/view/Plugins.js +++ b/apps/common/main/lib/view/Plugins.js @@ -314,7 +314,7 @@ define([ '<% } %>' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.url = options.url || ''; Common.UI.Window.prototype.initialize.call(this, _options); diff --git a/apps/common/main/lib/view/RenameDialog.js b/apps/common/main/lib/view/RenameDialog.js index 11f9ebffe..95207e9bf 100644 --- a/apps/common/main/lib/view/RenameDialog.js +++ b/apps/common/main/lib/view/RenameDialog.js @@ -66,7 +66,7 @@ define([ '' ].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); }, diff --git a/apps/common/main/lib/view/ReviewChanges.js b/apps/common/main/lib/view/ReviewChanges.js index 0aaf6d1a2..79ae74c4e 100644 --- a/apps/common/main/lib/view/ReviewChanges.js +++ b/apps/common/main/lib/view/ReviewChanges.js @@ -80,7 +80,7 @@ define([ this.store = options.store; 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.sdkBounds = {width: 0, height: 0, padding: 10, paddingTop: 20}; @@ -616,7 +616,7 @@ define([ }, getPanel: function () { - this.$el = $(_.template( template, {} )); + this.$el = $(_.template(template)( {} )); if ( this.appConfig.canReview ) { this.btnPrev.render(this.$el.find('#btn-change-prev')); diff --git a/apps/common/main/lib/view/SearchDialog.js b/apps/common/main/lib/view/SearchDialog.js index a6128c349..f518a9a88 100644 --- a/apps/common/main/lib/view/SearchDialog.js +++ b/apps/common/main/lib/view/SearchDialog.js @@ -105,7 +105,7 @@ '' ].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); }, diff --git a/apps/documenteditor/main/app/view/HyperlinkSettingsDialog.js b/apps/documenteditor/main/app/view/HyperlinkSettingsDialog.js index 335bd1de5..eda22b39d 100644 --- a/apps/documenteditor/main/app/view/HyperlinkSettingsDialog.js +++ b/apps/documenteditor/main/app/view/HyperlinkSettingsDialog.js @@ -81,7 +81,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.api = this.options.api; Common.UI.Window.prototype.initialize.call(this, this.options); diff --git a/apps/documenteditor/main/app/view/MailMergeRecepients.js b/apps/documenteditor/main/app/view/MailMergeRecepients.js index 1d6421a77..9135a9886 100644 --- a/apps/documenteditor/main/app/view/MailMergeRecepients.js +++ b/apps/documenteditor/main/app/view/MailMergeRecepients.js @@ -54,7 +54,7 @@ define([ '
    ' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.fileChoiceUrl = options.fileChoiceUrl || ''; Common.UI.Window.prototype.initialize.call(this, _options); diff --git a/apps/documenteditor/main/app/view/MailMergeSaveDlg.js b/apps/documenteditor/main/app/view/MailMergeSaveDlg.js index 7158ac534..3a76f35e6 100644 --- a/apps/documenteditor/main/app/view/MailMergeSaveDlg.js +++ b/apps/documenteditor/main/app/view/MailMergeSaveDlg.js @@ -55,7 +55,7 @@ define([ '
    ' ].join(''); - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); this.mergeFolderUrl = options.mergeFolderUrl || ''; this.mergedFileUrl = options.mergedFileUrl || ''; diff --git a/apps/documenteditor/main/app/view/PageMarginsDialog.js b/apps/documenteditor/main/app/view/PageMarginsDialog.js index d9fb32f4e..fcb925a01 100644 --- a/apps/documenteditor/main/app/view/PageMarginsDialog.js +++ b/apps/documenteditor/main/app/view/PageMarginsDialog.js @@ -89,7 +89,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.spinners = []; this._noApply = false; diff --git a/apps/documenteditor/main/app/view/PageSizeDialog.js b/apps/documenteditor/main/app/view/PageSizeDialog.js index 3537e1e3c..c3f5ac6da 100644 --- a/apps/documenteditor/main/app/view/PageSizeDialog.js +++ b/apps/documenteditor/main/app/view/PageSizeDialog.js @@ -79,7 +79,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.spinners = []; this._noApply = false; diff --git a/apps/documenteditor/main/app/view/Statusbar.js b/apps/documenteditor/main/app/view/Statusbar.js index 3365e5993..db9c7d389 100644 --- a/apps/documenteditor/main/app/view/Statusbar.js +++ b/apps/documenteditor/main/app/view/Statusbar.js @@ -430,7 +430,7 @@ define([ label: this.labelSelect, 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); }, diff --git a/apps/documenteditor/main/app/view/StyleTitleDialog.js b/apps/documenteditor/main/app/view/StyleTitleDialog.js index 3e9ff6363..8fd238dd1 100644 --- a/apps/documenteditor/main/app/view/StyleTitleDialog.js +++ b/apps/documenteditor/main/app/view/StyleTitleDialog.js @@ -70,7 +70,7 @@ define([ '' ].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); }, diff --git a/apps/documenteditor/main/app/view/Toolbar.js b/apps/documenteditor/main/app/view/Toolbar.js index e7287160c..d7cd3d807 100644 --- a/apps/documenteditor/main/app/view/Toolbar.js +++ b/apps/documenteditor/main/app/view/Toolbar.js @@ -195,7 +195,7 @@ define([ { caption: me.textTabHome, action: 'home', extcls: 'canedit'}, { caption: me.textTabInsert, action: 'ins', extcls: 'canedit'}, { caption: me.textTabLayout, action: 'layout', extcls: 'canedit'} ]; - config.$dom = $(_.template(template, config)); + config.$dom = $(_.template(template)(config)); /** * UI Components @@ -2476,7 +2476,7 @@ define([ var _elements = $tabs || config.$dom.find('.tabs'); var $target = _elements.find('a[data-tab=' + _after_action + ']'); if ( $target.length ) { - $target.parent().after( _.template(_tplTab, tab) ); + $target.parent().after( _.template(_tplTab)(tab) ); if ( panel ) { _elements = $panels || config.$dom.find('.box-panels > .panel'); diff --git a/apps/documenteditor/mobile/app/view/Settings.js b/apps/documenteditor/mobile/app/view/Settings.js index 3abe57f4d..34d0ef01a 100644 --- a/apps/documenteditor/mobile/app/view/Settings.js +++ b/apps/documenteditor/mobile/app/view/Settings.js @@ -175,7 +175,7 @@ define([ '', '', '' - ].join(''), { + ].join(''))({ android: Framework7.prototype.device.android, item: size, index: index, diff --git a/apps/documenteditor/mobile/app/view/edit/EditChart.js b/apps/documenteditor/mobile/app/view/edit/EditChart.js index 9c6e80151..0eba29262 100644 --- a/apps/documenteditor/mobile/app/view/edit/EditChart.js +++ b/apps/documenteditor/mobile/app/view/edit/EditChart.js @@ -203,7 +203,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ styles: styles }); diff --git a/apps/documenteditor/mobile/app/view/edit/EditTable.js b/apps/documenteditor/mobile/app/view/edit/EditTable.js index 2f546509b..172c53ec3 100644 --- a/apps/documenteditor/mobile/app/view/edit/EditTable.js +++ b/apps/documenteditor/mobile/app/view/edit/EditTable.js @@ -138,7 +138,7 @@ define([ '', '<% }); %>', '' - ].join(''), { + ].join(''))({ styles: styles }); diff --git a/apps/presentationeditor/main/app/view/DocumentPreview.js b/apps/presentationeditor/main/app/view/DocumentPreview.js index 39119c2e8..3db0f8d63 100644 --- a/apps/presentationeditor/main/app/view/DocumentPreview.js +++ b/apps/presentationeditor/main/app/view/DocumentPreview.js @@ -100,7 +100,7 @@ define([ render: function () { var el = $(this.el), me = this; - el.html(_.template(this.template, { + el.html(_.template(this.template)({ scope: this })); diff --git a/apps/presentationeditor/main/app/view/HyperlinkSettingsDialog.js b/apps/presentationeditor/main/app/view/HyperlinkSettingsDialog.js index 6561093d6..4d79ef530 100644 --- a/apps/presentationeditor/main/app/view/HyperlinkSettingsDialog.js +++ b/apps/presentationeditor/main/app/view/HyperlinkSettingsDialog.js @@ -103,7 +103,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.slides = this.options.slides; this.api = this.options.api; diff --git a/apps/presentationeditor/main/app/view/SlideSizeSettings.js b/apps/presentationeditor/main/app/view/SlideSizeSettings.js index 09a499c65..77ec2b563 100644 --- a/apps/presentationeditor/main/app/view/SlideSizeSettings.js +++ b/apps/presentationeditor/main/app/view/SlideSizeSettings.js @@ -87,7 +87,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.spinners = []; this._noApply = false; diff --git a/apps/presentationeditor/main/app/view/SlideshowSettings.js b/apps/presentationeditor/main/app/view/SlideshowSettings.js index d0ce309c2..41bc3ef22 100644 --- a/apps/presentationeditor/main/app/view/SlideshowSettings.js +++ b/apps/presentationeditor/main/app/view/SlideshowSettings.js @@ -68,7 +68,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.spinners = []; this._noApply = false; diff --git a/apps/presentationeditor/main/app/view/Toolbar.js b/apps/presentationeditor/main/app/view/Toolbar.js index 23bbed188..d0cd4efe3 100644 --- a/apps/presentationeditor/main/app/view/Toolbar.js +++ b/apps/presentationeditor/main/app/view/Toolbar.js @@ -251,7 +251,7 @@ define([ { caption: 'Home', action: 'home', extcls: 'canedit'}, { caption: 'Insert', action: 'ins', extcls: 'canedit'} ]; - config.$layout = $(_.template(template, config)); + config.$layout = $(_.template(template)(config)); me.paragraphControls = []; me.shapeControls = []; @@ -1795,7 +1795,7 @@ define([ var _elements = $tabs || config.$layout.find('.tabs'); var $target = _elements.find('a[data-tab=' + _after_action + ']'); if ( $target.length ) { - $target.parent().after( _.template(_tplTab, tab) ); + $target.parent().after( _.template(_tplTab)(tab) ); if ( panel ) { _elements = $panels || config.$layout.find('.box-panels > .panel'); diff --git a/apps/presentationeditor/mobile/app/view/add/AddSlide.js b/apps/presentationeditor/mobile/app/view/add/AddSlide.js index 91b5bef04..b2984c9ed 100644 --- a/apps/presentationeditor/mobile/app/view/add/AddSlide.js +++ b/apps/presentationeditor/mobile/app/view/add/AddSlide.js @@ -123,7 +123,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ layouts: layouts }); diff --git a/apps/presentationeditor/mobile/app/view/edit/EditChart.js b/apps/presentationeditor/mobile/app/view/edit/EditChart.js index 63cee3905..7508662d9 100644 --- a/apps/presentationeditor/mobile/app/view/edit/EditChart.js +++ b/apps/presentationeditor/mobile/app/view/edit/EditChart.js @@ -203,7 +203,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ styles: styles }); diff --git a/apps/presentationeditor/mobile/app/view/edit/EditSlide.js b/apps/presentationeditor/mobile/app/view/edit/EditSlide.js index 38ed167df..3acc66e2b 100644 --- a/apps/presentationeditor/mobile/app/view/edit/EditSlide.js +++ b/apps/presentationeditor/mobile/app/view/edit/EditSlide.js @@ -235,7 +235,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ layouts: layouts }); @@ -268,7 +268,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ themes: themes }); @@ -291,7 +291,7 @@ define([ '', '', '<% }); %>' - ].join(''), { + ].join(''))({ android : Common.SharedSettings.get('android'), types: _arrCurrentEffectTypes }); diff --git a/apps/presentationeditor/mobile/app/view/edit/EditTable.js b/apps/presentationeditor/mobile/app/view/edit/EditTable.js index aa922581b..d0cf8c92a 100644 --- a/apps/presentationeditor/mobile/app/view/edit/EditTable.js +++ b/apps/presentationeditor/mobile/app/view/edit/EditTable.js @@ -138,7 +138,7 @@ define([ '', '<% }); %>', '' - ].join(''), { + ].join(''))({ styles: styles }); diff --git a/apps/spreadsheeteditor/main/app/view/AutoFilterDialog.js b/apps/spreadsheeteditor/main/app/view/AutoFilterDialog.js index c0c2fa909..e1500b78b 100644 --- a/apps/spreadsheeteditor/main/app/view/AutoFilterDialog.js +++ b/apps/spreadsheeteditor/main/app/view/AutoFilterDialog.js @@ -93,7 +93,7 @@ define([ this.handler = options.handler; 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); }, @@ -342,7 +342,7 @@ define([ this.api = options.api; this.handler = options.handler; - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); Common.UI.Window.prototype.initialize.call(this, _options); }, @@ -516,7 +516,7 @@ define([ this.throughIndexes = []; this.filteredIndexes = []; - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); Common.UI.Window.prototype.initialize.call(this, _options); }, diff --git a/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js b/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js index dc36bcd89..b8a91cb4e 100644 --- a/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js +++ b/apps/spreadsheeteditor/main/app/view/CellRangeDialog.js @@ -69,7 +69,7 @@ define([ '' ].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); }, diff --git a/apps/spreadsheeteditor/main/app/view/FormulaDialog.js b/apps/spreadsheeteditor/main/app/view/FormulaDialog.js index 55dd6d9d8..a6bdf7a8a 100644 --- a/apps/spreadsheeteditor/main/app/view/FormulaDialog.js +++ b/apps/spreadsheeteditor/main/app/view/FormulaDialog.js @@ -92,7 +92,7 @@ define([ this.formulasGroups = options.formulasGroups; this.handler = options.handler; - _options.tpl = _.template(this.template, _options); + _options.tpl = _.template(this.template)(_options); Common.UI.Window.prototype.initialize.call(this, _options); }, diff --git a/apps/spreadsheeteditor/main/app/view/HyperlinkSettingsDialog.js b/apps/spreadsheeteditor/main/app/view/HyperlinkSettingsDialog.js index 0a9468a35..1a65109df 100644 --- a/apps/spreadsheeteditor/main/app/view/HyperlinkSettingsDialog.js +++ b/apps/spreadsheeteditor/main/app/view/HyperlinkSettingsDialog.js @@ -99,7 +99,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.api = this.options.api; Common.UI.Window.prototype.initialize.call(this, this.options); diff --git a/apps/spreadsheeteditor/main/app/view/SetValueDialog.js b/apps/spreadsheeteditor/main/app/view/SetValueDialog.js index 94aac612e..50fdfe928 100644 --- a/apps/spreadsheeteditor/main/app/view/SetValueDialog.js +++ b/apps/spreadsheeteditor/main/app/view/SetValueDialog.js @@ -67,7 +67,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.startvalue = this.options.startvalue; this.maxvalue = this.options.maxvalue; this.defaultUnit = this.options.defaultUnit; diff --git a/apps/spreadsheeteditor/main/app/view/Statusbar.js b/apps/spreadsheeteditor/main/app/view/Statusbar.js index c9a0223f4..4086b3e9a 100644 --- a/apps/spreadsheeteditor/main/app/view/Statusbar.js +++ b/apps/spreadsheeteditor/main/app/view/Statusbar.js @@ -68,7 +68,7 @@ define([ templateUserList: _.template(''), @@ -489,7 +489,7 @@ define([ _onAddUser: function(m, c, opts) { 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}); } }, @@ -706,7 +706,7 @@ define([ label: this.labelSheetName, 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); }, @@ -823,7 +823,7 @@ define([ label: options.ismove ? this.textMoveBefore : this.textCopyBefore, 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); }, diff --git a/apps/spreadsheeteditor/main/app/view/TableOptionsDialog.js b/apps/spreadsheeteditor/main/app/view/TableOptionsDialog.js index c4dc0a4e4..023fd5bc3 100644 --- a/apps/spreadsheeteditor/main/app/view/TableOptionsDialog.js +++ b/apps/spreadsheeteditor/main/app/view/TableOptionsDialog.js @@ -72,7 +72,7 @@ define([ '' ].join(''); - this.options.tpl = _.template(this.template, this.options); + this.options.tpl = _.template(this.template)(this.options); this.checkRangeType = Asc.c_oAscSelectionDialogType.FormatTable; this.selectionType = Asc.c_oAscSelectionType.RangeCells; diff --git a/apps/spreadsheeteditor/mobile/app/view/add/AddFunction.js b/apps/spreadsheeteditor/mobile/app/view/add/AddFunction.js index dea71b678..684c4d5da 100644 --- a/apps/spreadsheeteditor/mobile/app/view/add/AddFunction.js +++ b/apps/spreadsheeteditor/mobile/app/view/add/AddFunction.js @@ -57,7 +57,7 @@ define([ }; _.extend(_params, args); - var $content = $('
    ').append(_.template(this.template, _params)); + var $content = $('
    ').append(_.template(this.template)(_params)); // Android fix for navigation if (Framework7.prototype.device.android) { @@ -121,7 +121,7 @@ define([ 'Logical': me.sCatLogical }; - me.layout = $('
    ').append(_.template(me.template, { + me.layout = $('
    ').append(_.template(me.template)({ android : Common.SharedSettings.get('android'), phone : Common.SharedSettings.get('phone'), textGroups : me.textGroups, diff --git a/apps/spreadsheeteditor/mobile/app/view/add/AddLink.js b/apps/spreadsheeteditor/mobile/app/view/add/AddLink.js index de2d9ac1e..d73083cab 100644 --- a/apps/spreadsheeteditor/mobile/app/view/add/AddLink.js +++ b/apps/spreadsheeteditor/mobile/app/view/add/AddLink.js @@ -210,7 +210,7 @@ define([ '<% }) %>'; this.layout.find('#add-link-sheet select').html( - _.template(tpl, { + _.template(tpl)({ worksheets: sheets }) ); @@ -219,7 +219,7 @@ define([ if ($view.length > 0) { $view.find('#add-link-sheet select').html( - _.template(tpl, { + _.template(tpl)({ worksheets: sheets }) ); diff --git a/apps/spreadsheeteditor/mobile/app/view/add/AddOther.js b/apps/spreadsheeteditor/mobile/app/view/add/AddOther.js index efed7228c..6c14be0ff 100644 --- a/apps/spreadsheeteditor/mobile/app/view/add/AddOther.js +++ b/apps/spreadsheeteditor/mobile/app/view/add/AddOther.js @@ -76,8 +76,7 @@ define([ } mapNavigation = - el = _.template(tplNavigation, - { + el = _.template(tplNavigation)({ android : Common.SharedSettings.get('android'), phone : Common.SharedSettings.get('phone'), textBack : this.textBack, diff --git a/apps/spreadsheeteditor/mobile/app/view/edit/EditCell.js b/apps/spreadsheeteditor/mobile/app/view/edit/EditCell.js index 5164c86f9..ba5a83ea7 100644 --- a/apps/spreadsheeteditor/mobile/app/view/edit/EditCell.js +++ b/apps/spreadsheeteditor/mobile/app/view/edit/EditCell.js @@ -133,7 +133,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ styles: styles, styleSize: styleSize }); diff --git a/apps/spreadsheeteditor/mobile/app/view/edit/EditChart.js b/apps/spreadsheeteditor/mobile/app/view/edit/EditChart.js index dadda1dc3..e1ed91813 100644 --- a/apps/spreadsheeteditor/mobile/app/view/edit/EditChart.js +++ b/apps/spreadsheeteditor/mobile/app/view/edit/EditChart.js @@ -160,7 +160,7 @@ define([ '<% }); %>', '', '<% }); %>' - ].join(''), { + ].join(''))({ styles: styles }); diff --git a/vendor/underscore/.eslintrc b/vendor/underscore/.eslintrc new file mode 100644 index 000000000..5ab39e461 --- /dev/null +++ b/vendor/underscore/.eslintrc @@ -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 + } +} diff --git a/vendor/underscore/.gitignore b/vendor/underscore/.gitignore index 029616c3c..ab5b5c583 100644 --- a/vendor/underscore/.gitignore +++ b/vendor/underscore/.gitignore @@ -1,2 +1,7 @@ raw node_modules +*.log +*.idea +*.swp +nyc_output +coverage diff --git a/vendor/underscore/.travis.yml b/vendor/underscore/.travis.yml index 99dc7712c..f0e6ad148 100644 --- a/vendor/underscore/.travis.yml +++ b/vendor/underscore/.travis.yml @@ -1,5 +1,30 @@ language: node_js +sudo: false 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: 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 diff --git a/vendor/underscore/CODE_OF_CONDUCT.md b/vendor/underscore/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..6b777fb6d --- /dev/null +++ b/vendor/underscore/CODE_OF_CONDUCT.md @@ -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/ diff --git a/vendor/underscore/CONTRIBUTING.md b/vendor/underscore/CONTRIBUTING.md index e133ebea1..7e9a579e4 100644 --- a/vendor/underscore/CONTRIBUTING.md +++ b/vendor/underscore/CONTRIBUTING.md @@ -1,7 +1,11 @@ ## 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. +* 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/). * Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/underscore/blob/master/underscore.js). diff --git a/vendor/underscore/LICENSE b/vendor/underscore/LICENSE index 3acf90838..04be0d9fb 100644 --- a/vendor/underscore/LICENSE +++ b/vendor/underscore/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative +Copyright (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors Permission is hereby granted, free of charge, to any person diff --git a/vendor/underscore/README.md b/vendor/underscore/README.md index c2ba2590c..a99db1895 100644 --- a/vendor/underscore/README.md +++ b/vendor/underscore/README.md @@ -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...) 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: 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: https://github.com/documentcloud diff --git a/vendor/underscore/bower.json b/vendor/underscore/bower.json new file mode 100644 index 000000000..d5e1d7852 --- /dev/null +++ b/vendor/underscore/bower.json @@ -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.*"] +} diff --git a/vendor/underscore/docs/.eslintrc b/vendor/underscore/docs/.eslintrc new file mode 100644 index 000000000..aac42a91c --- /dev/null +++ b/vendor/underscore/docs/.eslintrc @@ -0,0 +1,5 @@ +{ + "globals": { + "_": true + } +} diff --git a/vendor/underscore/docs/docco.css b/vendor/underscore/docs/docco.css index f690a0794..b60f6fa3d 100644 --- a/vendor/underscore/docs/docco.css +++ b/vendor/underscore/docs/docco.css @@ -21,11 +21,11 @@ } @font-face { - font-family: 'novecento-bold'; - src: url('public/fonts/novecento-bold.eot'); - src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), - url('public/fonts/novecento-bold.woff') format('woff'), - url('public/fonts/novecento-bold.ttf') format('truetype'); + font-family: 'roboto-black'; + src: url('public/fonts/roboto-black.eot'); + src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'), + url('public/fonts/roboto-black.woff') format('woff'), + url('public/fonts/roboto-black.ttf') format('truetype'); font-weight: normal; font-style: normal; } @@ -51,15 +51,23 @@ b, strong { font-family: "aller-bold"; } -p, ul, ol { +p { 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 { color: #112233; line-height: 1em; font-weight: normal; - font-family: "novecento-bold"; + font-family: "roboto-black"; text-transform: uppercase; margin: 30px 0 15px 0; } @@ -67,10 +75,13 @@ h1, h2, h3, h4, h5, h6 { h1 { margin-top: 40px; } +h2 { + font-size: 1.26em; +} hr { border: 0; - background: 1px solid #ddd; + background: 1px #ddd; height: 1px; margin: 20px 0; } @@ -172,9 +183,18 @@ ul.sections > li > div { display: block; } +#jump_page_wrapper{ + position: fixed; + right: 0; + top: 0; + bottom: 0; +} + #jump_page { padding: 5px 0 3px; margin: 0 0 25px 25px; + max-height: 100%; + overflow: auto; } #jump_page .source { @@ -205,7 +225,6 @@ ul.sections > li > div { } ul.sections > li > div.content { - background: #f5f5ff; overflow-x:auto; -webkit-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 { padding: 13px; vertical-align: top; - background: #f5f5ff; border: none; -webkit-box-shadow: none; box-shadow: none; @@ -376,125 +394,125 @@ pre code { background: #f8f8ff } -pre .comment, -pre .template_comment, -pre .diff .header, -pre .javadoc { +pre .hljs-comment, +pre .hljs-template_comment, +pre .hljs-diff .hljs-header, +pre .hljs-javadoc { color: #408080; font-style: italic } -pre .keyword, -pre .assignment, -pre .literal, -pre .css .rule .keyword, -pre .winutils, -pre .javascript .title, -pre .lisp .title, -pre .subst { +pre .hljs-keyword, +pre .hljs-assignment, +pre .hljs-literal, +pre .hljs-css .hljs-rule .hljs-keyword, +pre .hljs-winutils, +pre .hljs-javascript .hljs-title, +pre .hljs-lisp .hljs-title, +pre .hljs-subst { color: #954121; /*font-weight: bold*/ } -pre .number, -pre .hexcolor { +pre .hljs-number, +pre .hljs-hexcolor { color: #40a070 } -pre .string, -pre .tag .value, -pre .phpdoc, -pre .tex .formula { +pre .hljs-string, +pre .hljs-tag .hljs-value, +pre .hljs-phpdoc, +pre .hljs-tex .hljs-formula { color: #219161; } -pre .title, -pre .id { +pre .hljs-title, +pre .hljs-id { color: #19469D; } -pre .params { +pre .hljs-params { color: #00F; } -pre .javascript .title, -pre .lisp .title, -pre .subst { +pre .hljs-javascript .hljs-title, +pre .hljs-lisp .hljs-title, +pre .hljs-subst { font-weight: normal } -pre .class .title, -pre .haskell .label, -pre .tex .command { +pre .hljs-class .hljs-title, +pre .hljs-haskell .hljs-label, +pre .hljs-tex .hljs-command { color: #458; font-weight: bold } -pre .tag, -pre .tag .title, -pre .rules .property, -pre .django .tag .keyword { +pre .hljs-tag, +pre .hljs-tag .hljs-title, +pre .hljs-rules .hljs-property, +pre .hljs-django .hljs-tag .hljs-keyword { color: #000080; font-weight: normal } -pre .attribute, -pre .variable, -pre .instancevar, -pre .lisp .body { +pre .hljs-attribute, +pre .hljs-variable, +pre .hljs-instancevar, +pre .hljs-lisp .hljs-body { color: #008080 } -pre .regexp { +pre .hljs-regexp { color: #B68 } -pre .class { +pre .hljs-class { color: #458; font-weight: bold } -pre .symbol, -pre .ruby .symbol .string, -pre .ruby .symbol .keyword, -pre .ruby .symbol .keymethods, -pre .lisp .keyword, -pre .tex .special, -pre .input_number { +pre .hljs-symbol, +pre .hljs-ruby .hljs-symbol .hljs-string, +pre .hljs-ruby .hljs-symbol .hljs-keyword, +pre .hljs-ruby .hljs-symbol .hljs-keymethods, +pre .hljs-lisp .hljs-keyword, +pre .hljs-tex .hljs-special, +pre .hljs-input_number { color: #990073 } -pre .builtin, -pre .constructor, -pre .built_in, -pre .lisp .title { +pre .hljs-builtin, +pre .hljs-constructor, +pre .hljs-built_in, +pre .hljs-lisp .hljs-title { color: #0086b3 } -pre .preprocessor, -pre .pi, -pre .doctype, -pre .shebang, -pre .cdata { +pre .hljs-preprocessor, +pre .hljs-pi, +pre .hljs-doctype, +pre .hljs-shebang, +pre .hljs-cdata { color: #999; font-weight: bold } -pre .deletion { +pre .hljs-deletion { background: #fdd } -pre .addition { +pre .hljs-addition { background: #dfd } -pre .diff .change { +pre .hljs-diff .hljs-change { background: #0086b3 } -pre .chunk { +pre .hljs-chunk { color: #aaa } -pre .tex .formula { +pre .hljs-tex .hljs-formula { opacity: 0.5; } diff --git a/vendor/underscore/docs/main.js b/vendor/underscore/docs/main.js new file mode 100644 index 000000000..009cd0ad0 --- /dev/null +++ b/vendor/underscore/docs/main.js @@ -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(); + } + }); +}()); diff --git a/vendor/underscore/docs/public/fonts/roboto-black.eot b/vendor/underscore/docs/public/fonts/roboto-black.eot new file mode 100644 index 000000000..571ed4912 Binary files /dev/null and b/vendor/underscore/docs/public/fonts/roboto-black.eot differ diff --git a/vendor/underscore/docs/public/fonts/roboto-black.ttf b/vendor/underscore/docs/public/fonts/roboto-black.ttf new file mode 100644 index 000000000..e0300b3ee Binary files /dev/null and b/vendor/underscore/docs/public/fonts/roboto-black.ttf differ diff --git a/vendor/underscore/docs/public/fonts/roboto-black.woff b/vendor/underscore/docs/public/fonts/roboto-black.woff new file mode 100644 index 000000000..642e5b60f Binary files /dev/null and b/vendor/underscore/docs/public/fonts/roboto-black.woff differ diff --git a/vendor/underscore/docs/underscore.html b/vendor/underscore/docs/underscore.html index b96d40874..2170598b4 100644 --- a/vendor/underscore/docs/underscore.html +++ b/vendor/underscore/docs/underscore.html @@ -27,14 +27,15 @@ -
    Underscore.js 1.5.2
    -http://underscorejs.org
    -(c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
    -Underscore may be freely distributed under the MIT license.
    - +
    Underscore.js 1.8.3
    +http://underscorejs.org
    +(c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
    +Underscore may be freely distributed under the MIT license.
    +
    -
    (function() {
    +
    +(function() {
    @@ -42,10 +43,10 @@ Underscore may be freely distributed under the MIT license.
  • -
    +
    -

    Baseline setup

    +

    Baseline setup

    @@ -58,12 +59,9 @@ Underscore may be freely distributed under the MIT license.
    -

    Establish the root object, window in the browser, or exports on the server.

    - +
    -
      var root = this;
    -
  • @@ -73,11 +71,11 @@ Underscore may be freely distributed under the MIT license.
    -

    Save the previous value of the _ variable.

    +

    Establish the root object, window in the browser, or exports on the server.

    -
      var previousUnderscore = root._;
    +
      var root = this;
    @@ -88,11 +86,11 @@ Underscore may be freely distributed under the MIT license.
    -

    Establish the object that gets returned to break out of a loop iteration.

    +

    Save the previous value of the _ variable.

    -
      var breaker = {};
    +
      var previousUnderscore = root._;
    @@ -107,7 +105,7 @@ Underscore may be freely distributed under the MIT license.
    -
      var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
    +
      var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
    @@ -122,10 +120,9 @@ Underscore may be freely distributed under the MIT license. -
      var
    +            
      var
         push             = ArrayProto.push,
         slice            = ArrayProto.slice,
    -    concat           = ArrayProto.concat,
         toString         = ObjProto.toString,
         hasOwnProperty   = ObjProto.hasOwnProperty;
    @@ -143,19 +140,11 @@ are declared here.

    -
      var
    -    nativeForEach      = ArrayProto.forEach,
    -    nativeMap          = ArrayProto.map,
    -    nativeReduce       = ArrayProto.reduce,
    -    nativeReduceRight  = ArrayProto.reduceRight,
    -    nativeFilter       = ArrayProto.filter,
    -    nativeEvery        = ArrayProto.every,
    -    nativeSome         = ArrayProto.some,
    -    nativeIndexOf      = ArrayProto.indexOf,
    -    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    -    nativeIsArray      = Array.isArray,
    -    nativeKeys         = Object.keys,
    -    nativeBind         = FuncProto.bind;
    +
      var
    +    nativeIsArray      = Array.isArray,
    +    nativeKeys         = Object.keys,
    +    nativeBind         = FuncProto.bind,
    +    nativeCreate       = Object.create;
    @@ -166,15 +155,11 @@ are declared here.

    -

    Create a safe reference to the Underscore object for use below.

    +

    Naked function reference for surrogate-prototype-swapping.

    -
      var _ = function(obj) {
    -    if (obj instanceof _) return obj;
    -    if (!(this instanceof _)) return new _(obj);
    -    this._wrapped = obj;
    -  };
    +
      var Ctor = function(){};
    @@ -185,21 +170,15 @@ are declared here.

    -

    Export the Underscore object for Node.js, with -backwards-compatibility for the old require() API. If we're in -the browser, add _ as a global object via a string identifier, -for Closure Compiler "advanced" mode.

    +

    Create a safe reference to the Underscore object for use below.

    -
      if (typeof exports !== 'undefined') {
    -    if (typeof module !== 'undefined' && module.exports) {
    -      exports = module.exports = _;
    -    }
    -    exports._ = _;
    -  } else {
    -    root._ = _;
    -  }
    +
      var _ = function(obj) {
    +    if (obj instanceof _) return obj;
    +    if (!(this instanceof _)) return new _(obj);
    +    this._wrapped = obj;
    +  };
    @@ -210,11 +189,20 @@ for Closure Compiler "advanced" mode.

    -

    Current version.

    +

    Export the Underscore object for Node.js, with +backwards-compatibility for the old require() API. If we’re in +the browser, add _ as a global object.

    -
      _.VERSION = '1.5.2';
    +
      if (typeof exports !== 'undefined') {
    +    if (typeof module !== 'undefined' && module.exports) {
    +      exports = module.exports = _;
    +    }
    +    exports._ = _;
    +  } else {
    +    root._ = _;
    +  }
    @@ -222,13 +210,15 @@ for Closure Compiler "advanced" mode.

  • -
    +
    -

    Collection Functions

    +

    Current version.

    +
      _.VERSION = '1.8.3';
    +
  • @@ -238,26 +228,31 @@ for Closure Compiler "advanced" mode.

    -

    The cornerstone, an each implementation, aka forEach. -Handles objects with the built-in forEach, arrays, and raw objects. -Delegates to ECMAScript 5's native forEach if available.

    +

    Internal function that returns an efficient (for current engines) version +of the passed-in callback, to be repeatedly applied in other Underscore +functions.

    -
      var each = _.each = _.forEach = function(obj, iterator, context) {
    -    if (obj == null) return;
    -    if (nativeForEach && obj.forEach === nativeForEach) {
    -      obj.forEach(iterator, context);
    -    } else if (obj.length === +obj.length) {
    -      for (var i = 0, length = obj.length; i < length; i++) {
    -        if (iterator.call(context, obj[i], i, obj) === breaker) return;
    -      }
    -    } else {
    -      var keys = _.keys(obj);
    -      for (var i = 0, length = keys.length; i < length; i++) {
    -        if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
    -      }
    +            
      var optimizeCb = function(func, context, argCount) {
    +    if (context === void 0) return func;
    +    switch (argCount == null ? 3 : argCount) {
    +      case 1: return function(value) {
    +        return func.call(context, value);
    +      };
    +      case 2: return function(value, other) {
    +        return func.call(context, value, other);
    +      };
    +      case 3: return function(value, index, collection) {
    +        return func.call(context, value, index, collection);
    +      };
    +      case 4: return function(accumulator, value, index, collection) {
    +        return func.call(context, accumulator, value, index, collection);
    +      };
         }
    +    return function() {
    +      return func.apply(context, arguments);
    +    };
       };
    @@ -269,22 +264,21 @@ Delegates to ECMAScript 5's native forEach if
    -

    Return the results of applying the iterator to each element. -Delegates to ECMAScript 5's native map if available.

    +

    A mostly-internal function to generate callbacks that can be applied +to each element in a collection, returning the desired result — either +identity, an arbitrary callback, a property matcher, or a property accessor.

    -
      _.map = _.collect = function(obj, iterator, context) {
    -    var results = [];
    -    if (obj == null) return results;
    -    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
    -    each(obj, function(value, index, list) {
    -      results.push(iterator.call(context, value, index, list));
    -    });
    -    return results;
    +            
      var cb = function(value, context, argCount) {
    +    if (value == null) return _.identity;
    +    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
    +    if (_.isObject(value)) return _.matcher(value);
    +    return _.property(value);
       };
    -
    -  var reduceError = 'Reduce of empty array with no initial value';
    + _.iteratee = function(value, context) { + return cb(value, context, Infinity); + };
    @@ -295,28 +289,25 @@ Delegates to ECMAScript 5's native map if avai
    -

    Reduce builds up a single result from a list of values, aka inject, -or foldl. Delegates to ECMAScript 5's native reduce if available.

    +

    An internal function for creating assigner functions.

    -
      _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    -    var initial = arguments.length > 2;
    -    if (obj == null) obj = [];
    -    if (nativeReduce && obj.reduce === nativeReduce) {
    -      if (context) iterator = _.bind(iterator, context);
    -      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    -    }
    -    each(obj, function(value, index, list) {
    -      if (!initial) {
    -        memo = value;
    -        initial = true;
    -      } else {
    -        memo = iterator.call(context, memo, value, index, list);
    +            
      var createAssigner = function(keysFunc, undefinedOnly) {
    +    return function(obj) {
    +      var length = arguments.length;
    +      if (length < 2 || obj == null) return obj;
    +      for (var index = 1; index < length; index++) {
    +        var source = arguments[index],
    +            keys = keysFunc(source),
    +            l = keys.length;
    +        for (var i = 0; i < l; i++) {
    +          var key = keys[i];
    +          if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
    +        }
           }
    -    });
    -    if (!initial) throw new TypeError(reduceError);
    -    return memo;
    +      return obj;
    +    };
       };
    @@ -328,34 +319,23 @@ or foldl. Delegates to ECMAScript 5's native <
    -

    The right-associative version of reduce, also known as foldr. -Delegates to ECMAScript 5's native reduceRight if available.

    +

    An internal function for creating a new object that inherits from another.

    -
      _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    -    var initial = arguments.length > 2;
    -    if (obj == null) obj = [];
    -    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
    -      if (context) iterator = _.bind(iterator, context);
    -      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    -    }
    -    var length = obj.length;
    -    if (length !== +length) {
    -      var keys = _.keys(obj);
    -      length = keys.length;
    -    }
    -    each(obj, function(value, index, list) {
    -      index = keys ? keys[--length] : --length;
    -      if (!initial) {
    -        memo = obj[index];
    -        initial = true;
    -      } else {
    -        memo = iterator.call(context, memo, obj[index], index, list);
    -      }
    -    });
    -    if (!initial) throw new TypeError(reduceError);
    -    return memo;
    +            
      var baseCreate = function(prototype) {
    +    if (!_.isObject(prototype)) return {};
    +    if (nativeCreate) return nativeCreate(prototype);
    +    Ctor.prototype = prototype;
    +    var result = new Ctor;
    +    Ctor.prototype = null;
    +    return result;
    +  };
    +
    +  var property = function(key) {
    +    return function(obj) {
    +      return obj == null ? void 0 : obj[key];
    +    };
       };
    @@ -367,19 +347,18 @@ Delegates to ECMAScript 5's native reduceRight
    -

    Return the first value which passes a truth test. Aliased as detect.

    +

    Helper for collection methods to determine whether a collection +should be iterated as an array or as an object +Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength +Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094

    -
      _.find = _.detect = function(obj, iterator, context) {
    -    var result;
    -    any(obj, function(value, index, list) {
    -      if (iterator.call(context, value, index, list)) {
    -        result = value;
    -        return true;
    -      }
    -    });
    -    return result;
    +            
      var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    +  var getLength = property('length');
    +  var isArrayLike = function(collection) {
    +    var length = getLength(collection);
    +    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
       };
    @@ -391,22 +370,10 @@ Delegates to ECMAScript 5's native reduceRight
    -

    Return all the elements that pass a truth test. -Delegates to ECMAScript 5's native filter if available. -Aliased as select.

    +

    Collection Functions

    -
      _.filter = _.select = function(obj, iterator, context) {
    -    var results = [];
    -    if (obj == null) return results;
    -    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    -    each(obj, function(value, index, list) {
    -      if (iterator.call(context, value, index, list)) results.push(value);
    -    });
    -    return results;
    -  };
    - @@ -416,16 +383,9 @@ Aliased as select.

    -

    Return all the elements for which a truth test fails.

    - +
    -
      _.reject = function(obj, iterator, context) {
    -    return _.filter(obj, function(value, index, list) {
    -      return !iterator.call(context, value, index, list);
    -    }, context);
    -  };
    - @@ -435,21 +395,26 @@ Aliased as select.

    -

    Determine whether all of the elements match a truth test. -Delegates to ECMAScript 5's native every if available. -Aliased as all.

    +

    The cornerstone, an each implementation, aka forEach. +Handles raw objects in addition to array-likes. Treats all +sparse array-likes as if they were dense.

    -
      _.every = _.all = function(obj, iterator, context) {
    -    iterator || (iterator = _.identity);
    -    var result = true;
    -    if (obj == null) return result;
    -    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    -    each(obj, function(value, index, list) {
    -      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
    -    });
    -    return !!result;
    +            
      _.each = _.forEach = function(obj, iteratee, context) {
    +    iteratee = optimizeCb(iteratee, context);
    +    var i, length;
    +    if (isArrayLike(obj)) {
    +      for (i = 0, length = obj.length; i < length; i++) {
    +        iteratee(obj[i], i, obj);
    +      }
    +    } else {
    +      var keys = _.keys(obj);
    +      for (i = 0, length = keys.length; i < length; i++) {
    +        iteratee(obj[keys[i]], keys[i], obj);
    +      }
    +    }
    +    return obj;
       };
    @@ -461,21 +426,20 @@ Aliased as all.

    -

    Determine if at least one element in the object matches a truth test. -Delegates to ECMAScript 5's native some if available. -Aliased as any.

    +

    Return the results of applying the iteratee to each element.

    -
      var any = _.some = _.any = function(obj, iterator, context) {
    -    iterator || (iterator = _.identity);
    -    var result = false;
    -    if (obj == null) return result;
    -    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    -    each(obj, function(value, index, list) {
    -      if (result || (result = iterator.call(context, value, index, list))) return breaker;
    -    });
    -    return !!result;
    +            
      _.map = _.collect = function(obj, iteratee, context) {
    +    iteratee = cb(iteratee, context);
    +    var keys = !isArrayLike(obj) && _.keys(obj),
    +        length = (keys || obj).length,
    +        results = Array(length);
    +    for (var index = 0; index < length; index++) {
    +      var currentKey = keys ? keys[index] : index;
    +      results[index] = iteratee(obj[currentKey], currentKey, obj);
    +    }
    +    return results;
       };
    @@ -487,18 +451,11 @@ Aliased as any.

    -

    Determine if the array or object contains a given value (using ===). -Aliased as include.

    +

    Create a reducing function iterating left or right.

    -
      _.contains = _.include = function(obj, target) {
    -    if (obj == null) return false;
    -    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    -    return any(obj, function(value) {
    -      return value === target;
    -    });
    -  };
    +
      function createReduce(dir) {
    @@ -509,17 +466,24 @@ Aliased as include.

    -

    Invoke a method (with arguments) on every item in a collection.

    +

    Optimized iterator function as using arguments.length +in the main function will deoptimize the, see #1991.

    -
      _.invoke = function(obj, method) {
    -    var args = slice.call(arguments, 2);
    -    var isFunc = _.isFunction(method);
    -    return _.map(obj, function(value) {
    -      return (isFunc ? method : value[method]).apply(value, args);
    -    });
    -  };
    +
        function iterator(obj, iteratee, memo, keys, index, length) {
    +      for (; index >= 0 && index < length; index += dir) {
    +        var currentKey = keys ? keys[index] : index;
    +        memo = iteratee(memo, obj[currentKey], currentKey, obj);
    +      }
    +      return memo;
    +    }
    +
    +    return function(obj, iteratee, memo, context) {
    +      iteratee = optimizeCb(iteratee, context, 4);
    +      var keys = !isArrayLike(obj) && _.keys(obj),
    +          length = (keys || obj).length,
    +          index = dir > 0 ? 0 : length - 1;
    @@ -530,13 +494,17 @@ Aliased as include.

    -

    Convenience version of a common use case of map: fetching a property.

    +

    Determine the initial value if none is provided.

    -
      _.pluck = function(obj, key) {
    -    return _.map(obj, function(value){ return value[key]; });
    -  };
    +
          if (arguments.length < 3) {
    +        memo = obj[keys ? keys[index] : index];
    +        index += dir;
    +      }
    +      return iterator(obj, iteratee, memo, keys, index, length);
    +    };
    +  }
    @@ -547,20 +515,12 @@ Aliased as include.

    -

    Convenience version of a common use case of filter: selecting only objects -containing specific key:value pairs.

    +

    Reduce builds up a single result from a list of values, aka inject, +or foldl.

    -
      _.where = function(obj, attrs, first) {
    -    if (_.isEmpty(attrs)) return first ? void 0 : [];
    -    return _[first ? 'find' : 'filter'](obj, function(value) {
    -      for (var key in attrs) {
    -        if (attrs[key] !== value[key]) return false;
    -      }
    -      return true;
    -    });
    -  };
    +
      _.reduce = _.foldl = _.inject = createReduce(1);
    @@ -571,14 +531,11 @@ containing specific key:value pairs.

    -

    Convenience version of a common use case of find: getting the first object -containing specific key:value pairs.

    +

    The right-associative version of reduce, also known as foldr.

    -
      _.findWhere = function(obj, attrs) {
    -    return _.where(obj, attrs, true);
    -  };
    +
      _.reduceRight = _.foldr = createReduce(-1);
    @@ -589,23 +546,18 @@ containing specific key:value pairs.

    -

    Return the maximum element or (element-based computation). -Can't optimize arrays of integers longer than 65,535 elements. -See WebKit Bug 80797

    +

    Return the first value which passes a truth test. Aliased as detect.

    -
      _.max = function(obj, iterator, context) {
    -    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
    -      return Math.max.apply(Math, obj);
    +            
      _.find = _.detect = function(obj, predicate, context) {
    +    var key;
    +    if (isArrayLike(obj)) {
    +      key = _.findIndex(obj, predicate, context);
    +    } else {
    +      key = _.findKey(obj, predicate, context);
         }
    -    if (!iterator && _.isEmpty(obj)) return -Infinity;
    -    var result = {computed : -Infinity, value: -Infinity};
    -    each(obj, function(value, index, list) {
    -      var computed = iterator ? iterator.call(context, value, index, list) : value;
    -      computed > result.computed && (result = {value : value, computed : computed});
    -    });
    -    return result.value;
    +    if (key !== void 0 && key !== -1) return obj[key];
       };
    @@ -617,21 +569,18 @@ See WebKit Bug 80797
    -

    Return the minimum element (or element-based computation).

    +

    Return all the elements that pass a truth test. +Aliased as select.

    -
      _.min = function(obj, iterator, context) {
    -    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
    -      return Math.min.apply(Math, obj);
    -    }
    -    if (!iterator && _.isEmpty(obj)) return Infinity;
    -    var result = {computed : Infinity, value: Infinity};
    -    each(obj, function(value, index, list) {
    -      var computed = iterator ? iterator.call(context, value, index, list) : value;
    -      computed < result.computed && (result = {value : value, computed : computed});
    +            
      _.filter = _.select = function(obj, predicate, context) {
    +    var results = [];
    +    predicate = cb(predicate, context);
    +    _.each(obj, function(value, index, list) {
    +      if (predicate(value, index, list)) results.push(value);
         });
    -    return result.value;
    +    return results;
       };
    @@ -643,21 +592,12 @@ See WebKit Bug 80797
    -

    Shuffle an array, using the modern version of the -Fisher-Yates shuffle.

    +

    Return all the elements for which a truth test fails.

    -
      _.shuffle = function(obj) {
    -    var rand;
    -    var index = 0;
    -    var shuffled = [];
    -    each(obj, function(value) {
    -      rand = _.random(index++);
    -      shuffled[index - 1] = shuffled[rand];
    -      shuffled[rand] = value;
    -    });
    -    return shuffled;
    +            
      _.reject = function(obj, predicate, context) {
    +    return _.filter(obj, _.negate(cb(predicate)), context);
       };
    @@ -669,17 +609,20 @@ See WebKit Bug 80797
    -

    Sample n random values from an array. -If n is not specified, returns a single random element from the array. -The internal guard argument allows it to work with map.

    +

    Determine whether all of the elements match a truth test. +Aliased as all.

    -
      _.sample = function(obj, n, guard) {
    -    if (arguments.length < 2 || guard) {
    -      return obj[_.random(obj.length - 1)];
    +            
      _.every = _.all = function(obj, predicate, context) {
    +    predicate = cb(predicate, context);
    +    var keys = !isArrayLike(obj) && _.keys(obj),
    +        length = (keys || obj).length;
    +    for (var index = 0; index < length; index++) {
    +      var currentKey = keys ? keys[index] : index;
    +      if (!predicate(obj[currentKey], currentKey, obj)) return false;
         }
    -    return _.shuffle(obj).slice(0, Math.max(0, n));
    +    return true;
       };
    @@ -691,12 +634,20 @@ The internal guard argument allows it to work with map
    -

    An internal function to generate lookup iterators.

    +

    Determine if at least one element in the object matches a truth test. +Aliased as any.

    -
      var lookupIterator = function(value) {
    -    return _.isFunction(value) ? value : function(obj){ return obj[value]; };
    +            
      _.some = _.any = function(obj, predicate, context) {
    +    predicate = cb(predicate, context);
    +    var keys = !isArrayLike(obj) && _.keys(obj),
    +        length = (keys || obj).length;
    +    for (var index = 0; index < length; index++) {
    +      var currentKey = keys ? keys[index] : index;
    +      if (predicate(obj[currentKey], currentKey, obj)) return true;
    +    }
    +    return false;
       };
    @@ -708,27 +659,15 @@ The internal guard argument allows it to work with map
    -

    Sort the object's values by a criterion produced by an iterator.

    +

    Determine if the array or object contains a given item (using ===). +Aliased as includes and include.

    -
      _.sortBy = function(obj, value, context) {
    -    var iterator = lookupIterator(value);
    -    return _.pluck(_.map(obj, function(value, index, list) {
    -      return {
    -        value: value,
    -        index: index,
    -        criteria: iterator.call(context, value, index, list)
    -      };
    -    }).sort(function(left, right) {
    -      var a = left.criteria;
    -      var b = right.criteria;
    -      if (a !== b) {
    -        if (a > b || a === void 0) return 1;
    -        if (a < b || b === void 0) return -1;
    -      }
    -      return left.index - right.index;
    -    }), 'value');
    +            
      _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
    +    if (!isArrayLike(obj)) obj = _.values(obj);
    +    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
    +    return _.indexOf(obj, item, fromIndex) >= 0;
       };
    @@ -740,20 +679,17 @@ The internal guard argument allows it to work with map
    -

    An internal function used for aggregate "group by" operations.

    +

    Invoke a method (with arguments) on every item in a collection.

    -
      var group = function(behavior) {
    -    return function(obj, value, context) {
    -      var result = {};
    -      var iterator = value == null ? _.identity : lookupIterator(value);
    -      each(obj, function(value, index) {
    -        var key = iterator.call(context, value, index, obj);
    -        behavior(result, key, value);
    -      });
    -      return result;
    -    };
    +            
      _.invoke = function(obj, method) {
    +    var args = slice.call(arguments, 2);
    +    var isFunc = _.isFunction(method);
    +    return _.map(obj, function(value) {
    +      var func = isFunc ? method : value[method];
    +      return func == null ? func : func.apply(value, args);
    +    });
       };
    @@ -765,14 +701,13 @@ The internal guard argument allows it to work with map
    -

    Groups the object's values by a criterion. Pass either a string attribute -to group by, or a function that returns the criterion.

    +

    Convenience version of a common use case of map: fetching a property.

    -
      _.groupBy = group(function(result, key, value) {
    -    (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
    -  });
    +
      _.pluck = function(obj, key) {
    +    return _.map(obj, _.property(key));
    +  };
    @@ -783,14 +718,14 @@ to group by, or a function that returns the criterion.

    -

    Indexes the object's values by a criterion, similar to groupBy, but for -when you know that your index values will be unique.

    +

    Convenience version of a common use case of filter: selecting only objects +containing specific key:value pairs.

    -
      _.indexBy = group(function(result, key, value) {
    -    result[key] = value;
    -  });
    +
      _.where = function(obj, attrs) {
    +    return _.filter(obj, _.matcher(attrs));
    +  };
    @@ -801,15 +736,14 @@ when you know that your index values will be unique.

    -

    Counts instances of an object that group by a certain criterion. Pass -either a string attribute to count by, or a function that returns the -criterion.

    +

    Convenience version of a common use case of find: getting the first object +containing specific key:value pairs.

    -
      _.countBy = group(function(result, key) {
    -    _.has(result, key) ? result[key]++ : result[key] = 1;
    -  });
    +
      _.findWhere = function(obj, attrs) {
    +    return _.find(obj, _.matcher(attrs));
    +  };
    @@ -820,20 +754,32 @@ criterion.

    -

    Use a comparator function to figure out the smallest index at which -an object should be inserted so as to maintain order. Uses binary search.

    +

    Return the maximum element (or element-based computation).

    -
      _.sortedIndex = function(array, obj, iterator, context) {
    -    iterator = iterator == null ? _.identity : lookupIterator(iterator);
    -    var value = iterator.call(context, obj);
    -    var low = 0, high = array.length;
    -    while (low < high) {
    -      var mid = (low + high) >>> 1;
    -      iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
    +            
      _.max = function(obj, iteratee, context) {
    +    var result = -Infinity, lastComputed = -Infinity,
    +        value, computed;
    +    if (iteratee == null && obj != null) {
    +      obj = isArrayLike(obj) ? obj : _.values(obj);
    +      for (var i = 0, length = obj.length; i < length; i++) {
    +        value = obj[i];
    +        if (value > result) {
    +          result = value;
    +        }
    +      }
    +    } else {
    +      iteratee = cb(iteratee, context);
    +      _.each(obj, function(value, index, list) {
    +        computed = iteratee(value, index, list);
    +        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
    +          result = value;
    +          lastComputed = computed;
    +        }
    +      });
         }
    -    return low;
    +    return result;
       };
    @@ -845,15 +791,32 @@ an object should be inserted so as to maintain order. Uses binary search.

    -

    Safely create a real, live array from anything iterable.

    +

    Return the minimum element (or element-based computation).

    -
      _.toArray = function(obj) {
    -    if (!obj) return [];
    -    if (_.isArray(obj)) return slice.call(obj);
    -    if (obj.length === +obj.length) return _.map(obj, _.identity);
    -    return _.values(obj);
    +            
      _.min = function(obj, iteratee, context) {
    +    var result = Infinity, lastComputed = Infinity,
    +        value, computed;
    +    if (iteratee == null && obj != null) {
    +      obj = isArrayLike(obj) ? obj : _.values(obj);
    +      for (var i = 0, length = obj.length; i < length; i++) {
    +        value = obj[i];
    +        if (value < result) {
    +          result = value;
    +        }
    +      }
    +    } else {
    +      iteratee = cb(iteratee, context);
    +      _.each(obj, function(value, index, list) {
    +        computed = iteratee(value, index, list);
    +        if (computed < lastComputed || computed === Infinity && result === Infinity) {
    +          result = value;
    +          lastComputed = computed;
    +        }
    +      });
    +    }
    +    return result;
       };
    @@ -865,13 +828,21 @@ an object should be inserted so as to maintain order. Uses binary search.

    -

    Return the number of elements in an object.

    +

    Shuffle a collection, using the modern version of the +Fisher-Yates shuffle.

    -
      _.size = function(obj) {
    -    if (obj == null) return 0;
    -    return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
    +            
      _.shuffle = function(obj) {
    +    var set = isArrayLike(obj) ? obj : _.values(obj);
    +    var length = set.length;
    +    var shuffled = Array(length);
    +    for (var index = 0, rand; index < length; index++) {
    +      rand = _.random(0, index);
    +      if (rand !== index) shuffled[index] = shuffled[rand];
    +      shuffled[rand] = set[index];
    +    }
    +    return shuffled;
       };
    @@ -880,13 +851,23 @@ an object should be inserted so as to maintain order. Uses binary search.

  • -
    +
    -

    Array Functions

    +

    Sample n random values from a collection. +If n is not specified, returns a single random element. +The internal guard argument allows it to work with map.

    +
      _.sample = function(obj, n, guard) {
    +    if (n == null || guard) {
    +      if (!isArrayLike(obj)) obj = _.values(obj);
    +      return obj[_.random(obj.length - 1)];
    +    }
    +    return _.shuffle(obj).slice(0, Math.max(0, n));
    +  };
    +
  • @@ -896,15 +877,27 @@ an object should be inserted so as to maintain order. Uses binary search.

    -

    Get the first element of an array. Passing n will return the first N -values in the array. Aliased as head and take. The guard check -allows it to work with _.map.

    +

    Sort the object’s values by a criterion produced by an iteratee.

    -
      _.first = _.head = _.take = function(array, n, guard) {
    -    if (array == null) return void 0;
    -    return (n == null) || guard ? array[0] : slice.call(array, 0, n);
    +            
      _.sortBy = function(obj, iteratee, context) {
    +    iteratee = cb(iteratee, context);
    +    return _.pluck(_.map(obj, function(value, index, list) {
    +      return {
    +        value: value,
    +        index: index,
    +        criteria: iteratee(value, index, list)
    +      };
    +    }).sort(function(left, right) {
    +      var a = left.criteria;
    +      var b = right.criteria;
    +      if (a !== b) {
    +        if (a > b || a === void 0) return 1;
    +        if (a < b || b === void 0) return -1;
    +      }
    +      return left.index - right.index;
    +    }), 'value');
       };
    @@ -916,15 +909,20 @@ allows it to work with _.map.

    -

    Returns everything but the last entry of the array. Especially useful on -the arguments object. Passing n will return all the values in -the array, excluding the last N. The guard check allows it to work with -_.map.

    +

    An internal function used for aggregate “group by” operations.

    -
      _.initial = function(array, n, guard) {
    -    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
    +            
      var group = function(behavior) {
    +    return function(obj, iteratee, context) {
    +      var result = {};
    +      iteratee = cb(iteratee, context);
    +      _.each(obj, function(value, index) {
    +        var key = iteratee(value, index, obj);
    +        behavior(result, value, key);
    +      });
    +      return result;
    +    };
       };
    @@ -936,19 +934,14 @@ the array, excluding the last N. The guard check allows it to w
    -

    Get the last element of an array. Passing n will return the last N -values in the array. The guard check allows it to work with _.map.

    +

    Groups the object’s values by a criterion. Pass either a string attribute +to group by, or a function that returns the criterion.

    -
      _.last = function(array, n, guard) {
    -    if (array == null) return void 0;
    -    if ((n == null) || guard) {
    -      return array[array.length - 1];
    -    } else {
    -      return slice.call(array, Math.max(array.length - n, 0));
    -    }
    -  };
    +
      _.groupBy = group(function(result, value, key) {
    +    if (_.has(result, key)) result[key].push(value); else result[key] = [value];
    +  });
    @@ -959,16 +952,14 @@ values in the array. The guard check allows it to work with
    -

    Returns everything but the first entry of the array. Aliased as tail and drop. -Especially useful on the arguments object. Passing an n will return -the rest N values in the array. The guard -check allows it to work with _.map.

    +

    Indexes the object’s values by a criterion, similar to groupBy, but for +when you know that your index values will be unique.

    -
      _.rest = _.tail = _.drop = function(array, n, guard) {
    -    return slice.call(array, (n == null) || guard ? 1 : n);
    -  };
    +
      _.indexBy = group(function(result, value, key) {
    +    result[key] = value;
    +  });
    @@ -979,13 +970,15 @@ check allows it to work with _.map.

    -

    Trim out all falsy values from an array.

    +

    Counts instances of an object that group by a certain criterion. Pass +either a string attribute to count by, or a function that returns the +criterion.

    -
      _.compact = function(array) {
    -    return _.filter(array, _.identity);
    -  };
    +
      _.countBy = group(function(result, value, key) {
    +    if (_.has(result, key)) result[key]++; else result[key] = 1;
    +  });
    @@ -996,22 +989,15 @@ check allows it to work with _.map.

    -

    Internal implementation of a recursive flatten function.

    +

    Safely create a real, live array from anything iterable.

    -
      var flatten = function(input, shallow, output) {
    -    if (shallow && _.every(input, _.isArray)) {
    -      return concat.apply(output, input);
    -    }
    -    each(input, function(value) {
    -      if (_.isArray(value) || _.isArguments(value)) {
    -        shallow ? push.apply(output, value) : flatten(value, shallow, output);
    -      } else {
    -        output.push(value);
    -      }
    -    });
    -    return output;
    +            
      _.toArray = function(obj) {
    +    if (!obj) return [];
    +    if (_.isArray(obj)) return slice.call(obj);
    +    if (isArrayLike(obj)) return _.map(obj, _.identity);
    +    return _.values(obj);
       };
    @@ -1023,12 +1009,13 @@ check allows it to work with _.map.

    -

    Flatten out an array, either recursively (by default), or just one level.

    +

    Return the number of elements in an object.

    -
      _.flatten = function(array, shallow) {
    -    return flatten(array, shallow, []);
    +            
      _.size = function(obj) {
    +    if (obj == null) return 0;
    +    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
       };
    @@ -1040,65 +1027,51 @@ check allows it to work with _.map.

    -

    Return a version of the array that does not contain the specified value(s).

    +

    Split a collection into two arrays: one whose elements all satisfy the given +predicate, and one whose elements all do not satisfy the predicate.

    -
      _.without = function(array) {
    -    return _.difference(array, slice.call(arguments, 1));
    +            
      _.partition = function(obj, predicate, context) {
    +    predicate = cb(predicate, context);
    +    var pass = [], fail = [];
    +    _.each(obj, function(value, key, obj) {
    +      (predicate(value, key, obj) ? pass : fail).push(value);
    +    });
    +    return [pass, fail];
       };
    - - + +
  • - +
    -

    Produce a duplicate-free version of the array. If the array has already -been sorted, you have the option of using a faster algorithm. -Aliased as unique.

    +

    Trim out all falsy values from an object.

    -
      _.uniq = _.unique = function(array, isSorted, iterator, context) {
    -    if (_.isFunction(isSorted)) {
    -      context = iterator;
    -      iterator = isSorted;
    -      isSorted = false;
    -    }
    -    var initial = iterator ? _.map(array, iterator, context) : array;
    -    var results = [];
    -    var seen = [];
    -    each(initial, function(value, index) {
    -      if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
    -        seen.push(value);
    -        results.push(array[index]);
    -      }
    -    });
    -    return results;
    +            
      _.compact = function(obj) {
    +    return _.filter(obj, _.identity);
       };
  • +
  • - +
    -

    Produce an array that contains the union: each distinct element from all of -the passed-in arrays.

    +

    Array Functions

    -
      _.union = function() {
    -    return _.uniq(_.flatten(arguments, true));
    -  };
    -
  • @@ -1106,22 +1079,11 @@ the passed-in arrays.

    - +
    -

    Produce an array that contains every item shared between all the -passed-in arrays.

    - +
    -
      _.intersection = function(array) {
    -    var rest = slice.call(arguments, 1);
    -    return _.filter(_.uniq(array), function(item) {
    -      return _.every(rest, function(other) {
    -        return _.indexOf(other, item) >= 0;
    -      });
    -    });
    -  };
    - @@ -1129,16 +1091,18 @@ passed-in arrays.

    - +
    -

    Take the difference between one array and a number of other arrays. -Only the elements present in just the first array will remain.

    +

    Get the first element of an array. Passing n will return the first N +values in the array. Aliased as head and take. The guard check +allows it to work with _.map.

    -
      _.difference = function(array) {
    -    var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
    -    return _.filter(array, function(value){ return !_.contains(rest, value); });
    +            
      _.first = _.head = _.take = function(array, n, guard) {
    +    if (array == null) return void 0;
    +    if (n == null || guard) return array[0];
    +    return _.initial(array, array.length - n);
       };
    @@ -1148,20 +1112,16 @@ Only the elements present in just the first array will remain.

    - +
    -

    Zip together multiple lists into a single array -- elements that share -an index go together.

    +

    Returns everything but the last entry of the array. Especially useful on +the arguments object. Passing n will return all the values in +the array, excluding the last N.

    -
      _.zip = function() {
    -    var length = _.max(_.pluck(arguments, "length").concat(0));
    -    var results = new Array(length);
    -    for (var i = 0; i < length; i++) {
    -      results[i] = _.pluck(arguments, '' + i);
    -    }
    -    return results;
    +            
      _.initial = function(array, n, guard) {
    +    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
       };
    @@ -1171,25 +1131,17 @@ an index go together.

    - +
    -

    Converts lists into objects. Pass either a single array of [key, value] -pairs, or two parallel arrays of the same length -- one of keys, and one of -the corresponding values.

    +

    Get the last element of an array. Passing n will return the last N +values in the array.

    -
      _.object = function(list, values) {
    -    if (list == null) return {};
    -    var result = {};
    -    for (var i = 0, length = list.length; i < length; i++) {
    -      if (values) {
    -        result[list[i]] = values[i];
    -      } else {
    -        result[list[i][0]] = list[i][1];
    -      }
    -    }
    -    return result;
    +            
      _.last = function(array, n, guard) {
    +    if (array == null) return void 0;
    +    if (n == null || guard) return array[array.length - 1];
    +    return _.rest(array, Math.max(0, array.length - n));
       };
    @@ -1199,34 +1151,19 @@ the corresponding values.

    - +
    -

    If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), -we need this function. Return the position of the first occurrence of an -item in an array, or -1 if the item is not included in the array. -Delegates to ECMAScript 5's native indexOf if available. -If the array is large and already in sort order, pass true -for isSorted to use binary search.

    +

    Returns everything but the first entry of the array. Aliased as tail and drop. +Especially useful on the arguments object. Passing an n will return +the rest N values in the array.

    -
      _.indexOf = function(array, item, isSorted) {
    -    if (array == null) return -1;
    -    var i = 0, length = array.length;
    -    if (isSorted) {
    -      if (typeof isSorted == 'number') {
    -        i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
    -      } else {
    -        i = _.sortedIndex(array, item);
    -        return array[i] === item ? i : -1;
    -      }
    -    }
    -    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
    -    for (; i < length; i++) if (array[i] === item) return i;
    -    return -1;
    +            
      _.rest = _.tail = _.drop = function(array, n, guard) {
    +    return slice.call(array, n == null || guard ? 1 : n);
       };
    - +
  • @@ -1235,20 +1172,15 @@ for isSorted to use binary search.

    -

    Delegates to ECMAScript 5's native lastIndexOf if available.

    +

    Internal implementation of a recursive flatten function.

  • -
      _.lastIndexOf = function(array, item, from) {
    -    if (array == null) return -1;
    -    var hasIndex = from != null;
    -    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
    -      return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
    -    }
    -    var i = (hasIndex ? from : array.length);
    -    while (i--) if (array[i] === item) return i;
    -    return -1;
    -  };
    +
      var flatten = function(input, shallow, strict, startIndex) {
    +    var output = [], idx = 0;
    +    for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
    +      var value = input[i];
    +      if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
    @@ -1259,29 +1191,21 @@ for isSorted to use binary search.

    -

    Generate an integer Array containing an arithmetic progression. A port of -the native Python range() function. See -the Python documentation.

    +

    flatten current level of array or arguments object

    -
      _.range = function(start, stop, step) {
    -    if (arguments.length <= 1) {
    -      stop = start || 0;
    -      start = 0;
    +            
            if (!shallow) value = flatten(value, shallow, strict);
    +        var j = 0, len = value.length;
    +        output.length += len;
    +        while (j < len) {
    +          output[idx++] = value[j++];
    +        }
    +      } else if (!strict) {
    +        output[idx++] = value;
    +      }
         }
    -    step = arguments[2] || 1;
    -
    -    var length = Math.max(Math.ceil((stop - start) / step), 0);
    -    var idx = 0;
    -    var range = new Array(length);
    -
    -    while(idx < length) {
    -      range[idx++] = start;
    -      start += step;
    -    }
    -
    -    return range;
    +    return output;
       };
    @@ -1290,13 +1214,17 @@ the native Python range() function. See
  • -
    +
    -

    Function (ahem) Functions

    +

    Flatten out an array, either recursively (by default), or just one level.

    +
      _.flatten = function(array, shallow) {
    +    return flatten(array, shallow, false);
    +  };
    +
  • @@ -1306,11 +1234,13 @@ the native Python range() function. See
    -

    Reusable constructor function for prototype setting.

    +

    Return a version of the array that does not contain the specified value(s).

    -
      var ctor = function(){};
    +
      _.without = function(array) {
    +    return _.difference(array, slice.call(arguments, 1));
    +  };
    @@ -1321,26 +1251,37 @@ the native Python range() function. See
    -

    Create a function bound to a given object (assigning this, and arguments, -optionally). Delegates to ECMAScript 5's native Function.bind if -available.

    +

    Produce a duplicate-free version of the array. If the array has already +been sorted, you have the option of using a faster algorithm. +Aliased as unique.

    -
      _.bind = function(func, context) {
    -    var args, bound;
    -    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    -    if (!_.isFunction(func)) throw new TypeError;
    -    args = slice.call(arguments, 2);
    -    return bound = function() {
    -      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
    -      ctor.prototype = func.prototype;
    -      var self = new ctor;
    -      ctor.prototype = null;
    -      var result = func.apply(self, args.concat(slice.call(arguments)));
    -      if (Object(result) === result) return result;
    -      return self;
    -    };
    +            
      _.uniq = _.unique = function(array, isSorted, iteratee, context) {
    +    if (!_.isBoolean(isSorted)) {
    +      context = iteratee;
    +      iteratee = isSorted;
    +      isSorted = false;
    +    }
    +    if (iteratee != null) iteratee = cb(iteratee, context);
    +    var result = [];
    +    var seen = [];
    +    for (var i = 0, length = getLength(array); i < length; i++) {
    +      var value = array[i],
    +          computed = iteratee ? iteratee(value, i, array) : value;
    +      if (isSorted) {
    +        if (!i || seen !== computed) result.push(value);
    +        seen = computed;
    +      } else if (iteratee) {
    +        if (!_.contains(seen, computed)) {
    +          seen.push(computed);
    +          result.push(value);
    +        }
    +      } else if (!_.contains(result, value)) {
    +        result.push(value);
    +      }
    +    }
    +    return result;
       };
    @@ -1352,16 +1293,13 @@ available.

    -

    Partially apply a function by creating a version that has had some of its -arguments pre-filled, without changing its dynamic this context.

    +

    Produce an array that contains the union: each distinct element from all of +the passed-in arrays.

    -
      _.partial = function(func) {
    -    var args = slice.call(arguments, 1);
    -    return function() {
    -      return func.apply(this, args.concat(slice.call(arguments)));
    -    };
    +            
      _.union = function() {
    +    return _.uniq(flatten(arguments, true, true));
       };
    @@ -1373,16 +1311,23 @@ arguments pre-filled, without changing its dynamic this context.

    -

    Bind all of an object's methods to that object. Useful for ensuring that -all callbacks defined on an object belong to it.

    +

    Produce an array that contains every item shared between all the +passed-in arrays.

    -
      _.bindAll = function(obj) {
    -    var funcs = slice.call(arguments, 1);
    -    if (funcs.length === 0) throw new Error("bindAll must be passed function names");
    -    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    -    return obj;
    +            
      _.intersection = function(array) {
    +    var result = [];
    +    var argsLength = arguments.length;
    +    for (var i = 0, length = getLength(array); i < length; i++) {
    +      var item = array[i];
    +      if (_.contains(result, item)) continue;
    +      for (var j = 1; j < argsLength; j++) {
    +        if (!_.contains(arguments[j], item)) break;
    +      }
    +      if (j === argsLength) result.push(item);
    +    }
    +    return result;
       };
    @@ -1394,17 +1339,16 @@ all callbacks defined on an object belong to it.

    -

    Memoize an expensive function by storing its results.

    +

    Take the difference between one array and a number of other arrays. +Only the elements present in just the first array will remain.

    -
      _.memoize = function(func, hasher) {
    -    var memo = {};
    -    hasher || (hasher = _.identity);
    -    return function() {
    -      var key = hasher.apply(this, arguments);
    -      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    -    };
    +            
      _.difference = function(array) {
    +    var rest = flatten(arguments, true, true, 1);
    +    return _.filter(array, function(value){
    +      return !_.contains(rest, value);
    +    });
       };
    @@ -1416,14 +1360,13 @@ all callbacks defined on an object belong to it.

    -

    Delays a function for the given number of milliseconds, and then calls -it with the arguments supplied.

    +

    Zip together multiple lists into a single array — elements that share +an index go together.

    -
      _.delay = function(func, wait) {
    -    var args = slice.call(arguments, 2);
    -    return setTimeout(function(){ return func.apply(null, args); }, wait);
    +            
      _.zip = function() {
    +    return _.unzip(arguments);
       };
    @@ -1435,13 +1378,19 @@ it with the arguments supplied.

    -

    Defers a function, scheduling it to run after the current call stack has -cleared.

    +

    Complement of _.zip. Unzip accepts an array of arrays and groups +each array’s elements on shared indices

    -
      _.defer = function(func) {
    -    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
    +            
      _.unzip = function(array) {
    +    var length = array && _.max(array, getLength).length || 0;
    +    var result = Array(length);
    +
    +    for (var index = 0; index < length; index++) {
    +      result[index] = _.pluck(array, index);
    +    }
    +    return result;
       };
    @@ -1453,40 +1402,22 @@ cleared.

    -

    Returns a function, that, when invoked, will only be triggered at most once -during a given window of time. Normally, the throttled function will run -as much as it can, without ever going more than once per wait duration; -but if you'd like to disable the execution on the leading edge, pass -{leading: false}. To disable execution on the trailing edge, ditto.

    +

    Converts lists into objects. Pass either a single array of [key, value] +pairs, or two parallel arrays of the same length — one of keys, and one of +the corresponding values.

    -
      _.throttle = function(func, wait, options) {
    -    var context, args, result;
    -    var timeout = null;
    -    var previous = 0;
    -    options || (options = {});
    -    var later = function() {
    -      previous = options.leading === false ? 0 : new Date;
    -      timeout = null;
    -      result = func.apply(context, args);
    -    };
    -    return function() {
    -      var now = new Date;
    -      if (!previous && options.leading === false) previous = now;
    -      var remaining = wait - (now - previous);
    -      context = this;
    -      args = arguments;
    -      if (remaining <= 0) {
    -        clearTimeout(timeout);
    -        timeout = null;
    -        previous = now;
    -        result = func.apply(context, args);
    -      } else if (!timeout && options.trailing !== false) {
    -        timeout = setTimeout(later, remaining);
    +            
      _.object = function(list, values) {
    +    var result = {};
    +    for (var i = 0, length = getLength(list); i < length; i++) {
    +      if (values) {
    +        result[list[i]] = values[i];
    +      } else {
    +        result[list[i][0]] = list[i][1];
           }
    -      return result;
    -    };
    +    }
    +    return result;
       };
    @@ -1498,36 +1429,21 @@ but if you'd like to disable the execution on the leading edge, pass
    -

    Returns a function, that, as long as it continues to be invoked, will not -be triggered. The function will be called after it stops being called for -N milliseconds. If immediate is passed, trigger the function on the -leading edge, instead of the trailing.

    +

    Generator function to create the findIndex and findLastIndex functions

    -
      _.debounce = function(func, wait, immediate) {
    -    var timeout, args, context, timestamp, result;
    -    return function() {
    -      context = this;
    -      args = arguments;
    -      timestamp = new Date();
    -      var later = function() {
    -        var last = (new Date()) - timestamp;
    -        if (last < wait) {
    -          timeout = setTimeout(later, wait - last);
    -        } else {
    -          timeout = null;
    -          if (!immediate) result = func.apply(context, args);
    -        }
    -      };
    -      var callNow = immediate && !timeout;
    -      if (!timeout) {
    -        timeout = setTimeout(later, wait);
    +            
      function createPredicateIndexFinder(dir) {
    +    return function(array, predicate, context) {
    +      predicate = cb(predicate, context);
    +      var length = getLength(array);
    +      var index = dir > 0 ? 0 : length - 1;
    +      for (; index >= 0 && index < length; index += dir) {
    +        if (predicate(array[index], index, array)) return index;
           }
    -      if (callNow) result = func.apply(context, args);
    -      return result;
    +      return -1;
         };
    -  };
    + }
    @@ -1538,21 +1454,12 @@ leading edge, instead of the trailing.

    -

    Returns a function that will be executed at most one time, no matter how -often you call it. Useful for lazy initialization.

    +

    Returns the first index on an array-like that passes a predicate test

    -
      _.once = function(func) {
    -    var ran = false, memo;
    -    return function() {
    -      if (ran) return memo;
    -      ran = true;
    -      memo = func.apply(this, arguments);
    -      func = null;
    -      return memo;
    -    };
    -  };
    +
      _.findIndex = createPredicateIndexFinder(1);
    +  _.findLastIndex = createPredicateIndexFinder(-1);
    @@ -1563,18 +1470,20 @@ often you call it. Useful for lazy initialization.

    -

    Returns the first function passed as an argument to the second, -allowing you to adjust arguments, run code before and after, and -conditionally execute the original function.

    +

    Use a comparator function to figure out the smallest index at which +an object should be inserted so as to maintain order. Uses binary search.

    -
      _.wrap = function(func, wrapper) {
    -    return function() {
    -      var args = [func];
    -      push.apply(args, arguments);
    -      return wrapper.apply(this, args);
    -    };
    +            
      _.sortedIndex = function(array, obj, iteratee, context) {
    +    iteratee = cb(iteratee, context, 1);
    +    var value = iteratee(obj);
    +    var low = 0, high = getLength(array);
    +    while (low < high) {
    +      var mid = Math.floor((low + high) / 2);
    +      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    +    }
    +    return low;
       };
    @@ -1586,21 +1495,33 @@ conditionally execute the original function.

    -

    Returns a function that is the composition of a list of functions, each -consuming the return value of the function that follows.

    +

    Generator function to create the indexOf and lastIndexOf functions

    -
      _.compose = function() {
    -    var funcs = arguments;
    -    return function() {
    -      var args = arguments;
    -      for (var i = funcs.length - 1; i >= 0; i--) {
    -        args = [funcs[i].apply(this, args)];
    +            
      function createIndexFinder(dir, predicateFind, sortedIndex) {
    +    return function(array, item, idx) {
    +      var i = 0, length = getLength(array);
    +      if (typeof idx == 'number') {
    +        if (dir > 0) {
    +            i = idx >= 0 ? idx : Math.max(idx + length, i);
    +        } else {
    +            length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
    +        }
    +      } else if (sortedIndex && idx && length) {
    +        idx = sortedIndex(array, item);
    +        return array[idx] === item ? idx : -1;
           }
    -      return args[0];
    +      if (item !== item) {
    +        idx = predicateFind(slice.call(array, i, length), _.isNaN);
    +        return idx >= 0 ? idx + i : -1;
    +      }
    +      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
    +        if (array[idx] === item) return idx;
    +      }
    +      return -1;
         };
    -  };
    + }
    @@ -1611,17 +1532,15 @@ consuming the return value of the function that follows.

    -

    Returns a function that will only be executed after being called N times.

    +

    Return the position of the first occurrence of an item in an array, +or -1 if the item is not included in the array. +If the array is large and already in sort order, pass true +for isSorted to use binary search.

    -
      _.after = function(times, func) {
    -    return function() {
    -      if (--times < 1) {
    -        return func.apply(this, arguments);
    -      }
    -    };
    -  };
    +
      _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
    +  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
    @@ -1629,13 +1548,32 @@ consuming the return value of the function that follows.

  • -
    +
    -

    Object Functions

    +

    Generate an integer Array containing an arithmetic progression. A port of +the native Python range() function. See +the Python documentation.

    +
      _.range = function(start, stop, step) {
    +    if (stop == null) {
    +      stop = start || 0;
    +      start = 0;
    +    }
    +    step = step || 1;
    +
    +    var length = Math.max(Math.ceil((stop - start) / step), 0);
    +    var range = Array(length);
    +
    +    for (var idx = 0; idx < length; idx++, start += step) {
    +      range[idx] = start;
    +    }
    +
    +    return range;
    +  };
    +
  • @@ -1645,18 +1583,10 @@ consuming the return value of the function that follows.

    -

    Retrieve the names of an object's properties. -Delegates to ECMAScript 5's native Object.keys

    +

    Function (ahem) Functions

    -
      _.keys = nativeKeys || function(obj) {
    -    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    -    var keys = [];
    -    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    -    return keys;
    -  };
    - @@ -1666,20 +1596,9 @@ Delegates to ECMAScript 5's native Object.keys
    -

    Retrieve the values of an object's properties.

    - +
    -
      _.values = function(obj) {
    -    var keys = _.keys(obj);
    -    var length = keys.length;
    -    var values = new Array(length);
    -    for (var i = 0; i < length; i++) {
    -      values[i] = obj[keys[i]];
    -    }
    -    return values;
    -  };
    - @@ -1689,18 +1608,17 @@ Delegates to ECMAScript 5's native Object.keys
    -

    Convert an object into a list of [key, value] pairs.

    +

    Determines whether to execute a function as a constructor +or a normal function with the provided arguments

    -
      _.pairs = function(obj) {
    -    var keys = _.keys(obj);
    -    var length = keys.length;
    -    var pairs = new Array(length);
    -    for (var i = 0; i < length; i++) {
    -      pairs[i] = [keys[i], obj[keys[i]]];
    -    }
    -    return pairs;
    +            
      var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
    +    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
    +    var self = baseCreate(sourceFunc.prototype);
    +    var result = sourceFunc.apply(self, args);
    +    if (_.isObject(result)) return result;
    +    return self;
       };
    @@ -1712,17 +1630,20 @@ Delegates to ECMAScript 5's native Object.keys
    -

    Invert the keys and values of an object. The values must be serializable.

    +

    Create a function bound to a given object (assigning this, and arguments, +optionally). Delegates to ECMAScript 5‘s native Function.bind if +available.

    -
      _.invert = function(obj) {
    -    var result = {};
    -    var keys = _.keys(obj);
    -    for (var i = 0, length = keys.length; i < length; i++) {
    -      result[obj[keys[i]]] = keys[i];
    -    }
    -    return result;
    +            
      _.bind = function(func, context) {
    +    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    +    if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
    +    var args = slice.call(arguments, 2);
    +    var bound = function() {
    +      return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
    +    };
    +    return bound;
       };
    @@ -1734,17 +1655,24 @@ Delegates to ECMAScript 5's native Object.keys
    -

    Return a sorted list of the function names available on the object. -Aliased as methods

    +

    Partially apply a function by creating a version that has had some of its +arguments pre-filled, without changing its dynamic this context. _ acts +as a placeholder, allowing any combination of arguments to be pre-filled.

    -
      _.functions = _.methods = function(obj) {
    -    var names = [];
    -    for (var key in obj) {
    -      if (_.isFunction(obj[key])) names.push(key);
    -    }
    -    return names.sort();
    +            
      _.partial = function(func) {
    +    var boundArgs = slice.call(arguments, 1);
    +    var bound = function() {
    +      var position = 0, length = boundArgs.length;
    +      var args = Array(length);
    +      for (var i = 0; i < length; i++) {
    +        args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
    +      }
    +      while (position < arguments.length) args.push(arguments[position++]);
    +      return executeBound(func, bound, this, this, args);
    +    };
    +    return bound;
       };
    @@ -1756,19 +1684,20 @@ Aliased as methods

    -

    Extend a given object with all the properties in passed-in object(s).

    +

    Bind a number of an object’s methods to that object. Remaining arguments +are the method names to be bound. Useful for ensuring that all callbacks +defined on an object belong to it.

    -
      _.extend = function(obj) {
    -    each(slice.call(arguments, 1), function(source) {
    -      if (source) {
    -        for (var prop in source) {
    -          obj[prop] = source[prop];
    -        }
    -      }
    -    });
    -    return obj;
    +            
      _.bindAll = function(obj) {
    +    var i, length = arguments.length, key;
    +    if (length <= 1) throw new Error('bindAll must be passed function names');
    +    for (i = 1; i < length; i++) {
    +      key = arguments[i];
    +      obj[key] = _.bind(obj[key], obj);
    +    }
    +    return obj;
       };
    @@ -1780,17 +1709,19 @@ Aliased as methods

    -

    Return a copy of the object only containing the whitelisted properties.

    +

    Memoize an expensive function by storing its results.

    -
      _.pick = function(obj) {
    -    var copy = {};
    -    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
    -    each(keys, function(key) {
    -      if (key in obj) copy[key] = obj[key];
    -    });
    -    return copy;
    +            
      _.memoize = function(func, hasher) {
    +    var memoize = function(key) {
    +      var cache = memoize.cache;
    +      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
    +      if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
    +      return cache[address];
    +    };
    +    memoize.cache = {};
    +    return memoize;
       };
    @@ -1802,17 +1733,16 @@ Aliased as methods

    -

    Return a copy of the object without the blacklisted properties.

    +

    Delays a function for the given number of milliseconds, and then calls +it with the arguments supplied.

    -
      _.omit = function(obj) {
    -    var copy = {};
    -    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
    -    for (var key in obj) {
    -      if (!_.contains(keys, key)) copy[key] = obj[key];
    -    }
    -    return copy;
    +            
      _.delay = function(func, wait) {
    +    var args = slice.call(arguments, 2);
    +    return setTimeout(function(){
    +      return func.apply(null, args);
    +    }, wait);
       };
    @@ -1824,20 +1754,12 @@ Aliased as methods

    -

    Fill in a given object with default properties.

    +

    Defers a function, scheduling it to run after the current call stack has +cleared.

    -
      _.defaults = function(obj) {
    -    each(slice.call(arguments, 1), function(source) {
    -      if (source) {
    -        for (var prop in source) {
    -          if (obj[prop] === void 0) obj[prop] = source[prop];
    -        }
    -      }
    -    });
    -    return obj;
    -  };
    +
      _.defer = _.partial(_.delay, _, 1);
    @@ -1848,13 +1770,44 @@ Aliased as methods

    -

    Create a (shallow-cloned) duplicate of an object.

    +

    Returns a function, that, when invoked, will only be triggered at most once +during a given window of time. Normally, the throttled function will run +as much as it can, without ever going more than once per wait duration; +but if you’d like to disable the execution on the leading edge, pass +{leading: false}. To disable execution on the trailing edge, ditto.

    -
      _.clone = function(obj) {
    -    if (!_.isObject(obj)) return obj;
    -    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    +            
      _.throttle = function(func, wait, options) {
    +    var context, args, result;
    +    var timeout = null;
    +    var previous = 0;
    +    if (!options) options = {};
    +    var later = function() {
    +      previous = options.leading === false ? 0 : _.now();
    +      timeout = null;
    +      result = func.apply(context, args);
    +      if (!timeout) context = args = null;
    +    };
    +    return function() {
    +      var now = _.now();
    +      if (!previous && options.leading === false) previous = now;
    +      var remaining = wait - (now - previous);
    +      context = this;
    +      args = arguments;
    +      if (remaining <= 0 || remaining > wait) {
    +        if (timeout) {
    +          clearTimeout(timeout);
    +          timeout = null;
    +        }
    +        previous = now;
    +        result = func.apply(context, args);
    +        if (!timeout) context = args = null;
    +      } else if (!timeout && options.trailing !== false) {
    +        timeout = setTimeout(later, remaining);
    +      }
    +      return result;
    +    };
       };
    @@ -1866,15 +1819,43 @@ Aliased as methods

    -

    Invokes interceptor with the obj, and then returns obj. -The primary purpose of this method is to "tap into" a method chain, in -order to perform operations on intermediate results within the chain.

    +

    Returns a function, that, as long as it continues to be invoked, will not +be triggered. The function will be called after it stops being called for +N milliseconds. If immediate is passed, trigger the function on the +leading edge, instead of the trailing.

    -
      _.tap = function(obj, interceptor) {
    -    interceptor(obj);
    -    return obj;
    +            
      _.debounce = function(func, wait, immediate) {
    +    var timeout, args, context, timestamp, result;
    +
    +    var later = function() {
    +      var last = _.now() - timestamp;
    +
    +      if (last < wait && last >= 0) {
    +        timeout = setTimeout(later, wait - last);
    +      } else {
    +        timeout = null;
    +        if (!immediate) {
    +          result = func.apply(context, args);
    +          if (!timeout) context = args = null;
    +        }
    +      }
    +    };
    +
    +    return function() {
    +      context = this;
    +      args = arguments;
    +      timestamp = _.now();
    +      var callNow = immediate && !timeout;
    +      if (!timeout) timeout = setTimeout(later, wait);
    +      if (callNow) {
    +        result = func.apply(context, args);
    +        context = args = null;
    +      }
    +
    +      return result;
    +    };
       };
    @@ -1886,11 +1867,15 @@ order to perform operations on intermediate results within the chain.

    -

    Internal recursive comparison function for isEqual.

    +

    Returns the first function passed as an argument to the second, +allowing you to adjust arguments, run code before and after, and +conditionally execute the original function.

    -
      var eq = function(a, b, aStack, bStack) {
    +
      _.wrap = function(func, wrapper) {
    +    return _.partial(wrapper, func);
    +  };
    @@ -1901,12 +1886,15 @@ order to perform operations on intermediate results within the chain.

    -

    Identical objects are equal. 0 === -0, but they aren't identical. -See the Harmony egal proposal.

    +

    Returns a negated version of the passed-in predicate.

    -
        if (a === b) return a !== 0 || 1 / a == 1 / b;
    +
      _.negate = function(predicate) {
    +    return function() {
    +      return !predicate.apply(this, arguments);
    +    };
    +  };
    @@ -1917,11 +1905,21 @@ See the Harmony
    -

    A strict comparison is necessary because null == undefined.

    +

    Returns a function that is the composition of a list of functions, each +consuming the return value of the function that follows.

    -
        if (a == null || b == null) return a === b;
    +
      _.compose = function() {
    +    var args = arguments;
    +    var start = args.length - 1;
    +    return function() {
    +      var i = start;
    +      var result = args[start].apply(this, arguments);
    +      while (i--) result = args[i].call(this, result);
    +      return result;
    +    };
    +  };
    @@ -1932,12 +1930,17 @@ See the Harmony
    -

    Unwrap any wrapped objects.

    +

    Returns a function that will only be executed on and after the Nth call.

    -
        if (a instanceof _) a = a._wrapped;
    -    if (b instanceof _) b = b._wrapped;
    +
      _.after = function(times, func) {
    +    return function() {
    +      if (--times < 1) {
    +        return func.apply(this, arguments);
    +      }
    +    };
    +  };
    @@ -1948,13 +1951,20 @@ See the Harmony
    -

    Compare [[Class]] names.

    +

    Returns a function that will only be executed up to (but not including) the Nth call.

    -
        var className = toString.call(a);
    -    if (className != toString.call(b)) return false;
    -    switch (className) {
    +
      _.before = function(times, func) {
    +    var memo;
    +    return function() {
    +      if (--times > 0) {
    +        memo = func.apply(this, arguments);
    +      }
    +      if (times <= 1) func = null;
    +      return memo;
    +    };
    +  };
    @@ -1965,11 +1975,12 @@ See the Harmony
    -

    Strings, numbers, dates, and booleans are compared by value.

    +

    Returns a function that will be executed at most one time, no matter how +often you call it. Useful for lazy initialization.

    -
          case '[object String]':
    +
      _.once = _.partial(_.before, 2);
    @@ -1980,14 +1991,10 @@ See the Harmony
    -

    Primitives and their corresponding object wrappers are equivalent; thus, "5" is -equivalent to new String("5").

    +

    Object Functions

    -
            return a == String(b);
    -      case '[object Number]':
    - @@ -1997,15 +2004,9 @@ equivalent to new String("5").

    -

    NaNs are equivalent, but non-reflexive. An egal comparison is performed for -other numeric values.

    - +
    -
            return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
    -      case '[object Date]':
    -      case '[object Boolean]':
    - @@ -2015,13 +2016,18 @@ other numeric values.

    -

    Coerce dates and booleans to numeric primitive values. Dates are compared by their -millisecond representations. Note that invalid dates with millisecond representations -of NaN are not equivalent.

    +

    Keys in IE < 9 that won’t be iterated by for key in ... and thus missed.

    -
            return +a == +b;
    +
      var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
    +  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
    +                      'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
    +
    +  function collectNonEnumProps(obj, keys) {
    +    var nonEnumIdx = nonEnumerableProps.length;
    +    var constructor = obj.constructor;
    +    var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
    @@ -2032,17 +2038,20 @@ of NaN are not equivalent.

    -

    RegExps are compared by their source patterns and flags.

    +

    Constructor is a special case.

    -
          case '[object RegExp]':
    -        return a.source == b.source &&
    -               a.global == b.global &&
    -               a.multiline == b.multiline &&
    -               a.ignoreCase == b.ignoreCase;
    +            
        var prop = 'constructor';
    +    if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
    +
    +    while (nonEnumIdx--) {
    +      prop = nonEnumerableProps[nonEnumIdx];
    +      if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
    +        keys.push(prop);
    +      }
         }
    -    if (typeof a != 'object' || typeof b != 'object') return false;
    + }
    @@ -2053,13 +2062,16 @@ of NaN are not equivalent.

    -

    Assume equality for cyclic structures. The algorithm for detecting cyclic -structures is adapted from ES 5.1 section 15.12.3, abstract operation JO.

    +

    Retrieve the names of an object’s own properties. +Delegates to ECMAScript 5‘s native Object.keys

    -
        var length = aStack.length;
    -    while (length--) {
    +
      _.keys = function(obj) {
    +    if (!_.isObject(obj)) return [];
    +    if (nativeKeys) return nativeKeys(obj);
    +    var keys = [];
    +    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    @@ -2070,13 +2082,13 @@ structures is adapted from ES 5.1 section 15.12.3, abstract operation JO
    -

    Linear search. Performance is inversely proportional to the number of -unique nested structures.

    +

    Ahem, IE < 9.

    -
          if (aStack[length] == a) return bStack[length] == b;
    -    }
    +
        if (hasEnumBug) collectNonEnumProps(obj, keys);
    +    return keys;
    +  };
    @@ -2087,16 +2099,14 @@ unique nested structures.

    -

    Objects with different constructors are not equivalent, but Objects -from different frames are.

    +

    Retrieve all the property names of an object.

    -
        var aCtor = a.constructor, bCtor = b.constructor;
    -    if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
    -                             _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
    -      return false;
    -    }
    +
      _.allKeys = function(obj) {
    +    if (!_.isObject(obj)) return [];
    +    var keys = [];
    +    for (var key in obj) keys.push(key);
    @@ -2107,13 +2117,13 @@ from different frames are.

    -

    Add the first object to the stack of traversed objects.

    +

    Ahem, IE < 9.

    -
        aStack.push(a);
    -    bStack.push(b);
    -    var size = 0, result = true;
    +
        if (hasEnumBug) collectNonEnumProps(obj, keys);
    +    return keys;
    +  };
    @@ -2124,11 +2134,19 @@ from different frames are.

    -

    Recursively compare objects and arrays.

    +

    Retrieve the values of an object’s properties.

    -
        if (className == '[object Array]') {
    +
      _.values = function(obj) {
    +    var keys = _.keys(obj);
    +    var length = keys.length;
    +    var values = Array(length);
    +    for (var i = 0; i < length; i++) {
    +      values[i] = obj[keys[i]];
    +    }
    +    return values;
    +  };
    @@ -2139,13 +2157,23 @@ from different frames are.

    -

    Compare array lengths to determine if a deep comparison is necessary.

    +

    Returns the results of applying the iteratee to each element of the object +In contrast to _.map it returns an object

    -
          size = a.length;
    -      result = size == b.length;
    -      if (result) {
    +
      _.mapObject = function(obj, iteratee, context) {
    +    iteratee = cb(iteratee, context);
    +    var keys =  _.keys(obj),
    +          length = keys.length,
    +          results = {},
    +          currentKey;
    +      for (var index = 0; index < length; index++) {
    +        currentKey = keys[index];
    +        results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
    +      }
    +      return results;
    +  };
    @@ -2156,15 +2184,19 @@ from different frames are.

    -

    Deep compare the contents, ignoring non-numeric properties.

    +

    Convert an object into a list of [key, value] pairs.

    -
            while (size--) {
    -          if (!(result = eq(a[size], b[size], aStack, bStack))) break;
    -        }
    -      }
    -    } else {
    +
      _.pairs = function(obj) {
    +    var keys = _.keys(obj);
    +    var length = keys.length;
    +    var pairs = Array(length);
    +    for (var i = 0; i < length; i++) {
    +      pairs[i] = [keys[i], obj[keys[i]]];
    +    }
    +    return pairs;
    +  };
    @@ -2175,12 +2207,18 @@ from different frames are.

    -

    Deep compare objects.

    +

    Invert the keys and values of an object. The values must be serializable.

    -
          for (var key in a) {
    -        if (_.has(a, key)) {
    +
      _.invert = function(obj) {
    +    var result = {};
    +    var keys = _.keys(obj);
    +    for (var i = 0, length = keys.length; i < length; i++) {
    +      result[obj[keys[i]]] = keys[i];
    +    }
    +    return result;
    +  };
    @@ -2191,11 +2229,18 @@ from different frames are.

    -

    Count the expected number of properties.

    +

    Return a sorted list of the function names available on the object. +Aliased as methods

    -
              size++;
    +
      _.functions = _.methods = function(obj) {
    +    var names = [];
    +    for (var key in obj) {
    +      if (_.isFunction(obj[key])) names.push(key);
    +    }
    +    return names.sort();
    +  };
    @@ -2206,13 +2251,11 @@ from different frames are.

    -

    Deep compare each member.

    +

    Extend a given object with all the properties in passed-in object(s).

    -
              if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
    -        }
    -      }
    +
      _.extend = createAssigner(_.allKeys);
    @@ -2223,17 +2266,12 @@ from different frames are.

    -

    Ensure that both objects contain the same number of properties.

    +

    Assigns a given object with all the own properties in the passed-in object(s) +(https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)

    -
          if (result) {
    -        for (key in b) {
    -          if (_.has(b, key) && !(size--)) break;
    -        }
    -        result = !size;
    -      }
    -    }
    +
      _.extendOwn = _.assign = createAssigner(_.keys);
    @@ -2244,13 +2282,17 @@ from different frames are.

    -

    Remove the first object from the stack of traversed objects.

    +

    Returns the first key on an object that passes a predicate test

    -
        aStack.pop();
    -    bStack.pop();
    -    return result;
    +            
      _.findKey = function(obj, predicate, context) {
    +    predicate = cb(predicate, context);
    +    var keys = _.keys(obj), key;
    +    for (var i = 0, length = keys.length; i < length; i++) {
    +      key = keys[i];
    +      if (predicate(obj[key], key, obj)) return key;
    +    }
       };
    @@ -2262,12 +2304,27 @@ from different frames are.

    -

    Perform a deep comparison to check if two objects are equal.

    +

    Return a copy of the object only containing the whitelisted properties.

    -
      _.isEqual = function(a, b) {
    -    return eq(a, b, [], []);
    +            
      _.pick = function(object, oiteratee, context) {
    +    var result = {}, obj = object, iteratee, keys;
    +    if (obj == null) return result;
    +    if (_.isFunction(oiteratee)) {
    +      keys = _.allKeys(obj);
    +      iteratee = optimizeCb(oiteratee, context);
    +    } else {
    +      keys = flatten(arguments, false, false, 1);
    +      iteratee = function(value, key, obj) { return key in obj; };
    +      obj = Object(obj);
    +    }
    +    for (var i = 0, length = keys.length; i < length; i++) {
    +      var key = keys[i];
    +      var value = obj[key];
    +      if (iteratee(value, key, obj)) result[key] = value;
    +    }
    +    return result;
       };
    @@ -2279,16 +2336,20 @@ from different frames are.

    -

    Is a given array, string, or object empty? -An "empty" object has no enumerable own-properties.

    +

    Return a copy of the object without the blacklisted properties.

    -
      _.isEmpty = function(obj) {
    -    if (obj == null) return true;
    -    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
    -    for (var key in obj) if (_.has(obj, key)) return false;
    -    return true;
    +            
      _.omit = function(obj, iteratee, context) {
    +    if (_.isFunction(iteratee)) {
    +      iteratee = _.negate(iteratee);
    +    } else {
    +      var keys = _.map(flatten(arguments, false, false, 1), String);
    +      iteratee = function(value, key) {
    +        return !_.contains(keys, key);
    +      };
    +    }
    +    return _.pick(obj, iteratee, context);
       };
    @@ -2300,13 +2361,11 @@ An "empty" object has no enumerable own-properties.

    -

    Is a given value a DOM element?

    +

    Fill in a given object with default properties.

    -
      _.isElement = function(obj) {
    -    return !!(obj && obj.nodeType === 1);
    -  };
    +
      _.defaults = createAssigner(_.allKeys, true);
    @@ -2317,13 +2376,16 @@ An "empty" object has no enumerable own-properties.

    -

    Is a given value an array? -Delegates to ECMA5's native Array.isArray

    +

    Creates an object that inherits from the given prototype object. +If additional properties are provided then they will be added to the +created object.

    -
      _.isArray = nativeIsArray || function(obj) {
    -    return toString.call(obj) == '[object Array]';
    +            
      _.create = function(prototype, props) {
    +    var result = baseCreate(prototype);
    +    if (props) _.extendOwn(result, props);
    +    return result;
       };
    @@ -2335,12 +2397,13 @@ Delegates to ECMA5's native Array.isArray

    -

    Is a given variable an object?

    +

    Create a (shallow-cloned) duplicate of an object.

    -
      _.isObject = function(obj) {
    -    return obj === Object(obj);
    +            
      _.clone = function(obj) {
    +    if (!_.isObject(obj)) return obj;
    +    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
       };
    @@ -2352,15 +2415,16 @@ Delegates to ECMA5's native Array.isArray

    -

    Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.

    +

    Invokes interceptor with the obj, and then returns obj. +The primary purpose of this method is to “tap into” a method chain, in +order to perform operations on intermediate results within the chain.

    -
      each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
    -    _['is' + name] = function(obj) {
    -      return toString.call(obj) == '[object ' + name + ']';
    -    };
    -  });
    +
      _.tap = function(obj, interceptor) {
    +    interceptor(obj);
    +    return obj;
    +  };
    @@ -2371,16 +2435,20 @@ Delegates to ECMA5's native Array.isArray

    -

    Define a fallback version of the method in browsers (ahem, IE), where -there isn't any inspectable "Arguments" type.

    +

    Returns whether an object has a given set of key:value pairs.

    -
      if (!_.isArguments(arguments)) {
    -    _.isArguments = function(obj) {
    -      return !!(obj && _.has(obj, 'callee'));
    -    };
    -  }
    +
      _.isMatch = function(object, attrs) {
    +    var keys = _.keys(attrs), length = keys.length;
    +    if (object == null) return !length;
    +    var obj = Object(object);
    +    for (var i = 0; i < length; i++) {
    +      var key = keys[i];
    +      if (attrs[key] !== obj[key] || !(key in obj)) return false;
    +    }
    +    return true;
    +  };
    @@ -2391,15 +2459,11 @@ there isn't any inspectable "Arguments" type.

    -

    Optimize isFunction if appropriate.

    +

    Internal recursive comparison function for isEqual.

    -
      if (typeof (/./) !== 'function') {
    -    _.isFunction = function(obj) {
    -      return typeof obj === 'function';
    -    };
    -  }
    +
      var eq = function(a, b, aStack, bStack) {
    @@ -2410,13 +2474,12 @@ there isn't any inspectable "Arguments" type.

    -

    Is a given object a finite number?

    +

    Identical objects are equal. 0 === -0, but they aren’t identical. +See the Harmony egal proposal.

    -
      _.isFinite = function(obj) {
    -    return isFinite(obj) && !isNaN(parseFloat(obj));
    -  };
    +
        if (a === b) return a !== 0 || 1 / a === 1 / b;
    @@ -2427,13 +2490,11 @@ there isn't any inspectable "Arguments" type.

    -

    Is the given value NaN? (NaN is the only number which does not equal itself).

    +

    A strict comparison is necessary because null == undefined.

    -
      _.isNaN = function(obj) {
    -    return _.isNumber(obj) && obj != +obj;
    -  };
    +
        if (a == null || b == null) return a === b;
    @@ -2444,13 +2505,12 @@ there isn't any inspectable "Arguments" type.

    -

    Is a given value a boolean?

    +

    Unwrap any wrapped objects.

    -
      _.isBoolean = function(obj) {
    -    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
    -  };
    +
        if (a instanceof _) a = a._wrapped;
    +    if (b instanceof _) b = b._wrapped;
    @@ -2461,13 +2521,13 @@ there isn't any inspectable "Arguments" type.

    -

    Is a given value equal to null?

    +

    Compare [[Class]] names.

    -
      _.isNull = function(obj) {
    -    return obj === null;
    -  };
    +
        var className = toString.call(a);
    +    if (className !== toString.call(b)) return false;
    +    switch (className) {
    @@ -2478,13 +2538,11 @@ there isn't any inspectable "Arguments" type.

    -

    Is a given variable undefined?

    +

    Strings, numbers, regular expressions, dates, and booleans are compared by value.

    -
      _.isUndefined = function(obj) {
    -    return obj === void 0;
    -  };
    +
          case '[object RegExp]':
    @@ -2495,14 +2553,11 @@ there isn't any inspectable "Arguments" type.

    -

    Shortcut function for checking if an object has a given property directly -on itself (in other words, not on a prototype).

    +

    RegExps are coerced to strings for comparison (Note: ‘’ + /a/i === ‘/a/i’)

    -
      _.has = function(obj, key) {
    -    return hasOwnProperty.call(obj, key);
    -  };
    +
          case '[object String]':
    @@ -2510,13 +2565,17 @@ on itself (in other words, not on a prototype).

  • -
    +
    -

    Utility Functions

    +

    Primitives and their corresponding object wrappers are equivalent; thus, "5" is +equivalent to new String("5").

    +
            return '' + a === '' + b;
    +      case '[object Number]':
    +
  • @@ -2526,15 +2585,12 @@ on itself (in other words, not on a prototype).

    -

    Run Underscore.js in noConflict mode, returning the _ variable to its -previous owner. Returns a reference to the Underscore object.

    +

    NaNs are equivalent, but non-reflexive. +Object(NaN) is equivalent to NaN

    -
      _.noConflict = function() {
    -    root._ = previousUnderscore;
    -    return this;
    -  };
    +
            if (+a !== +a) return +b !== +b;
    @@ -2545,13 +2601,13 @@ previous owner. Returns a reference to the Underscore object.

    -

    Keep the identity function around for default iterators.

    +

    An egal comparison is performed for other numeric values.

    -
      _.identity = function(value) {
    -    return value;
    -  };
    +
            return +a === 0 ? 1 / +a === 1 / b : +a === +b;
    +      case '[object Date]':
    +      case '[object Boolean]':
    @@ -2562,15 +2618,18 @@ previous owner. Returns a reference to the Underscore object.

    -

    Run a function n times.

    +

    Coerce dates and booleans to numeric primitive values. Dates are compared by their +millisecond representations. Note that invalid dates with millisecond representations +of NaN are not equivalent.

    -
      _.times = function(n, iterator, context) {
    -    var accum = Array(Math.max(0, n));
    -    for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
    -    return accum;
    -  };
    +
            return +a === +b;
    +    }
    +
    +    var areArrays = className === '[object Array]';
    +    if (!areArrays) {
    +      if (typeof a != 'object' || typeof b != 'object') return false;
    @@ -2581,17 +2640,18 @@ previous owner. Returns a reference to the Underscore object.

    -

    Return a random integer between min and max (inclusive).

    +

    Objects with different constructors are not equivalent, but Objects or Arrays +from different frames are.

    -
      _.random = function(min, max) {
    -    if (max == null) {
    -      max = min;
    -      min = 0;
    -    }
    -    return min + Math.floor(Math.random() * (max - min + 1));
    -  };
    +
          var aCtor = a.constructor, bCtor = b.constructor;
    +      if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
    +                               _.isFunction(bCtor) && bCtor instanceof bCtor)
    +                          && ('constructor' in a && 'constructor' in b)) {
    +        return false;
    +      }
    +    }
    @@ -2602,21 +2662,11 @@ previous owner. Returns a reference to the Underscore object.

    -

    List of HTML entities for escaping.

    +

    Assume equality for cyclic structures. The algorithm for detecting cyclic +structures is adapted from ES 5.1 section 15.12.3, abstract operation JO.

    -
      var entityMap = {
    -    escape: {
    -      '&': '&amp;',
    -      '<': '&lt;',
    -      '>': '&gt;',
    -      '"': '&quot;',
    -      "'": '&#x27;'
    -    }
    -  };
    -  entityMap.unescape = _.invert(entityMap.escape);
    - @@ -2626,14 +2676,15 @@ previous owner. Returns a reference to the Underscore object.

    -

    Regexes containing the keys and values listed immediately above.

    +

    Initializing stack of traversed objects. +It’s done here since we only need them for objects and arrays comparison.

    -
      var entityRegexes = {
    -    escape:   new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
    -    unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
    -  };
    +
        aStack = aStack || [];
    +    bStack = bStack || [];
    +    var length = aStack.length;
    +    while (length--) {
    @@ -2644,18 +2695,13 @@ previous owner. Returns a reference to the Underscore object.

    -

    Functions for escaping and unescaping strings to/from HTML interpolation.

    +

    Linear search. Performance is inversely proportional to the number of +unique nested structures.

    -
      _.each(['escape', 'unescape'], function(method) {
    -    _[method] = function(string) {
    -      if (string == null) return '';
    -      return ('' + string).replace(entityRegexes[method], function(match) {
    -        return entityMap[method][match];
    -      });
    -    };
    -  });
    +
          if (aStack[length] === a) return bStack[length] === b;
    +    }
    @@ -2666,16 +2712,12 @@ previous owner. Returns a reference to the Underscore object.

    -

    If the value of the named property is a function then invoke it with the -object as context; otherwise, return it.

    +

    Add the first object to the stack of traversed objects.

    -
      _.result = function(object, property) {
    -    if (object == null) return void 0;
    -    var value = object[property];
    -    return _.isFunction(value) ? value.call(object) : value;
    -  };
    +
        aStack.push(a);
    +    bStack.push(b);
    @@ -2686,20 +2728,11 @@ previous owner. Returns a reference to the Underscore object.

    -

    Add your own custom functions to the Underscore object.

    +

    Recursively compare objects and arrays.

    -
      _.mixin = function(obj) {
    -    each(_.functions(obj), function(name) {
    -      var func = _[name] = obj[name];
    -      _.prototype[name] = function() {
    -        var args = [this._wrapped];
    -        push.apply(args, arguments);
    -        return result.call(this, func.apply(_, args));
    -      };
    -    });
    -  };
    +
        if (areArrays) {
    @@ -2710,16 +2743,12 @@ previous owner. Returns a reference to the Underscore object.

    -

    Generate a unique integer id (unique within the entire client session). -Useful for temporary DOM ids.

    +

    Compare array lengths to determine if a deep comparison is necessary.

    -
      var idCounter = 0;
    -  _.uniqueId = function(prefix) {
    -    var id = ++idCounter + '';
    -    return prefix ? prefix + id : id;
    -  };
    +
          length = a.length;
    +      if (length !== b.length) return false;
    @@ -2730,16 +2759,14 @@ Useful for temporary DOM ids.

    -

    By default, Underscore uses ERB-style template delimiters, change the -following template settings to use alternative delimiters.

    +

    Deep compare the contents, ignoring non-numeric properties.

    -
      _.templateSettings = {
    -    evaluate    : /<%([\s\S]+?)%>/g,
    -    interpolate : /<%=([\s\S]+?)%>/g,
    -    escape      : /<%-([\s\S]+?)%>/g
    -  };
    +
          while (length--) {
    +        if (!eq(a[length], b[length], aStack, bStack)) return false;
    +      }
    +    } else {
    @@ -2750,13 +2777,12 @@ following template settings to use alternative delimiters.

    -

    When customizing templateSettings, if you don't want to define an -interpolation, evaluation or escaping regex, we need one that is -guaranteed not to match.

    +

    Deep compare objects.

    -
      var noMatch = /(.)^/;
    +
          var keys = _.keys(a), key;
    +      length = keys.length;
    @@ -2767,22 +2793,12 @@ guaranteed not to match.

    -

    Certain characters need to be escaped so that they can be put into a -string literal.

    +

    Ensure that both objects contain the same number of properties before comparing deep equality.

    -
      var escapes = {
    -    "'":      "'",
    -    '\\':     '\\',
    -    '\r':     'r',
    -    '\n':     'n',
    -    '\t':     't',
    -    '\u2028': 'u2028',
    -    '\u2029': 'u2029'
    -  };
    -
    -  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
    +
          if (_.keys(b).length !== length) return false;
    +      while (length--) {
    @@ -2793,15 +2809,14 @@ string literal.

    -

    JavaScript micro-templating, similar to John Resig's implementation. -Underscore templating handles arbitrary delimiters, preserves whitespace, -and correctly escapes quotes within interpolated code.

    +

    Deep compare each member

    -
      _.template = function(text, data, settings) {
    -    var render;
    -    settings = _.defaults({}, settings, _.templateSettings);
    +
            key = keys[length];
    +        if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
    +      }
    +    }
    @@ -2812,15 +2827,14 @@ and correctly escapes quotes within interpolated code.

    -

    Combine delimiters into one regular expression via alternation.

    +

    Remove the first object from the stack of traversed objects.

    -
        var matcher = new RegExp([
    -      (settings.escape || noMatch).source,
    -      (settings.interpolate || noMatch).source,
    -      (settings.evaluate || noMatch).source
    -    ].join('|') + '|$', 'g');
    +
        aStack.pop();
    +    bStack.pop();
    +    return true;
    +  };
    @@ -2831,29 +2845,13 @@ and correctly escapes quotes within interpolated code.

    -

    Compile the template source, escaping string literals appropriately.

    +

    Perform a deep comparison to check if two objects are equal.

    -
        var index = 0;
    -    var source = "__p+='";
    -    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
    -      source += text.slice(index, offset)
    -        .replace(escaper, function(match) { return '\\' + escapes[match]; });
    -
    -      if (escape) {
    -        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
    -      }
    -      if (interpolate) {
    -        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
    -      }
    -      if (evaluate) {
    -        source += "';\n" + evaluate + "\n__p+='";
    -      }
    -      index = offset + match.length;
    -      return match;
    -    });
    -    source += "';\n";
    +
      _.isEqual = function(a, b) {
    +    return eq(a, b);
    +  };
    @@ -2864,27 +2862,16 @@ and correctly escapes quotes within interpolated code.

    -

    If a variable is not specified, place data values in local scope.

    +

    Is a given array, string, or object empty? +An “empty” object has no enumerable own-properties.

    -
        if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
    -
    -    source = "var __t,__p='',__j=Array.prototype.join," +
    -      "print=function(){__p+=__j.call(arguments,'');};\n" +
    -      source + "return __p;\n";
    -
    -    try {
    -      render = new Function(settings.variable || 'obj', '_', source);
    -    } catch (e) {
    -      e.source = source;
    -      throw e;
    -    }
    -
    -    if (data) return render(data, _);
    -    var template = function(data) {
    -      return render.call(this, data, _);
    -    };
    +
      _.isEmpty = function(obj) {
    +    if (obj == null) return true;
    +    if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
    +    return _.keys(obj).length === 0;
    +  };
    @@ -2895,13 +2882,12 @@ and correctly escapes quotes within interpolated code.

    -

    Provide the compiled function source as a convenience for precompilation.

    +

    Is a given value a DOM element?

    -
        template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
    -
    -    return template;
    +            
      _.isElement = function(obj) {
    +    return !!(obj && obj.nodeType === 1);
       };
    @@ -2913,12 +2899,13 @@ and correctly escapes quotes within interpolated code.

    -

    Add a "chain" function, which will delegate to the wrapper.

    +

    Is a given value an array? +Delegates to ECMA5’s native Array.isArray

    -
      _.chain = function(obj) {
    -    return _(obj).chain();
    +            
      _.isArray = nativeIsArray || function(obj) {
    +    return toString.call(obj) === '[object Array]';
       };
    @@ -2927,13 +2914,18 @@ and correctly escapes quotes within interpolated code.

  • -
    +
    -

    OOP

    +

    Is a given variable an object?

    +
      _.isObject = function(obj) {
    +    var type = typeof obj;
    +    return type === 'function' || type === 'object' && !!obj;
    +  };
    +
  • @@ -2943,16 +2935,15 @@ and correctly escapes quotes within interpolated code.

    -

    If Underscore is called as a function, it returns a wrapped object that -can be used OO-style. This wrapper holds altered versions of all the -underscore functions. Wrapped objects may be chained.

    -

    Helper function to continue chaining intermediate results.

    +

    Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.

    -
      var result = function(obj) {
    -    return this._chain ? _(obj).chain() : obj;
    -  };
    +
      _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
    +    _['is' + name] = function(obj) {
    +      return toString.call(obj) === '[object ' + name + ']';
    +    };
    +  });
    @@ -2963,11 +2954,16 @@ underscore functions. Wrapped objects may be chained.

    -

    Add all of the Underscore functions to the wrapper object.

    +

    Define a fallback version of the method in browsers (ahem, IE < 9), where +there isn’t any inspectable “Arguments” type.

    -
      _.mixin(_);
    +
      if (!_.isArguments(arguments)) {
    +    _.isArguments = function(obj) {
    +      return _.has(obj, 'callee');
    +    };
    +  }
    @@ -2978,19 +2974,16 @@ underscore functions. Wrapped objects may be chained.

    -

    Add all mutator Array functions to the wrapper.

    +

    Optimize isFunction if appropriate. Work around some typeof bugs in old v8, +IE 11 (#1621), and in Safari 8 (#1929).

    -
      each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    -    var method = ArrayProto[name];
    -    _.prototype[name] = function() {
    -      var obj = this._wrapped;
    -      method.apply(obj, arguments);
    -      if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
    -      return result.call(this, obj);
    +            
      if (typeof /./ != 'function' && typeof Int8Array != 'object') {
    +    _.isFunction = function(obj) {
    +      return typeof obj == 'function' || false;
         };
    -  });
    + }
    @@ -3001,18 +2994,13 @@ underscore functions. Wrapped objects may be chained.

    -

    Add all accessor Array functions to the wrapper.

    +

    Is a given object a finite number?

    -
      each(['concat', 'join', 'slice'], function(name) {
    -    var method = ArrayProto[name];
    -    _.prototype[name] = function() {
    -      return result.call(this, method.apply(this._wrapped, arguments));
    -    };
    -  });
    -
    -  _.extend(_.prototype, {
    +
      _.isFinite = function(obj) {
    +    return isFinite(obj) && !isNaN(parseFloat(obj));
    +  };
    @@ -3023,14 +3011,13 @@ underscore functions. Wrapped objects may be chained.

    -

    Start chaining a wrapped Underscore object.

    +

    Is the given value NaN? (NaN is the only number which does not equal itself).

    -
        chain: function() {
    -      this._chain = true;
    -      return this;
    -    },
    +
      _.isNaN = function(obj) {
    +    return _.isNumber(obj) && obj !== +obj;
    +  };
    @@ -3041,17 +3028,761 @@ underscore functions. Wrapped objects may be chained.

    +

    Is a given value a boolean?

    + + + +
      _.isBoolean = function(obj) {
    +    return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
    +  };
    + + + + +
  • +
    + +
    + +
    +

    Is a given value equal to null?

    + +
    + +
      _.isNull = function(obj) {
    +    return obj === null;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Is a given variable undefined?

    + +
    + +
      _.isUndefined = function(obj) {
    +    return obj === void 0;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Shortcut function for checking if an object has a given property directly +on itself (in other words, not on a prototype).

    + +
    + +
      _.has = function(obj, key) {
    +    return obj != null && hasOwnProperty.call(obj, key);
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Utility Functions

    + +
    + +
  • + + +
  • +
    + +
    + +
    + +
    + +
  • + + +
  • +
    + +
    + +
    +

    Run Underscore.js in noConflict mode, returning the _ variable to its +previous owner. Returns a reference to the Underscore object.

    + +
    + +
      _.noConflict = function() {
    +    root._ = previousUnderscore;
    +    return this;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Keep the identity function around for default iteratees.

    + +
    + +
      _.identity = function(value) {
    +    return value;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Predicate-generating functions. Often useful outside of Underscore.

    + +
    + +
      _.constant = function(value) {
    +    return function() {
    +      return value;
    +    };
    +  };
    +
    +  _.noop = function(){};
    +
    +  _.property = property;
    + +
  • + + +
  • +
    + +
    + +
    +

    Generates a function for a given object that returns a given property.

    + +
    + +
      _.propertyOf = function(obj) {
    +    return obj == null ? function(){} : function(key) {
    +      return obj[key];
    +    };
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Returns a predicate for checking whether an object has a given set of +key:value pairs.

    + +
    + +
      _.matcher = _.matches = function(attrs) {
    +    attrs = _.extendOwn({}, attrs);
    +    return function(obj) {
    +      return _.isMatch(obj, attrs);
    +    };
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Run a function n times.

    + +
    + +
      _.times = function(n, iteratee, context) {
    +    var accum = Array(Math.max(0, n));
    +    iteratee = optimizeCb(iteratee, context, 1);
    +    for (var i = 0; i < n; i++) accum[i] = iteratee(i);
    +    return accum;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Return a random integer between min and max (inclusive).

    + +
    + +
      _.random = function(min, max) {
    +    if (max == null) {
    +      max = min;
    +      min = 0;
    +    }
    +    return min + Math.floor(Math.random() * (max - min + 1));
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    A (possibly faster) way to get the current timestamp as an integer.

    + +
    + +
      _.now = Date.now || function() {
    +    return new Date().getTime();
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    List of HTML entities for escaping.

    + +
    + +
      var escapeMap = {
    +    '&': '&amp;',
    +    '<': '&lt;',
    +    '>': '&gt;',
    +    '"': '&quot;',
    +    "'": '&#x27;',
    +    '`': '&#x60;'
    +  };
    +  var unescapeMap = _.invert(escapeMap);
    + +
  • + + +
  • +
    + +
    + +
    +

    Functions for escaping and unescaping strings to/from HTML interpolation.

    + +
    + +
      var createEscaper = function(map) {
    +    var escaper = function(match) {
    +      return map[match];
    +    };
    + +
  • + + +
  • +
    + +
    + +
    +

    Regexes for identifying a key that needs to be escaped

    + +
    + +
        var source = '(?:' + _.keys(map).join('|') + ')';
    +    var testRegexp = RegExp(source);
    +    var replaceRegexp = RegExp(source, 'g');
    +    return function(string) {
    +      string = string == null ? '' : '' + string;
    +      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
    +    };
    +  };
    +  _.escape = createEscaper(escapeMap);
    +  _.unescape = createEscaper(unescapeMap);
    + +
  • + + +
  • +
    + +
    + +
    +

    If the value of the named property is a function then invoke it with the +object as context; otherwise, return it.

    + +
    + +
      _.result = function(object, property, fallback) {
    +    var value = object == null ? void 0 : object[property];
    +    if (value === void 0) {
    +      value = fallback;
    +    }
    +    return _.isFunction(value) ? value.call(object) : value;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Generate a unique integer id (unique within the entire client session). +Useful for temporary DOM ids.

    + +
    + +
      var idCounter = 0;
    +  _.uniqueId = function(prefix) {
    +    var id = ++idCounter + '';
    +    return prefix ? prefix + id : id;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    By default, Underscore uses ERB-style template delimiters, change the +following template settings to use alternative delimiters.

    + +
    + +
      _.templateSettings = {
    +    evaluate    : /<%([\s\S]+?)%>/g,
    +    interpolate : /<%=([\s\S]+?)%>/g,
    +    escape      : /<%-([\s\S]+?)%>/g
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    When customizing templateSettings, if you don’t want to define an +interpolation, evaluation or escaping regex, we need one that is +guaranteed not to match.

    + +
    + +
      var noMatch = /(.)^/;
    + +
  • + + +
  • +
    + +
    + +
    +

    Certain characters need to be escaped so that they can be put into a +string literal.

    + +
    + +
      var escapes = {
    +    "'":      "'",
    +    '\\':     '\\',
    +    '\r':     'r',
    +    '\n':     'n',
    +    '\u2028': 'u2028',
    +    '\u2029': 'u2029'
    +  };
    +
    +  var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
    +
    +  var escapeChar = function(match) {
    +    return '\\' + escapes[match];
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    JavaScript micro-templating, similar to John Resig’s implementation. +Underscore templating handles arbitrary delimiters, preserves whitespace, +and correctly escapes quotes within interpolated code. +NB: oldSettings only exists for backwards compatibility.

    + +
    + +
      _.template = function(text, settings, oldSettings) {
    +    if (!settings && oldSettings) settings = oldSettings;
    +    settings = _.defaults({}, settings, _.templateSettings);
    + +
  • + + +
  • +
    + +
    + +
    +

    Combine delimiters into one regular expression via alternation.

    + +
    + +
        var matcher = RegExp([
    +      (settings.escape || noMatch).source,
    +      (settings.interpolate || noMatch).source,
    +      (settings.evaluate || noMatch).source
    +    ].join('|') + '|$', 'g');
    + +
  • + + +
  • +
    + +
    + +
    +

    Compile the template source, escaping string literals appropriately.

    + +
    + +
        var index = 0;
    +    var source = "__p+='";
    +    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
    +      source += text.slice(index, offset).replace(escaper, escapeChar);
    +      index = offset + match.length;
    +
    +      if (escape) {
    +        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
    +      } else if (interpolate) {
    +        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
    +      } else if (evaluate) {
    +        source += "';\n" + evaluate + "\n__p+='";
    +      }
    + +
  • + + +
  • +
    + +
    + +
    +

    Adobe VMs need the match returned to produce the correct offset.

    + +
    + +
          return match;
    +    });
    +    source += "';\n";
    + +
  • + + +
  • +
    + +
    + +
    +

    If a variable is not specified, place data values in local scope.

    + +
    + +
        if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
    +
    +    source = "var __t,__p='',__j=Array.prototype.join," +
    +      "print=function(){__p+=__j.call(arguments,'');};\n" +
    +      source + 'return __p;\n';
    +
    +    try {
    +      var render = new Function(settings.variable || 'obj', '_', source);
    +    } catch (e) {
    +      e.source = source;
    +      throw e;
    +    }
    +
    +    var template = function(data) {
    +      return render.call(this, data, _);
    +    };
    + +
  • + + +
  • +
    + +
    + +
    +

    Provide the compiled source as a convenience for precompilation.

    + +
    + +
        var argument = settings.variable || 'obj';
    +    template.source = 'function(' + argument + '){\n' + source + '}';
    +
    +    return template;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Add a “chain” function. Start chaining a wrapped Underscore object.

    + +
    + +
      _.chain = function(obj) {
    +    var instance = _(obj);
    +    instance._chain = true;
    +    return instance;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    OOP

    + +
    + +
  • + + +
  • +
    + +
    + +
    +

    If Underscore is called as a function, it returns a wrapped object that +can be used OO-style. This wrapper holds altered versions of all the +underscore functions. Wrapped objects may be chained.

    + +
    + +
  • + + +
  • +
    + +
    + +
    +

    Helper function to continue chaining intermediate results.

    + +
    + +
      var result = function(instance, obj) {
    +    return instance._chain ? _(obj).chain() : obj;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Add your own custom functions to the Underscore object.

    + +
    + +
      _.mixin = function(obj) {
    +    _.each(_.functions(obj), function(name) {
    +      var func = _[name] = obj[name];
    +      _.prototype[name] = function() {
    +        var args = [this._wrapped];
    +        push.apply(args, arguments);
    +        return result(this, func.apply(_, args));
    +      };
    +    });
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Add all of the Underscore functions to the wrapper object.

    + +
    + +
      _.mixin(_);
    + +
  • + + +
  • +
    + +
    + +
    +

    Add all mutator Array functions to the wrapper.

    + +
    + +
      _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    +    var method = ArrayProto[name];
    +    _.prototype[name] = function() {
    +      var obj = this._wrapped;
    +      method.apply(obj, arguments);
    +      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
    +      return result(this, obj);
    +    };
    +  });
    + +
  • + + +
  • +
    + +
    + +
    +

    Add all accessor Array functions to the wrapper.

    + +
    + +
      _.each(['concat', 'join', 'slice'], function(name) {
    +    var method = ArrayProto[name];
    +    _.prototype[name] = function() {
    +      return result(this, method.apply(this._wrapped, arguments));
    +    };
    +  });
    + +
  • + + +
  • +
    + +
    + +

    Extracts the result from a wrapped and chained object.

    -
        value: function() {
    -      return this._wrapped;
    -    }
    +            
      _.prototype.value = function() {
    +    return this._wrapped;
    +  };
    + +
  • + + +
  • +
    + +
    + +
    +

    Provide unwrapping proxy for some methods used in engine operations +such as arithmetic and JSON stringification.

    - }); +
    + +
      _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
     
    -}).call(this);
    + _.prototype.toString = function() { + return '' + this._wrapped; + }; + +
  • + + +
  • +
    + +
    + +
    +

    AMD registration happens at the end for compatibility with AMD loaders +that may not enforce next-turn semantics on modules. Even though general +practice for AMD registration is to be anonymous, underscore registers +as a named module because, like jQuery, it is a base library that is +popular enough to be bundled in a third party lib, but not be part of +an AMD load request. Those cases could generate an error when an +anonymous define() is called outside of a loader request.

    + +
    + +
      if (typeof define === 'function' && define.amd) {
    +    define('underscore', [], function() {
    +      return _;
    +    });
    +  }
    +}.call(this));
  • diff --git a/vendor/underscore/index.html b/vendor/underscore/index.html index eddf69f38..173ca6fdc 100644 --- a/vendor/underscore/index.html +++ b/vendor/underscore/index.html @@ -6,7 +6,7 @@ - + Underscore.js '; - - // HTML markup for the UI - var MARKUP = '
    \ - \ - \ -
    \ -
    \ - Normalize results \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
    ' + platform + '
    TestOps/sec
    \ -
    \ - \ - Powered by JSLitmus \ -
    '; - - /** - * 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 = - '
    ' + test.error + '
    ' + - ''; - } else { - if (test.running) { - cns.push('test_running'); - cell.innerHTML = 'running'; - } else if (jsl.indexOf(JSLitmus._queue, test) >= 0) { - cns.push('test_pending'); - cell.innerHTML = 'pending'; - } else if (test.count) { - cns.push('test_done'); - var hz = test.getHz(jsl.$('test_normalize').checked); - cell.innerHTML = hz != Infinity ? hz : '∞'; - } else { - cell.innerHTML = 'ready'; - } - } - cell.className = cns.join(' '); - }, - - /** - * Create a new test - */ - test: function(name, f) { - // Create the Test object - var test = new Test(name, f); - JSLitmus._tests.push(test); - - // Re-render if the test state changes - test.onChange = JSLitmus.renderTest; - - // Run the next test if this one finished - test.onStop = function(test) { - if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test); - JSLitmus.currentTest = null; - JSLitmus._nextTest(); - }; - - // Render the new test - this.renderTest(test); - }, - - /** - * Add all tests to the run queue - */ - runAll: function(e) { - e = e || window.event; - var reverse = e && e.shiftKey, len = JSLitmus._tests.length; - for (var i = 0; i < len; i++) { - JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]); - } - }, - - /** - * Remove all tests from the run queue. The current test has to finish on - * it's own though - */ - stop: function() { - while (JSLitmus._queue.length) { - var test = JSLitmus._queue.shift(); - JSLitmus.renderTest(test); - } - }, - - /** - * Run the next test in the run queue - */ - _nextTest: function() { - if (!JSLitmus.currentTest) { - var test = JSLitmus._queue.shift(); - if (test) { - jsl.$('stop_button').disabled = false; - JSLitmus.currentTest = test; - test.run(); - JSLitmus.renderTest(test); - if (JSLitmus.onTestStart) JSLitmus.onTestStart(test); - } else { - jsl.$('stop_button').disabled = true; - JSLitmus.renderChart(); - } - } - }, - - /** - * Add a test to the run queue - */ - _queueTest: function(test) { - if (jsl.indexOf(JSLitmus._queue, test) >= 0) return; - JSLitmus._queue.push(test); - JSLitmus.renderTest(test); - JSLitmus._nextTest(); - }, - - /** - * Generate a Google Chart URL that shows the data for all tests - */ - chartUrl: function() { - var n = JSLitmus._tests.length, markers = [], data = []; - var d, min = 0, max = -1e10; - var normalize = jsl.$('test_normalize').checked; - - // Gather test data - for (var i=0; i < JSLitmus._tests.length; i++) { - var test = JSLitmus._tests[i]; - if (test.count) { - var hz = test.getHz(normalize); - var v = hz != Infinity ? hz : 0; - data.push(v); - markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' + - markers.length + ',10'); - max = Math.max(v, max); - } - } - if (markers.length <= 0) return null; - - // Build chart title - var title = document.getElementsByTagName('title'); - title = (title && title.length) ? title[0].innerHTML : null; - var chart_title = []; - if (title) chart_title.push(title); - chart_title.push('Ops/sec (' + platform + ')'); - - // Build labels - var labels = [jsl.toLabel(min), jsl.toLabel(max)]; - - var w = 250, bw = 15; - var bs = 5; - var h = markers.length*(bw + bs) + 30 + chart_title.length*20; - - var params = { - chtt: escape(chart_title.join('|')), - chts: '000000,10', - cht: 'bhg', // chart type - chd: 't:' + data.join(','), // data set - chds: min + ',' + max, // max/min of data - chxt: 'x', // label axes - chxl: '0:|' + labels.join('|'), // labels - chsp: '0,1', - chm: markers.join('|'), // test names - chbh: [bw, 0, bs].join(','), // bar widths - // chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient - chs: w + 'x' + h - }; - return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&'); - } - }; - - JSLitmus._init(); -})(); \ No newline at end of file diff --git a/vendor/underscore/test/vendor/qunit-extras.js b/vendor/underscore/test/vendor/qunit-extras.js new file mode 100644 index 000000000..663d32489 --- /dev/null +++ b/vendor/underscore/test/vendor/qunit-extras.js @@ -0,0 +1,776 @@ +/*! + * QUnit Extras v1.4.1 + * Copyright 2011-2015 John-David Dalton + * Based on a gist by Jörn Zaefferer + * Available under MIT license + */ +;(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 = /(&|<|>|"|')/g; + + /** Used to match parts of the assert message. */ + var reDied = /^Died on test #\d+/, + reMessage = /^([\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 = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + + /** 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 `&`, `<`, `>`, `"`, and `'` + * 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)); \ No newline at end of file diff --git a/vendor/underscore/test/vendor/qunit.css b/vendor/underscore/test/vendor/qunit.css index 7ba3f9a30..f1dcd4e1c 100644 --- a/vendor/underscore/test/vendor/qunit.css +++ b/vendor/underscore/test/vendor/qunit.css @@ -1,11 +1,12 @@ -/** - * QUnit v1.12.0 - A JavaScript Unit Testing Framework +/*! + * QUnit 1.18.0 + * http://qunitjs.com/ * - * http://qunitjs.com - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license. + * Copyright jQuery Foundation and other contributors + * Released under the MIT license * http://jquery.org/license + * + * Date: 2015-04-03T10:23Z */ /** Font Family and Sizes */ @@ -31,32 +32,29 @@ #qunit-header { padding: 0.5em 0 0.5em 1em; - color: #8699a4; - background-color: #0d3349; + color: #8699A4; + background-color: #0D3349; font-size: 1.5em; line-height: 1em; - font-weight: normal; + font-weight: 400; border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-top-right-radius: 5px; - -webkit-border-top-left-radius: 5px; } #qunit-header a { text-decoration: none; - color: #c2ccd1; + color: #C2CCD1; } #qunit-header a:hover, #qunit-header a:focus { - color: #fff; + color: #FFF; } #qunit-testrunner-toolbar label { display: inline-block; - padding: 0 .5em 0 .1em; + padding: 0 0.5em 0 0.1em; } #qunit-banner { @@ -64,21 +62,33 @@ } #qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; + padding: 0.5em 1em 0.5em 1em; color: #5E740B; - background-color: #eee; + background-color: #EEE; overflow: hidden; } #qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; + padding: 0.5em 1em 0.5em 1em; + background-color: #2B81AF; + color: #FFF; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } #qunit-modulefilter-container { 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 */ @@ -88,24 +98,51 @@ } #qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; + padding: 0.4em 1em 0.4em 1em; + border-bottom: 1px solid #FFF; list-style-position: inside; } -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { +#qunit-tests > li { 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 { cursor: pointer; } +#qunit-tests li.skipped strong { + cursor: default; +} + #qunit-tests li a { padding: 0.5em; - color: #c2ccd1; + color: #C2CCD1; text-decoration: none; } + +#qunit-tests li p a { + padding: 0.25em; + color: #6B6464; +} #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; @@ -120,11 +157,9 @@ margin-top: 0.5em; padding: 0.5em; - background-color: #fff; + background-color: #FFF; border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; } .qunit-collapsed { @@ -133,13 +168,13 @@ #qunit-tests table { border-collapse: collapse; - margin-top: .2em; + margin-top: 0.2em; } #qunit-tests th { text-align: right; vertical-align: top; - padding: 0 .5em 0 0; + padding: 0 0.5em 0 0; } #qunit-tests td { @@ -153,26 +188,26 @@ } #qunit-tests del { - background-color: #e0f2be; - color: #374e0c; + background-color: #E0F2BE; + color: #374E0C; text-decoration: none; } #qunit-tests ins { - background-color: #ffcaca; + background-color: #FFCACA; color: #500; text-decoration: none; } /*** Test Counts */ -#qunit-tests b.counts { color: black; } +#qunit-tests b.counts { color: #000; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; - background-color: #fff; + background-color: #FFF; border-bottom: none; list-style-position: inside; } @@ -180,8 +215,8 @@ /*** Passing Styles */ #qunit-tests li li.pass { - color: #3c510c; - background-color: #fff; + color: #3C510C; + background-color: #FFF; border-left: 10px solid #C6E746; } @@ -189,7 +224,7 @@ #qunit-tests .pass .test-name { color: #366097; } #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; } @@ -197,40 +232,52 @@ #qunit-tests li li.fail { color: #710909; - background-color: #fff; + background-color: #FFF; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; - -moz-border-radius: 0 0 5px 5px; - -webkit-border-bottom-right-radius: 5px; - -webkit-border-bottom-left-radius: 5px; } -#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail { color: #000; background-color: #EE5757; } #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-expected { color: green; } +#qunit-tests .fail .test-expected { color: #008000; } #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 */ #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; - border-bottom: 1px solid white; + border-bottom: 1px solid #FFF; } #qunit-testresult .module-name { - font-weight: bold; + font-weight: 700; } /** Fixture */ diff --git a/vendor/underscore/test/vendor/qunit.js b/vendor/underscore/test/vendor/qunit.js index 84c73907d..f3542ca9d 100644 --- a/vendor/underscore/test/vendor/qunit.js +++ b/vendor/underscore/test/vendor/qunit.js @@ -1,35 +1,42 @@ -/** - * 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 + * http://jquery.org/license * - * Copyright 2013 jQuery Foundation and other contributors - * Released under the MIT license. - * https://jquery.org/license/ + * Date: 2015-04-03T10:23Z */ (function( window ) { var QUnit, - assert, config, onErrorFnPrev, - testId = 0, - fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), + loggingCallbacks = {}, + fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ), toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, // Keep a local reference to Date (GH-283) Date = window.Date, + now = Date.now || function() { + return new Date().getTime(); + }, + globalStartCalled = false, + runStarted = false, setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, defined = { - setTimeout: typeof window.setTimeout !== "undefined", + document: window.document !== undefined, + setTimeout: window.setTimeout !== undefined, sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; - } catch( e ) { + } catch ( e ) { return false; } }()) @@ -71,627 +78,18 @@ var QUnit, * @return {Object} New object with only the own properties (recursively). */ objectValues = function( obj ) { - // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. - /*jshint newcap: false */ var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { - val = obj[key]; - vals[key] = val === Object(val) ? objectValues(val) : val; + val = obj[ key ]; + vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } } return vals; }; -function Test( settings ) { - extend( this, settings ); - this.assertions = []; - this.testNumber = ++Test.count; -} - -Test.count = 0; - -Test.prototype = { - init: function() { - var a, b, li, - tests = id( "qunit-tests" ); - - if ( tests ) { - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml; - - // `a` initialized at top of scope - a = document.createElement( "a" ); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ testNumber: this.testNumber }); - - li = document.createElement( "li" ); - li.appendChild( b ); - li.appendChild( a ); - li.className = "running"; - li.id = this.id = "qunit-test-output" + testId++; - - tests.appendChild( li ); - } - }, - setup: function() { - if ( - // Emit moduleStart when we're switching from one module to another - this.module !== config.previousModule || - // They could be equal (both undefined) but if the previousModule property doesn't - // yet exist it means this is the first test in a suite that isn't wrapped in a - // module, in which case we'll just emit a moduleStart event for 'undefined'. - // Without this, reporters can get testStart before moduleStart which is a problem. - !hasOwn.call( config, "previousModule" ) - ) { - if ( hasOwn.call( config, "previousModule" ) ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } - - config.current = this; - - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment ); - - this.started = +new Date(); - runLoggingCallbacks( "testStart", QUnit, { - name: this.testName, - module: this.module - }); - - /*jshint camelcase:false */ - - - /** - * Expose the current test environment. - * - * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead. - */ - QUnit.current_testEnvironment = this.testEnvironment; - - /*jshint camelcase:true */ - - if ( !config.pollution ) { - saveGlobal(); - } - if ( config.notrycatch ) { - this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); - return; - } - try { - this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); - } catch( e ) { - QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - }, - run: function() { - config.current = this; - - var running = id( "qunit-testresult" ); - - if ( running ) { - running.innerHTML = "Running:
    " + this.nameHtml; - } - - if ( this.async ) { - QUnit.stop(); - } - - this.callbackStarted = +new Date(); - - if ( config.notrycatch ) { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - return; - } - - try { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - } catch( e ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - - QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - if ( config.notrycatch ) { - if ( typeof this.callbackRuntime === "undefined" ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - } - this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); - return; - } else { - try { - this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); - } catch( e ) { - QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - } - checkPollution(); - }, - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected === null ) { - QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); - } else if ( this.expected !== null && this.expected !== this.assertions.length ) { - QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); - } else if ( this.expected === null && !this.assertions.length ) { - QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); - } - - var i, assertion, a, b, time, li, ol, - test = this, - good = 0, - bad = 0, - tests = id( "qunit-tests" ); - - this.runtime = +new Date() - this.started; - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - ol = document.createElement( "ol" ); - ol.className = "qunit-assert-list"; - - for ( i = 0; i < this.assertions.length; i++ ) { - assertion = this.assertions[i]; - - li = document.createElement( "li" ); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); - } - } - - if ( bad === 0 ) { - addClass( ol, "qunit-collapsed" ); - } - - // `b` initialized at top of scope - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - addEvent(b, "click", function() { - var next = b.parentNode.lastChild, - collapsed = hasClass( next, "qunit-collapsed" ); - ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); - }); - - addEvent(b, "dblclick", function( e ) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ testNumber: test.testNumber }); - } - }); - - // `time` initialized at top of scope - time = document.createElement( "span" ); - time.className = "runtime"; - time.innerHTML = this.runtime + " ms"; - - // `li` initialized at top of scope - li = id( this.id ); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - a = li.firstChild; - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( time ); - li.appendChild( ol ); - - } else { - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - runLoggingCallbacks( "testDone", QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - duration: this.runtime - }); - - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var bad, - test = this; - - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - - // `bad` initialized at top of scope - // defer when previous test run passed, if storage is available - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); - - if ( bad ) { - run(); - } else { - synchronize( run, true ); - } - } -}; - -// Root QUnit object. -// `QUnit` initialized at top of scope -QUnit = { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment ) { - config.currentModule = name; - config.currentModuleTestEnvironment = testEnvironment; - config.modules[name] = true; - }, - - asyncTest: function( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); - }, - - test: function( testName, expected, callback, async ) { - var test, - nameHtml = "" + escapeText( testName ) + ""; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - if ( config.currentModule ) { - nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml; - } - - test = new Test({ - nameHtml: nameHtml, - testName: testName, - expected: expected, - async: async, - callback: callback, - module: config.currentModule, - moduleTestEnvironment: config.currentModuleTestEnvironment, - stack: sourceFromStacktrace( 2 ) - }); - - if ( !validTest( test ) ) { - return; - } - - test.queue(); - }, - - // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through. - expect: function( asserts ) { - if (arguments.length === 1) { - config.current.expected = asserts; - } else { - return config.current.expected; - } - }, - - start: function( count ) { - // QUnit hasn't been initialized yet. - // Note: RequireJS (et al) may delay onLoad - if ( config.semaphore === undefined ) { - QUnit.begin(function() { - // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first - setTimeout(function() { - QUnit.start( count ); - }); - }); - return; - } - - config.semaphore -= count || 1; - // don't start until equal number of stop-calls - if ( config.semaphore > 0 ) { - return; - } - // ignore if start is called more often then stop - if ( config.semaphore < 0 ) { - config.semaphore = 0; - QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); - return; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - setTimeout(function() { - if ( config.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - config.blocking = false; - process( true ); - }, 13); - } else { - config.blocking = false; - process( true ); - } - }, - - stop: function( count ) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout ); - } - } -}; - -// `assert` initialized at top of scope -// Assert helpers -// All of these must either call QUnit.push() or manually do: -// - runLoggingCallbacks( "log", .. ); -// - config.current.assertions.push({ .. }); -// We attach it to the QUnit object *after* we expose the public API, -// otherwise `assert` will become a global variable in browsers (#341). -assert = { - /** - * Asserts rough true-ish result. - * @name ok - * @function - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function( result, msg ) { - if ( !config.current ) { - throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - result = !!result; - msg = msg || (result ? "okay" : "failed" ); - - var source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: msg - }; - - msg = "" + escapeText( msg ) + ""; - - if ( !result ) { - source = sourceFromStacktrace( 2 ); - if ( source ) { - details.source = source; - msg += "
    Source:
    " + escapeText( source ) + "
    "; - } - } - runLoggingCallbacks( "log", QUnit, details ); - config.current.assertions.push({ - result: result, - message: msg - }); - }, - - /** - * Assert that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * @name equal - * @function - * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); - */ - equal: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected == actual, actual, expected, message ); - }, - - /** - * @name notEqual - * @function - */ - notEqual: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected != actual, actual, expected, message ); - }, - - /** - * @name propEqual - * @function - */ - propEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notPropEqual - * @function - */ - notPropEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name deepEqual - * @function - */ - deepEqual: function( actual, expected, message ) { - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notDeepEqual - * @function - */ - notDeepEqual: function( actual, expected, message ) { - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name strictEqual - * @function - */ - strictEqual: function( actual, expected, message ) { - QUnit.push( expected === actual, actual, expected, message ); - }, - - /** - * @name notStrictEqual - * @function - */ - notStrictEqual: function( actual, expected, message ) { - QUnit.push( expected !== actual, actual, expected, message ); - }, - - "throws": function( block, expected, message ) { - var actual, - expectedOutput = expected, - ok = false; - - // 'expected' is optional - if ( typeof expected === "string" ) { - message = expected; - expected = null; - } - - config.current.ignoreGlobalErrors = true; - try { - block.call( config.current.testEnvironment ); - } catch (e) { - actual = e; - } - config.current.ignoreGlobalErrors = false; - - if ( actual ) { - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - expectedOutput = null; - // expected is a regexp - } else if ( QUnit.objectType( expected ) === "regexp" ) { - ok = expected.test( errorString( actual ) ); - // expected is a constructor - } else if ( actual instanceof expected ) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if ( expected.call( {}, actual ) === true ) { - expectedOutput = null; - ok = true; - } - - QUnit.push( ok, actual, expectedOutput, message ); - } else { - QUnit.pushFailure( message, null, "No exception was thrown." ); - } - } -}; - -/** - * @deprecated since 1.8.0 - * Kept assertion helpers in root for backwards compatibility. - */ -extend( QUnit, assert ); - -/** - * @deprecated since 1.9.0 - * Kept root "raises()" for backwards compatibility. - * (Note that we don't introduce assert.raises). - */ -QUnit.raises = assert[ "throws" ]; - -/** - * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 - * Kept to avoid TypeErrors for undefined methods. - */ -QUnit.equals = function() { - QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); -}; -QUnit.same = function() { - QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); -}; - -// We want access to the constructor's prototype -(function() { - function F() {} - F.prototype = QUnit; - QUnit = new F(); - // Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -}()); +QUnit = {}; /** * Config object: Maintain internal state @@ -705,10 +103,6 @@ config = { // block until document ready blocking: true, - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, @@ -716,165 +110,233 @@ config = { // by default, modify document.title when suite is done altertitle: true, + // by default, scroll to top of the page when suite is done + scrolltop: true, + // when enabled, all tests must call expect() requireExpects: false, + // depth up-to which object will be dumped + maxDepth: 5, + // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ + { + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, { id: "noglobals", label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." + tooltip: "Enabling this will test if any test introduces new properties on the " + + "`window` object. Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + + "exceptions in IE reasonable. Stored as query-strings." } ], // Set of all modules. - modules: {}, + modules: [], - // logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] + // The first unnamed module + currentModule: { + name: "", + tests: [] + }, + + callbacks: {} }; -// Export global variables, unless an 'exports' object exists, -// in that case we assume we're in CommonJS (dealt with on the bottom of the script) -if ( typeof exports === "undefined" ) { - extend( window, QUnit.constructor.prototype ); - - // Expose QUnit object - window.QUnit = QUnit; -} +// Push a loose unnamed module to the modules collection +config.modules.push( config.currentModule ); // Initialize more QUnit.config and QUnit.urlParams (function() { - var i, + var i, current, location = window.location || { search: "", protocol: "file:" }, params = location.search.slice( 1 ).split( "&" ), length = params.length, - urlParams = {}, - current; + urlParams = {}; if ( params[ 0 ] ) { for ( i = 0; i < length; i++ ) { current = params[ i ].split( "=" ); current[ 0 ] = decodeURIComponent( current[ 0 ] ); + // allow just a key to turn on a flag, e.g., test.html?noglobals current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; + if ( urlParams[ current[ 0 ] ] ) { + urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] ); + } else { + urlParams[ current[ 0 ] ] = current[ 1 ]; + } } } + if ( urlParams.filter === true ) { + delete urlParams.filter; + } + QUnit.urlParams = urlParams; // String search anywhere in moduleName+testName config.filter = urlParams.filter; - // Exact match of the module name - config.module = urlParams.module; + if ( urlParams.maxDepth ) { + config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ? + Number.POSITIVE_INFINITY : + urlParams.maxDepth; + } - config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; + config.testId = []; + if ( urlParams.testId ) { + + // Ensure that urlParams.testId is an array + urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); + for ( i = 0; i < urlParams.testId.length; i++ ) { + config.testId.push( urlParams.testId[ i ] ); + } + } // Figure out if we're running the tests from a server or not QUnit.isLocal = location.protocol === "file:"; + + // Expose the current QUnit version + QUnit.version = "1.18.0"; }()); -// Extend QUnit object, -// these after set here because they should not be exposed as global functions +// Root QUnit object. +// `QUnit` initialized at top of scope extend( QUnit, { - assert: assert, - config: config, + // call on start of module test to prepend name to all tests + module: function( name, testEnvironment ) { + var currentModule = { + name: name, + testEnvironment: testEnvironment, + tests: [] + }; - // Initialize the configuration options - init: function() { - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date(), - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 1 + // DEPRECATED: handles setup/teardown functions, + // beforeEach and afterEach should be used instead + if ( testEnvironment && testEnvironment.setup ) { + testEnvironment.beforeEach = testEnvironment.setup; + delete testEnvironment.setup; + } + if ( testEnvironment && testEnvironment.teardown ) { + testEnvironment.afterEach = testEnvironment.teardown; + delete testEnvironment.teardown; + } + + config.modules.push( currentModule ); + config.currentModule = currentModule; + }, + + // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. + asyncTest: function( testName, expected, callback ) { + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + + QUnit.test( testName, expected, callback, true ); + }, + + test: function( testName, expected, callback, async ) { + var test; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + + test = new Test({ + testName: testName, + expected: expected, + async: async, + callback: callback }); - var tests, banner, result, - qunit = id( "qunit" ); - - if ( qunit ) { - qunit.innerHTML = - "

    " + escapeText( document.title ) + "

    " + - "

    " + - "
    " + - "

    " + - "
      "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
       "; - } + test.queue(); }, - // Resets the test setup. Useful for tests that modify the DOM. - /* - DEPRECATED: Use multiple tests instead of resetting inside a test. - Use testStart or testDone for custom cleanup. - This method will throw an error in 2.0, and will be removed in 2.1 - */ - reset: function() { - var fixture = id( "qunit-fixture" ); - if ( fixture ) { - fixture.innerHTML = config.fixture; - } + skip: function( testName ) { + var test = new Test({ + testName: testName, + skip: true + }); + + test.queue(); }, - // Trigger an event on an element. - // @example triggerEvent( document.body, "click" ); - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent( "MouseEvents" ); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); + // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. + // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. + start: function( count ) { + var globalStartAlreadyCalled = globalStartCalled; - elem.dispatchEvent( event ); - } else if ( elem.fireEvent ) { - elem.fireEvent( "on" + type ); + if ( !config.current ) { + globalStartCalled = true; + + if ( runStarted ) { + throw new Error( "Called start() outside of a test context while already started" ); + } else if ( globalStartAlreadyCalled || count > 1 ) { + throw new Error( "Called start() outside of a test context too many times" ); + } else if ( config.autostart ) { + throw new Error( "Called start() outside of a test context when " + + "QUnit.config.autostart was true" ); + } else if ( !config.pageLoaded ) { + + // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it + config.autostart = true; + return; + } + } else { + + // If a test is running, adjust its semaphore + config.current.semaphore -= count || 1; + + // Don't start until equal number of stop-calls + if ( config.current.semaphore > 0 ) { + return; + } + + // throw an Error if start is called more often than stop + if ( config.current.semaphore < 0 ) { + config.current.semaphore = 0; + + QUnit.pushFailure( + "Called start() while already started (test's semaphore was 0 already)", + sourceFromStacktrace( 2 ) + ); + return; + } } + + resumeProcessing(); }, + // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. + stop: function( count ) { + + // If there isn't a test running, don't allow QUnit.stop() to be called + if ( !config.current ) { + throw new Error( "Called stop() outside of a test context" ); + } + + // If a test is running, adjust its semaphore + config.current.semaphore += count || 1; + + pauseProcessing(); + }, + + config: config, + // Safe object type checking is: function( type, obj ) { return QUnit.objectType( obj ) === type; @@ -882,19 +344,20 @@ extend( QUnit, { objectType: function( obj ) { if ( typeof obj === "undefined" ) { - return "undefined"; - // consider: typeof null === object - } - if ( obj === null ) { - return "null"; + return "undefined"; } - var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), - type = match && match[1] || ""; + // Consider: typeof null === object + if ( obj === null ) { + return "null"; + } + + var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), + type = match && match[ 1 ] || ""; switch ( type ) { case "Number": - if ( isNaN(obj) ) { + if ( isNaN( obj ) ) { return "nan"; } return "number"; @@ -912,308 +375,65 @@ extend( QUnit, { return undefined; }, - push: function( result, actual, expected, message ) { - if ( !config.current ) { - throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); - } - - var output, source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeText( message ) || ( result ? "okay" : "failed" ); - message = "" + message + ""; - output = message; - - if ( !result ) { - expected = escapeText( QUnit.jsDump.parse(expected) ); - actual = escapeText( QUnit.jsDump.parse(actual) ); - output += ""; - - if ( actual !== expected ) { - output += ""; - output += ""; - } - - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
      Expected:
      " + expected + "
      Result:
      " + actual + "
      Diff:
      " + QUnit.diff( expected, actual ) + "
      Source:
      " + escapeText( source ) + "
      "; - } - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - pushFailure: function( message, source, actual ) { - if ( !config.current ) { - throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - - var output, - details = { - module: config.current.module, - name: config.current.testName, - result: false, - message: message - }; - - message = escapeText( message ) || "error"; - message = "" + message + ""; - output = message; - - output += ""; - - if ( actual ) { - output += ""; - } - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
      Result:
      " + escapeText( actual ) + "
      Source:
      " + escapeText( source ) + "
      "; - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: false, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var key, - querystring = "?"; - - for ( key in params ) { - if ( hasOwn.call( params, key ) ) { - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - } - return window.location.protocol + "//" + window.location.host + - window.location.pathname + querystring.slice( 0, -1 ); - }, - extend: extend, - id: id, - addEvent: addEvent, - addClass: addClass, - hasClass: hasClass, - removeClass: removeClass - // load, equiv, jsDump, diff: Attached later + + load: function() { + config.pageLoaded = true; + + // Initialize the configuration options + extend( config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, true ); + + config.blocking = false; + + if ( config.autostart ) { + resumeProcessing(); + } + } }); -/** - * @deprecated: Created for backwards compatibility with test runner that set the hook function - * into QUnit.{hook}, instead of invoking it and passing the hook function. - * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. - * Doing this allows us to tell if the following methods have been overwritten on the actual - * QUnit object. - */ -extend( QUnit.constructor.prototype, { +// Register logging callbacks +(function() { + var i, l, key, + callbacks = [ "begin", "done", "log", "testStart", "testDone", + "moduleStart", "moduleDone" ]; - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback( "begin" ), - - // done: { failed, passed, total, runtime } - done: registerLoggingCallback( "done" ), - - // log: { result, actual, expected, message } - log: registerLoggingCallback( "log" ), - - // testStart: { name } - testStart: registerLoggingCallback( "testStart" ), - - // testDone: { name, failed, passed, total, duration } - testDone: registerLoggingCallback( "testDone" ), - - // moduleStart: { name } - moduleStart: registerLoggingCallback( "moduleStart" ), - - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback( "moduleDone" ) -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( "begin", QUnit, {} ); - - // Initialize the config, saving the execution queue - var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, - urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, - numModules = 0, - moduleNames = [], - moduleFilterHtml = "", - urlConfigHtml = "", - oldconfig = extend( {}, config ); - - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - len = config.urlConfig.length; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[i]; - if ( typeof val === "string" ) { - val = { - id: val, - label: val, - tooltip: "[no tooltip available]" - }; - } - config[ val.id ] = QUnit.urlParams[ val.id ]; - urlConfigHtml += ""; - } - for ( i in config.modules ) { - if ( config.modules.hasOwnProperty( i ) ) { - moduleNames.push(i); - } - } - numModules = moduleNames.length; - moduleNames.sort( function( a, b ) { - return a.localeCompare( b ); - }); - moduleFilterHtml += ""; - - // `userAgent` initialized at top of scope - userAgent = id( "qunit-userAgent" ); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - - // `banner` initialized at top of scope - banner = id( "qunit-header" ); - if ( banner ) { - banner.innerHTML = "" + banner.innerHTML + " "; - } - - // `toolbar` initialized at top of scope - toolbar = id( "qunit-testrunner-toolbar" ); - if ( toolbar ) { - // `filter` initialized at top of scope - filter = document.createElement( "input" ); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - - addEvent( filter, "click", function() { - var tmp, - ol = document.getElementById( "qunit-tests" ); - - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace( / hidepass /, " " ); + function registerLoggingCallback( key ) { + var loggingCallback = function( callback ) { + if ( QUnit.objectType( callback ) !== "function" ) { + throw new Error( + "QUnit logging methods require a callback function as their first parameters." + ); } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); - } else { - sessionStorage.removeItem( "qunit-filter-passed-tests" ); - } - } - }); - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { - filter.checked = true; - // `ol` initialized at top of scope - ol = document.getElementById( "qunit-tests" ); - ol.className = ol.className + " hidepass"; + config.callbacks[ key ].push( callback ); + }; + + // DEPRECATED: This will be removed on QUnit 2.0.0+ + // Stores the registered functions allowing restoring + // at verifyLoggingCallbacks() if modified + loggingCallbacks[ key ] = loggingCallback; + + return loggingCallback; + } + + for ( i = 0, l = callbacks.length; i < l; i++ ) { + key = callbacks[ i ]; + + // Initialize key collection of logging callback + if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) { + config.callbacks[ key ] = []; } - toolbar.appendChild( filter ); - // `label` initialized at top of scope - label = document.createElement( "label" ); - label.setAttribute( "for", "qunit-filter-pass" ); - label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." ); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - - urlConfigCheckboxesContainer = document.createElement("span"); - urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; - urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); - // For oldIE support: - // * Add handlers to the individual elements instead of the container - // * Use "click" instead of "change" - // * Fallback from event.target to event.srcElement - addEvents( urlConfigCheckboxes, "click", function( event ) { - var params = {}, - target = event.target || event.srcElement; - params[ target.name ] = target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - toolbar.appendChild( urlConfigCheckboxesContainer ); - - if (numModules > 1) { - moduleFilter = document.createElement( "span" ); - moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); - moduleFilter.innerHTML = moduleFilterHtml; - addEvent( moduleFilter.lastChild, "change", function() { - var selectBox = moduleFilter.getElementsByTagName("select")[0], - selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); - - window.location = QUnit.url({ - module: ( selectedModule === "" ) ? undefined : selectedModule, - // Remove any existing filters - filter: undefined, - testNumber: undefined - }); - }); - toolbar.appendChild(moduleFilter); - } + QUnit[ key ] = registerLoggingCallback( key ); } - - // `main` initialized at top of scope - main = id( "qunit-fixture" ); - if ( main ) { - config.fixture = main.innerHTML; - } - - if ( config.autostart ) { - QUnit.start(); - } -}; - -addEvent( window, "load", QUnit.load ); +})(); // `onErrorFnPrev` initialized at top of scope // Preserve other handlers @@ -1222,7 +442,7 @@ onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will suppress the default browser handler, // returning false will let it run. -window.onerror = function ( error, filePath, linerNr ) { +window.onerror = function( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); @@ -1237,9 +457,9 @@ window.onerror = function ( error, filePath, linerNr ) { } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { - QUnit.test( "global failure", extend( function() { + QUnit.test( "global failure", extend(function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: validTest } ) ); + }, { validTest: true } ) ); } return false; } @@ -1248,71 +468,27 @@ window.onerror = function ( error, filePath, linerNr ) { }; function done() { + var runtime, passed; + config.autorun = true; // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.currentModule, + if ( config.previousModule ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started }); } delete config.previousModule; - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - runtime = +new Date() - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - "Tests completed in ", - runtime, - " milliseconds.
      ", - "", - passed, - " assertions of ", - config.stats.all, - " passed, ", - config.stats.bad, - " failed." - ].join( "" ); + runtime = now() - config.started; + passed = config.stats.all - config.stats.bad; - if ( banner ) { - banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( config.stats.bad ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { - // `key` & `i` initialized at top of scope - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - // scroll back to top to show results - if ( window.scrollTo ) { - window.scrollTo(0, 0); - } - - runLoggingCallbacks( "done", QUnit, { + runLoggingCallbacks( "done", { failed: config.stats.bad, passed: passed, total: config.stats.all, @@ -1320,60 +496,16 @@ function done() { }); } -/** @return Boolean: true if this test should be ran */ -function validTest( test ) { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = config.module && config.module.toLowerCase(), - fullName = (test.module + ": " + test.testName).toLowerCase(); - - // Internally-generated tests are always valid - if ( test.callback && test.callback.validTest === validTest ) { - delete test.callback.validTest; - return true; - } - - if ( config.testNumber ) { - return test.testNumber === config.testNumber; - } - - if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; -} - -// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) -// Later Safari and IE10 are supposed to support error.stack as well +// Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { - offset = offset === undefined ? 3 : offset; + offset = offset === undefined ? 4 : offset; var stack, include, i; - if ( e.stacktrace ) { - // Opera - return e.stacktrace.split( "\n" )[ offset + 3 ]; - } else if ( e.stack ) { - // Firefox, Chrome + if ( e.stack ) { stack = e.stack.split( "\n" ); - if (/^error$/i.test( stack[0] ) ) { + if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); } if ( fileName ) { @@ -1389,51 +521,43 @@ function extractStacktrace( e, offset ) { } } return stack[ offset ]; + + // Support: Safari <=6 only } else if ( e.sourceURL ) { - // Safari, PhantomJS - // hopefully one day Safari provides actual stacktraces + // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } + // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } -function sourceFromStacktrace( offset ) { - try { - throw new Error(); - } catch ( e ) { - return extractStacktrace( e, offset ); - } -} -/** - * Escape text for attribute or text content. - */ -function escapeText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - // Both single quotes and double quotes (for attributes) - return s.replace( /['"<>&]/g, function( s ) { - switch( s ) { - case "'": - return "'"; - case "\"": - return """; - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; +function sourceFromStacktrace( offset ) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if ( !error.stack ) { + try { + throw error; + } catch ( err ) { + error = err; } - }); + } + + return extractStacktrace( error, offset ); } function synchronize( callback, last ) { + if ( QUnit.objectType( callback ) === "array" ) { + while ( callback.length ) { + synchronize( callback.shift() ); + } + return; + } config.queue.push( callback ); if ( config.autorun && !config.blocking ) { @@ -1445,11 +569,17 @@ function process( last ) { function next() { process( last ); } - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; + var start = now(); + config.depth = ( config.depth || 0 ) + 1; while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { + if ( !defined.setTimeout || config.updateRate <= 0 || + ( ( now() - start ) < config.updateRate ) ) { + if ( config.current ) { + + // Reset async tracking for each phase of the Test lifecycle + config.current.usedAsync = false; + } config.queue.shift()(); } else { setTimeout( next, 13 ); @@ -1462,6 +592,79 @@ function process( last ) { } } +function begin() { + var i, l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if ( !config.started ) { + + // Record the time of the test run's beginning + config.started = now(); + + verifyLoggingCallbacks(); + + // Delete the loose unnamed module if unused. + if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for ( i = 0, l = config.modules.length; i < l; i++ ) { + modulesLog.push({ + name: config.modules[ i ].name, + tests: config.modules[ i ].tests + }); + } + + // The test run is officially beginning now + runLoggingCallbacks( "begin", { + totalTests: Test.count, + modules: modulesLog + }); + } + + config.blocking = false; + process( true ); +} + +function resumeProcessing() { + runStarted = true; + + // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) + if ( defined.setTimeout ) { + setTimeout(function() { + if ( config.current && config.current.semaphore > 0 ) { + return; + } + if ( config.timeout ) { + clearTimeout( config.timeout ); + } + + begin(); + }, 13 ); + } else { + begin(); + } +} + +function pauseProcessing() { + config.blocking = true; + + if ( config.testTimeout && defined.setTimeout ) { + clearTimeout( config.timeout ); + config.timeout = setTimeout(function() { + if ( config.current ) { + config.current.semaphore = 0; + QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); + } else { + throw new Error( "Test timed out" ); + } + resumeProcessing(); + }, config.testTimeout ); + } +} + function saveGlobal() { config.pollution = []; @@ -1487,12 +690,12 @@ function checkPollution() { newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); + QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); + QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); } } @@ -1503,7 +706,7 @@ function diff( a, b ) { for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { + if ( result[ i ] === b[ j ] ) { result.splice( i, 1 ); i--; break; @@ -1513,14 +716,15 @@ function diff( a, b ) { return result; } -function extend( a, b ) { +function extend( a, b, undefOnly ) { for ( var prop in b ) { if ( hasOwn.call( b, prop ) ) { + // Avoid "Member not found" error in IE8 caused by messing with window.constructor if ( !( prop === "constructor" && a === window ) ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; - } else { + } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { a[ prop ] = b[ prop ]; } } @@ -1530,77 +734,680 @@ function extend( a, b ) { return a; } -/** - * @param {HTMLElement} elem - * @param {string} type - * @param {Function} fn - */ -function addEvent( elem, type, fn ) { - // Standards-based browsers - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - // IE - } else { - elem.attachEvent( "on" + type, fn ); +function runLoggingCallbacks( key, args ) { + var i, l, callbacks; + + callbacks = config.callbacks[ key ]; + for ( i = 0, l = callbacks.length; i < l; i++ ) { + callbacks[ i ]( args ); } } -/** - * @param {Array|NodeList} elems - * @param {string} type - * @param {Function} fn - */ -function addEvents( elems, type, fn ) { - var i = elems.length; - while ( i-- ) { - addEvent( elems[i], type, fn ); - } -} +// DEPRECATED: This will be removed on 2.0.0+ +// This function verifies if the loggingCallbacks were modified by the user +// If so, it will restore it, assign the given callback and print a console warning +function verifyLoggingCallbacks() { + var loggingCallback, userCallback; -function hasClass( elem, name ) { - return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; -} + for ( loggingCallback in loggingCallbacks ) { + if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += (elem.className ? " " : "") + name; - } -} + userCallback = QUnit[ loggingCallback ]; -function removeClass( elem, name ) { - var set = " " + elem.className + " "; - // Class name may appear multiple times - while ( set.indexOf(" " + name + " ") > -1 ) { - set = set.replace(" " + name + " " , " "); - } - // If possible, trim it for prettiness, but not necessarily - elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); -} + // Restore the callback function + QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; -function id( name ) { - return !!( typeof document !== "undefined" && document && document.getElementById ) && - document.getElementById( name ); -} + // Assign the deprecated given callback + QUnit[ loggingCallback ]( userCallback ); -function registerLoggingCallback( key ) { - return function( callback ) { - config[key].push( callback ); - }; -} - -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks( key, scope, args ) { - var i, callbacks; - if ( QUnit.hasOwnProperty( key ) ) { - QUnit[ key ].call(scope, args ); - } else { - callbacks = config[ key ]; - for ( i = 0; i < callbacks.length; i++ ) { - callbacks[ i ].call( scope, args ); + if ( window.console && window.console.warn ) { + window.console.warn( + "QUnit." + loggingCallback + " was replaced with a new value.\n" + + "Please, check out the documentation on how to apply logging callbacks.\n" + + "Reference: http://api.qunitjs.com/category/callbacks/" + ); + } } } } +// from jquery.js +function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; +} + +function Test( settings ) { + var i, l; + + ++Test.count; + + extend( this, settings ); + this.assertions = []; + this.semaphore = 0; + this.usedAsync = false; + this.module = config.currentModule; + this.stack = sourceFromStacktrace( 3 ); + + // Register unique strings + for ( i = 0, l = this.module.tests; i < l.length; i++ ) { + if ( this.module.tests[ i ].name === this.testName ) { + this.testName += " "; + } + } + + this.testId = generateHash( this.module.name, this.testName ); + + this.module.tests.push({ + name: this.testName, + testId: this.testId + }); + + if ( settings.skip ) { + + // Skipped tests will fully ignore any sent callback + this.callback = function() {}; + this.async = false; + this.expected = 0; + } else { + this.assert = new Assert( this ); + } +} + +Test.count = 0; + +Test.prototype = { + before: function() { + if ( + + // Emit moduleStart when we're switching from one module to another + this.module !== config.previousModule || + + // They could be equal (both undefined) but if the previousModule property doesn't + // yet exist it means this is the first test in a suite that isn't wrapped in a + // module, in which case we'll just emit a moduleStart event for 'undefined'. + // Without this, reporters can get testStart before moduleStart which is a problem. + !hasOwn.call( config, "previousModule" ) + ) { + if ( hasOwn.call( config, "previousModule" ) ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + }); + } + config.previousModule = this.module; + config.moduleStats = { all: 0, bad: 0, started: now() }; + runLoggingCallbacks( "moduleStart", { + name: this.module.name, + tests: this.module.tests + }); + } + + config.current = this; + + this.testEnvironment = extend( {}, this.module.testEnvironment ); + delete this.testEnvironment.beforeEach; + delete this.testEnvironment.afterEach; + + this.started = now(); + runLoggingCallbacks( "testStart", { + name: this.testName, + module: this.module.name, + testId: this.testId + }); + + if ( !config.pollution ) { + saveGlobal(); + } + }, + + run: function() { + var promise; + + config.current = this; + + if ( this.async ) { + QUnit.stop(); + } + + this.callbackStarted = now(); + + if ( config.notrycatch ) { + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); + return; + } + + try { + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); + } catch ( e ) { + this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); + + // else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + QUnit.start(); + } + } + }, + + after: function() { + checkPollution(); + }, + + queueHook: function( hook, hookName ) { + var promise, + test = this; + return function runHook() { + config.current = test; + if ( config.notrycatch ) { + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); + return; + } + try { + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); + } catch ( error ) { + test.pushFailure( hookName + " failed on " + test.testName + ": " + + ( error.message || error ), extractStacktrace( error, 0 ) ); + } + }; + }, + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function( handler ) { + var hooks = []; + + // Hooks are ignored on skipped tests + if ( this.skip ) { + return hooks; + } + + if ( this.module.testEnvironment && + QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) { + hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) ); + } + + return hooks; + }, + + finish: function() { + config.current = this; + if ( config.requireExpects && this.expected === null ) { + this.pushFailure( "Expected number of assertions to be defined, but expect() was " + + "not called.", this.stack ); + } else if ( this.expected !== null && this.expected !== this.assertions.length ) { + this.pushFailure( "Expected " + this.expected + " assertions, but " + + this.assertions.length + " were run", this.stack ); + } else if ( this.expected === null && !this.assertions.length ) { + this.pushFailure( "Expected at least one assertion, but none were run - call " + + "expect(0) to accept zero assertions.", this.stack ); + } + + var i, + bad = 0; + + this.runtime = now() - this.started; + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + + for ( i = 0; i < this.assertions.length; i++ ) { + if ( !this.assertions[ i ].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + + runLoggingCallbacks( "testDone", { + name: this.testName, + module: this.module.name, + skipped: !!this.skip, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // DEPRECATED: this property will be removed in 2.0.0, use runtime instead + duration: this.runtime + }); + + // QUnit.reset() is deprecated and will be replaced for a new + // fixture reset function on QUnit 2.0/2.1. + // It's still called here for backwards compatibility handling + QUnit.reset(); + + config.current = undefined; + }, + + queue: function() { + var bad, + test = this; + + if ( !this.valid() ) { + return; + } + + function run() { + + // each of these can by async + synchronize([ + function() { + test.before(); + }, + + test.hooks( "beforeEach" ), + + function() { + test.run(); + }, + + test.hooks( "afterEach" ).reverse(), + + function() { + test.after(); + }, + function() { + test.finish(); + } + ]); + } + + // `bad` initialized at top of scope + // defer when previous test run passed, if storage is available + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); + + if ( bad ) { + run(); + } else { + synchronize( run, true ); + } + }, + + push: function( result, actual, expected, message ) { + var source, + details = { + module: this.module.name, + name: this.testName, + result: result, + message: message, + actual: actual, + expected: expected, + testId: this.testId, + runtime: now() - this.started + }; + + if ( !result ) { + source = sourceFromStacktrace(); + + if ( source ) { + details.source = source; + } + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push({ + result: !!result, + message: message + }); + }, + + pushFailure: function( message, source, actual ) { + if ( !this instanceof Test ) { + throw new Error( "pushFailure() assertion outside test context, was " + + sourceFromStacktrace( 2 ) ); + } + + var details = { + module: this.module.name, + name: this.testName, + result: false, + message: message || "error", + actual: actual || null, + testId: this.testId, + runtime: now() - this.started + }; + + if ( source ) { + details.source = source; + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push({ + result: false, + message: message + }); + }, + + resolvePromise: function( promise, phase ) { + var then, message, + test = this; + if ( promise != null ) { + then = promise.then; + if ( QUnit.objectType( then ) === "function" ) { + QUnit.stop(); + then.call( + promise, + QUnit.start, + function( error ) { + message = "Promise rejected " + + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + + " " + test.testName + ": " + ( error.message || error ); + test.pushFailure( message, extractStacktrace( error, 0 ) ); + + // else next test will carry the responsibility + saveGlobal(); + + // Unblock + QUnit.start(); + } + ); + } + } + }, + + valid: function() { + var include, + filter = config.filter && config.filter.toLowerCase(), + module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), + fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); + + // Internally-generated tests are always valid + if ( this.callback && this.callback.validTest ) { + return true; + } + + if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { + return false; + } + + if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) { + return false; + } + + if ( !filter ) { + return true; + } + + include = filter.charAt( 0 ) !== "!"; + if ( !include ) { + filter = filter.slice( 1 ); + } + + // If the filter matches, we need to honour include + if ( fullName.indexOf( filter ) !== -1 ) { + return include; + } + + // Otherwise, do the opposite + return !include; + } + +}; + +// Resets the test setup. Useful for tests that modify the DOM. +/* +DEPRECATED: Use multiple tests instead of resetting inside a test. +Use testStart or testDone for custom cleanup. +This method will throw an error in 2.0, and will be removed in 2.1 +*/ +QUnit.reset = function() { + + // Return on non-browser environments + // This is necessary to not break on node tests + if ( typeof window === "undefined" ) { + return; + } + + var fixture = defined.document && document.getElementById && + document.getElementById( "qunit-fixture" ); + + if ( fixture ) { + fixture.innerHTML = config.fixture; + } +}; + +QUnit.pushFailure = function() { + if ( !QUnit.config.current ) { + throw new Error( "pushFailure() assertion outside test context, in " + + sourceFromStacktrace( 2 ) ); + } + + // Gets current test obj + var currentTest = QUnit.config.current; + + return currentTest.pushFailure.apply( currentTest, arguments ); +}; + +// Based on Java's String.hashCode, a simple but not +// rigorously collision resistant hashing function +function generateHash( module, testName ) { + var hex, + i = 0, + hash = 0, + str = module + "\x1C" + testName, + len = str.length; + + for ( ; i < len; i++ ) { + hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); + hash |= 0; + } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + hex = ( 0x100000000 + hash ).toString( 16 ); + if ( hex.length < 8 ) { + hex = "0000000" + hex; + } + + return hex.slice( -8 ); +} + +function Assert( testContext ) { + this.test = testContext; +} + +// Assert helpers +QUnit.assert = Assert.prototype = { + + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + expect: function( asserts ) { + if ( arguments.length === 1 ) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + }, + + // Increment this Test's semaphore counter, then return a single-use function that + // decrements that counter a maximum of once. + async: function() { + var test = this.test, + popped = false; + + test.semaphore += 1; + test.usedAsync = true; + pauseProcessing(); + + return function done() { + if ( !popped ) { + test.semaphore -= 1; + popped = true; + resumeProcessing(); + } else { + test.pushFailure( "Called the callback returned from `assert.async` more than once", + sourceFromStacktrace( 2 ) ); + } + }; + }, + + // Exports test.push() to the user API + push: function( /* result, actual, expected, message */ ) { + var assert = this, + currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if ( !currentTest ) { + throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); + } + + if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { + currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", + sourceFromStacktrace( 2 ) ); + + // Allow this assertion to continue running anyway... + } + + if ( !( assert instanceof Assert ) ) { + assert = currentTest.assert; + } + return assert.test.push.apply( assert.test, arguments ); + }, + + ok: function( result, message ) { + message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + + QUnit.dump.parse( result ) ); + this.push( !!result, result, true, message ); + }, + + notOk: function( result, message ) { + message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + + QUnit.dump.parse( result ) ); + this.push( !result, result, false, message ); + }, + + equal: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.push( expected == actual, actual, expected, message ); + }, + + notEqual: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.push( expected != actual, actual, expected, message ); + }, + + propEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + notPropEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + deepEqual: function( actual, expected, message ) { + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + notDeepEqual: function( actual, expected, message ) { + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); + }, + + strictEqual: function( actual, expected, message ) { + this.push( expected === actual, actual, expected, message ); + }, + + notStrictEqual: function( actual, expected, message ) { + this.push( expected !== actual, actual, expected, message ); + }, + + "throws": function( block, expected, message ) { + var actual, expectedType, + expectedOutput = expected, + ok = false, + currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; + + // 'expected' is optional unless doing string comparison + if ( message == null && typeof expected === "string" ) { + message = expected; + expected = null; + } + + currentTest.ignoreGlobalErrors = true; + try { + block.call( currentTest.testEnvironment ); + } catch (e) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if ( actual ) { + expectedType = QUnit.objectType( expected ); + + // we don't want to validate thrown error + if ( !expected ) { + ok = true; + expectedOutput = null; + + // expected is a regexp + } else if ( expectedType === "regexp" ) { + ok = expected.test( errorString( actual ) ); + + // expected is a string + } else if ( expectedType === "string" ) { + ok = expected === errorString( actual ); + + // expected is a constructor, maybe an Error constructor + } else if ( expectedType === "function" && actual instanceof expected ) { + ok = true; + + // expected is an Error object + } else if ( expectedType === "object" ) { + ok = actual instanceof expected.constructor && + actual.name === expected.name && + actual.message === expected.message; + + // expected is a validation function which returns true if validation passed + } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { + expectedOutput = null; + ok = true; + } + } + + currentTest.assert.push( ok, actual, expectedOutput, message ); + } +}; + +// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word +// Known to us are: Closure Compiler, Narwhal +(function() { + /*jshint sub:true */ + Assert.prototype.raises = Assert.prototype[ "throws" ]; +}()); + // Test for equality any JavaScript type. // Author: Philippe Rathé QUnit.equiv = (function() { @@ -1619,22 +1426,26 @@ QUnit.equiv = (function() { // the real equiv function var innerEquiv, + // stack to decide between skip/abort functions callers = [], + // stack to avoiding loops from circular referencing parents = [], parentsB = [], - getProto = Object.getPrototypeOf || function ( obj ) { - /*jshint camelcase:false */ + getProto = Object.getPrototypeOf || function( obj ) { + /* jshint camelcase: false, proto: true */ return obj.__proto__; }, - callbacks = (function () { + callbacks = (function() { // for string, boolean, number and null function useStrictEquality( b, a ) { + /*jshint eqeqeq:false */ if ( b instanceof a.constructor || a instanceof b.constructor ) { + // to catch short annotation VS 'new' annotation of a // declaration // e.g. var i = 1; @@ -1662,10 +1473,13 @@ QUnit.equiv = (function() { "regexp": function( b, a ) { return QUnit.objectType( b ) === "regexp" && + // the regex itself a.source === b.source && + // and its modifiers a.global === b.global && + // (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline && @@ -1676,7 +1490,7 @@ QUnit.equiv = (function() { // - abort otherwise, // initial === would have catch identical references anyway "function": function() { - var caller = callers[callers.length - 1]; + var caller = callers[ callers.length - 1 ]; return caller !== Object && typeof caller !== "undefined"; }, @@ -1700,10 +1514,10 @@ QUnit.equiv = (function() { for ( i = 0; i < len; i++ ) { loop = false; for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[j] === a[i]; - bCircular = parentsB[j] === b[i]; + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { - if ( a[i] === b[i] || aCircular && bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { parents.pop(); @@ -1712,7 +1526,7 @@ QUnit.equiv = (function() { } } } - if ( !loop && !innerEquiv(a[i], b[i]) ) { + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { parents.pop(); parentsB.pop(); return false; @@ -1724,6 +1538,7 @@ QUnit.equiv = (function() { }, "object": function( b, a ) { + /*jshint forin:false */ var i, j, loop, aCircular, bCircular, // Default to true @@ -1734,11 +1549,12 @@ QUnit.equiv = (function() { // comparing constructors is more strict than using // instanceof if ( a.constructor !== b.constructor ) { + // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. - if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || - ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { - return false; + if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) || + ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) { + return false; } } @@ -1753,10 +1569,10 @@ QUnit.equiv = (function() { for ( i in a ) { loop = false; for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[j] === a[i]; - bCircular = parentsB[j] === b[i]; + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { - if ( a[i] === b[i] || aCircular && bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { eq = false; @@ -1764,8 +1580,8 @@ QUnit.equiv = (function() { } } } - aProperties.push(i); - if ( !loop && !innerEquiv(a[i], b[i]) ) { + aProperties.push( i ); + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { eq = false; break; } @@ -1791,35 +1607,30 @@ QUnit.equiv = (function() { return true; // end transition } - return (function( a, b ) { + return ( (function( a, b ) { if ( a === b ) { return true; // catch the most you can } else if ( a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || - QUnit.objectType(a) !== QUnit.objectType(b) ) { - return false; // don't lose time with error prone cases + QUnit.objectType( a ) !== QUnit.objectType( b ) ) { + + // don't lose time with error prone cases + return false; } else { - return bindCallbacks(a, callbacks, [ b, a ]); + return bindCallbacks( a, callbacks, [ b, a ] ); } // apply transition with (1..n) arguments - }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) ); + }( args[ 0 ], args[ 1 ] ) ) && + innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) ); }; return innerEquiv; }()); -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { +// Based on jsDump by Ariel Flesler +// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html +QUnit.dump = (function() { function quote( str ) { return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; } @@ -1827,48 +1638,57 @@ QUnit.jsDump = (function() { return o + ""; } function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent( 1 ); if ( arr.join ) { arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } - return [ pre, inner + arr, base + post ].join(s); + return [ pre, inner + arr, base + post ].join( s ); } function array( arr, stack ) { - var i = arr.length, ret = new Array(i); + var i = arr.length, + ret = new Array( i ); + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Array]"; + } + this.up(); while ( i-- ) { - ret[i] = this.parse( arr[i] , undefined , stack); + ret[ i ] = this.parse( arr[ i ], undefined, stack ); } this.down(); return join( "[", ret, "]" ); } var reName = /^function (\w+)/, - jsDump = { - // type is used mostly internally, you can fix a (custom)type in advance - parse: function( obj, type, stack ) { - stack = stack || [ ]; - var inStack, res, - parser = this.parsers[ type || this.typeOf(obj) ]; + dump = { - type = typeof parser; - inStack = inArray( obj, stack ); + // objType is used mostly internally, you can fix a (custom) type in advance + parse: function( obj, objType, stack ) { + stack = stack || []; + var res, parser, parserType, + inStack = inArray( obj, stack ); if ( inStack !== -1 ) { - return "recursion(" + (inStack - stack.length) + ")"; + return "recursion(" + ( inStack - stack.length ) + ")"; } - if ( type === "function" ) { + + objType = objType || this.typeOf( obj ); + parser = this.parsers[ objType ]; + parserType = typeof parser; + + if ( parserType === "function" ) { stack.push( obj ); res = parser.call( this, obj, stack ); stack.pop(); return res; } - return ( type === "string" ) ? parser : this.parsers.error; + return ( parserType === "string" ) ? parser : this.parsers.error; }, typeOf: function( obj ) { var type; @@ -1876,23 +1696,29 @@ QUnit.jsDump = (function() { type = "null"; } else if ( typeof obj === "undefined" ) { type = "undefined"; - } else if ( QUnit.is( "regexp", obj) ) { + } else if ( QUnit.is( "regexp", obj ) ) { type = "regexp"; - } else if ( QUnit.is( "date", obj) ) { + } else if ( QUnit.is( "date", obj ) ) { type = "date"; - } else if ( QUnit.is( "function", obj) ) { + } else if ( QUnit.is( "function", obj ) ) { type = "function"; - } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { + } else if ( obj.setInterval !== undefined && + obj.document !== undefined && + obj.nodeType === undefined ) { type = "window"; } else if ( obj.nodeType === 9 ) { type = "document"; } else if ( obj.nodeType ) { type = "node"; } else if ( + // native arrays toString.call( obj ) === "[object Array]" || + // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) + ( typeof obj.length === "number" && obj.item !== undefined && + ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && + obj[ 0 ] === undefined ) ) ) ) { type = "array"; } else if ( obj.constructor === Error.prototype.constructor ) { @@ -1903,7 +1729,7 @@ QUnit.jsDump = (function() { return type; }, separator: function() { - return this.multiline ? this.HTML ? "
      " : "\n" : this.HTML ? " " : " "; + return this.multiline ? this.HTML ? "
      " : "\n" : this.HTML ? " " : " "; }, // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { @@ -1912,9 +1738,9 @@ QUnit.jsDump = (function() { } var chr = this.indentChar; if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); + chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } - return new Array( this.depth + ( extra || 0 ) ).join(chr); + return new Array( this.depth + ( extra || 0 ) ).join( chr ); }, up: function( a ) { this.depth += a || 1; @@ -1923,7 +1749,7 @@ QUnit.jsDump = (function() { this.depth -= a || 1; }, setParser: function( name, parser ) { - this.parsers[name] = parser; + this.parsers[ name ] = parser; }, // The next 3 are exposed so you can use them quote: quote, @@ -1931,11 +1757,13 @@ QUnit.jsDump = (function() { join: join, // depth: 1, - // This is the list of parsers, to modify them, use jsDump.setParser + maxDepth: QUnit.config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser parsers: { window: "[Window]", document: "[Document]", - error: function(error) { + error: function( error ) { return "Error(\"" + error.message + "\")"; }, unknown: "[Unknown]", @@ -1943,52 +1771,71 @@ QUnit.jsDump = (function() { "undefined": "undefined", "function": function( fn ) { var ret = "function", + // functions never have name in IE - name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; + name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; if ( name ) { ret += " " + name; } ret += "( "; - ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); + ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); + return join( ret, dump.parse( fn, "functionCode" ), "}" ); }, array: array, nodelist: array, "arguments": array, object: function( map, stack ) { - /*jshint forin:false */ - var ret = [ ], keys, key, val, i; - QUnit.jsDump.up(); + var keys, key, val, i, nonEnumerableProperties, + ret = []; + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Object]"; + } + + dump.up(); keys = []; for ( key in map ) { keys.push( key ); } + + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = [ "message", "name" ]; + for ( i in nonEnumerableProperties ) { + key = nonEnumerableProperties[ i ]; + if ( key in map && inArray( key, keys ) < 0 ) { + keys.push( key ); + } + } keys.sort(); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; val = map[ key ]; - ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); + ret.push( dump.parse( key, "key" ) + ": " + + dump.parse( val, undefined, stack ) ); } - QUnit.jsDump.down(); + dump.down(); return join( "{", ret, "}" ); }, node: function( node ) { var len, i, val, - open = QUnit.jsDump.HTML ? "<" : "<", - close = QUnit.jsDump.HTML ? ">" : ">", + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", tag = node.nodeName.toLowerCase(), ret = open + tag, attrs = node.attributes; if ( attrs ) { for ( i = 0, len = attrs.length; i < len; i++ ) { - val = attrs[i].nodeValue; - // IE6 includes all attributes in .attributes, even ones not explicitly set. - // Those have values like undefined, null, 0, false, "" or "inherit". + val = attrs[ i ].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". if ( val && val !== "inherit" ) { - ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); + ret += " " + attrs[ i ].nodeName + "=" + + dump.parse( val, "attribute" ); } } } @@ -2001,6 +1848,7 @@ QUnit.jsDump = (function() { return ret + open + "/" + tag + close; }, + // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, @@ -2010,10 +1858,11 @@ QUnit.jsDump = (function() { return ""; } - args = new Array(l); + args = new Array( l ); while ( l-- ) { + // 97 is 'a' - args[l] = String.fromCharCode(97+l); + args[ l ] = String.fromCharCode( 97 + l ); } return " " + args.join( ", " ) + " "; }, @@ -2037,176 +1886,1943 @@ QUnit.jsDump = (function() { multiline: true }; - return jsDump; + return dump; }()); -// from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } +// back compat +QUnit.jsDump = QUnit.dump; - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; +// For browser, export only select globals +if ( typeof window !== "undefined" ) { + + // Deprecated + // Extend assert methods to QUnit and Global scope through Backwards compatibility + (function() { + var i, + assertions = Assert.prototype; + + function applyCurrent( current ) { + return function() { + var assert = new Assert( QUnit.config.current ); + current.apply( assert, arguments ); + }; } - } - return -1; + for ( i in assertions ) { + QUnit[ i ] = applyCurrent( assertions[ i ] ); + } + })(); + + (function() { + var i, l, + keys = [ + "test", + "module", + "expect", + "asyncTest", + "start", + "stop", + "ok", + "notOk", + "equal", + "notEqual", + "propEqual", + "notPropEqual", + "deepEqual", + "notDeepEqual", + "strictEqual", + "notStrictEqual", + "throws" + ]; + + for ( i = 0, l = keys.length; i < l; i++ ) { + window[ keys[ i ] ] = QUnit[ keys[ i ] ]; + } + })(); + + window.QUnit = QUnit; } +// For nodejs +if ( typeof module !== "undefined" && module && module.exports ) { + module.exports = QUnit; + + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; +} + +// For CommonJS with exports, but without module.exports, like Rhino +if ( typeof exports !== "undefined" && exports ) { + exports.QUnit = QUnit; +} + +if ( typeof define === "function" && define.amd ) { + define( function() { + return QUnit; + } ); + QUnit.config.autostart = false; +} + +// Get a reference to the global object, like window in browsers +}( (function() { + return this; +})() )); + +/*istanbul ignore next */ +// jscs:disable maximumLineLength /* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. * - * Released under the MIT license. + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ + * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" + * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the quick brown fox jumpsed 0; i-- ) { - if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && - n[ i - 1 ] == o[ n[i].row - 1 ]) { + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number} optDeadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) { + var deadline, checklines, commonlength, + commonprefix, commonsuffix, diffs; + // Set a deadline by which time the diff must be complete. + if ( typeof optDeadline === "undefined" ) { + if ( this.DiffTimeout <= 0 ) { + optDeadline = Number.MAX_VALUE; + } else { + optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000; + } + } + deadline = optDeadline; - n[ i - 1 ] = { - text: n[ i - 1 ], - row: n[i].row - 1 - }; - o[ n[i].row - 1 ] = { - text: o[ n[i].row - 1 ], - row: i - 1 - }; - } - } + // Check for null inputs. + if ( text1 === null || text2 === null ) { + throw new Error( "Null input. (DiffMain)" ); + } - return { - o: o, - n: n - }; + // Check for equality (speedup). + if ( text1 === text2 ) { + if ( text1 ) { + return [ + [ DIFF_EQUAL, text1 ] + ]; + } + return []; + } + + if ( typeof optChecklines === "undefined" ) { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix( text1, text2 ); + commonprefix = text1.substring( 0, commonlength ); + text1 = text1.substring( commonlength ); + text2 = text2.substring( commonlength ); + + // Trim off common suffix (speedup). + ///////// + commonlength = this.diffCommonSuffix( text1, text2 ); + commonsuffix = text1.substring( text1.length - commonlength ); + text1 = text1.substring( 0, text1.length - commonlength ); + text2 = text2.substring( 0, text2.length - commonlength ); + + // Compute the diff on the middle block. + diffs = this.diffCompute( text1, text2, checklines, deadline ); + + // Restore the prefix and suffix. + if ( commonprefix ) { + diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); + } + if ( commonsuffix ) { + diffs.push( [ DIFF_EQUAL, commonsuffix ] ); + } + this.diffCleanupMerge( diffs ); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { + var changes, equalities, equalitiesLength, lastequality, + pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + preIns = false; + // Is there a deletion operation before the last equality. + preDel = false; + // Is there an insertion operation after the last equality. + postIns = false; + // Is there a deletion operation after the last equality. + postDel = false; + while ( pointer < diffs.length ) { + if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. + if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) { + // Candidate found. + equalities[ equalitiesLength++ ] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[ pointer ][ 1 ]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + } else { // An insertion or deletion. + if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { + postDel = true; + } else { + postIns = true; + } + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || + ( ( lastequality.length < this.DiffEditCost / 2 ) && + ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { + // Duplicate record. + diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { + var op, data, x, html = []; + for ( x = 0; x < diffs.length; x++ ) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch ( op ) { + case DIFF_INSERT: + html[x] = "" + data + ""; + break; + case DIFF_DELETE: + html[x] = "" + data + ""; + break; + case DIFF_EQUAL: + html[x] = "" + data + ""; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerstart; + // Quick check for common null cases. + if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min( text1.length, text2.length ); + pointermid = pointermax; + pointerstart = 0; + while ( pointermin < pointermid ) { + if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerend; + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while ( pointermin < pointermid ) { + if (text1.substring( text1.length - pointermid, text1.length - pointerend ) === + text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { + var diffs, longtext, shorttext, i, hm, + text1A, text2A, text1B, text2B, + midCommon, diffsA, diffsB; + + if ( !text1 ) { + // Just add some text (speedup). + return [ + [ DIFF_INSERT, text2 ] + ]; + } + + if (!text2) { + // Just delete some text (speedup). + return [ + [ DIFF_DELETE, text1 ] + ]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf( shorttext ); + if ( i !== -1 ) { + // Shorter text is inside the longer text (speedup). + diffs = [ + [ DIFF_INSERT, longtext.substring( 0, i ) ], + [ DIFF_EQUAL, shorttext ], + [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] + ]; + // Swap insertions for deletions if diff is reversed. + if ( text1.length > text2.length ) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if ( shorttext.length === 1 ) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + // Merge the results. + return diffsA.concat([ + [ DIFF_EQUAL, midCommon ] + ], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) { + var longtext, shorttext, dmp, + text1A, text2B, text2A, text1B, midCommon, + hm1, hm2, hm; + if (this.DiffTimeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, j, bestCommon, prefixLength, suffixLength, + bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix(longtext.substring(i), + shorttext.substring(j)); + suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [ bestLongtextA, bestLongtextB, + bestShorttextA, bestShorttextB, bestCommon + ]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + text1A, text1B, text2A, text2B; + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [ text1A, text1B, text2A, text2B, midCommon ]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) { + var a, diffs, linearray, pointer, countInsert, + countDelete, textInsert, textDelete, j; + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push( [ DIFF_EQUAL, "" ] ); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch ( diffs[pointer][0] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - countDelete - countInsert, + countDelete + countInsert); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice( pointer, 0, a[j] ); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) { + var text1Length, text2Length, maxD, vOffset, vLength, + v1, v2, x, delta, front, k1start, k1end, k2start, + k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = (delta % 2 !== 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while (x1 < text1Length && y1 < text2Length && + text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while (x2 < text1Length && y2 < text2Length && + text1.charAt(text1Length - x2 - 1) === + text2.charAt(text2Length - y2 - 1)) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) { + var changes, equalities, equalitiesLength, lastequality, + pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, + lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && (lastequality.length <= + Math.max(lengthInsertions1, lengthDeletions1)) && + (lastequality.length <= Math.max(lengthInsertions2, + lengthDeletions2))) { + // Duplicate record. + diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + lengthInsertions1 = 0; // Reset the counters. + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && + diffs[pointer][0] === DIFF_INSERT) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if (overlapLength1 >= deletion.length / 2 || + overlapLength1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlapLength1); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if (overlapLength2 >= deletion.length / 2 || + overlapLength2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlapLength2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) { + var text1Length, text2Length, textLength, + best, length, pattern, found; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(textLength - length) === + text2.substring(0, length)) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // e.g. lineArray[4] === 'Hello\n' + lineHash = {}; // e.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode( lineHash[ line ] ); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { + var x, chars, text, y; + for ( x = 0; x < diffs.length; x++ ) { + chars = diffs[x][1]; + text = []; + for ( y = 0; y < chars.length; y++ ) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) { + var pointer, countDelete, countInsert, textInsert, textDelete, + commonlength, changes; + diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + commonlength; + while (pointer < diffs.length) { + switch ( diffs[ pointer ][ 0 ] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + // Factor out any common prefixies. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if ((pointer - countDelete - countInsert) > 0 && + diffs[pointer - countDelete - countInsert - 1][0] === + DIFF_EQUAL) { + diffs[pointer - countDelete - countInsert - 1][1] += + textInsert.substring(0, commonlength); + } else { + diffs.splice( 0, 0, [ DIFF_EQUAL, + textInsert.substring( 0, commonlength ) + ] ); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = textInsert.substring(textInsert.length - + commonlength) + diffs[pointer][1]; + textInsert = textInsert.substring(0, textInsert.length - + commonlength); + textDelete = textDelete.substring(0, textDelete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice( pointer - countInsert, + countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); + } else if (countInsert === 0) { + diffs.splice( pointer - countDelete, + countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); + } else { + diffs.splice( pointer - countDelete - countInsert, + countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); + } + pointer = pointer - countDelete - countInsert + + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length - + diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === + diffs[ pointer + 1 ][ 1 ] ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function(o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + //console.log(output); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; +}()); +// jscs:enable + +(function() { + +// Deprecated QUnit.init - Ref #530 +// Re-initialize the configuration options +QUnit.init = function() { + var tests, banner, result, qunit, + config = QUnit.config; + + config.stats = { all: 0, bad: 0 }; + config.moduleStats = { all: 0, bad: 0 }; + config.started = 0; + config.updateRate = 1000; + config.blocking = false; + config.autostart = true; + config.autorun = false; + config.filter = ""; + config.queue = []; + + // Return on non-browser environments + // This is necessary to not break on node tests + if ( typeof window === "undefined" ) { + return; } - return function( o, n ) { - o = o.replace( /\s+$/, "" ); - n = n.replace( /\s+$/, "" ); + qunit = id( "qunit" ); + if ( qunit ) { + qunit.innerHTML = + "

      " + escapeText( document.title ) + "

      " + + "

      " + + "
      " + + "

      " + + "
        "; + } - var i, pre, - str = "", - out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), - oSpace = o.match(/\s+/g), - nSpace = n.match(/\s+/g); + tests = id( "qunit-tests" ); + banner = id( "qunit-banner" ); + result = id( "qunit-testresult" ); - if ( oSpace == null ) { - oSpace = [ " " ]; - } - else { - oSpace.push( " " ); - } + if ( tests ) { + tests.innerHTML = ""; + } - if ( nSpace == null ) { - nSpace = [ " " ]; - } - else { - nSpace.push( " " ); - } + if ( banner ) { + banner.className = ""; + } - if ( out.n.length === 0 ) { - for ( i = 0; i < out.o.length; i++ ) { - str += "" + out.o[i] + oSpace[i] + ""; - } - } - else { - if ( out.n[0].text == null ) { - for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { - str += "" + out.o[n] + oSpace[n] + ""; - } - } + if ( result ) { + result.parentNode.removeChild( result ); + } - for ( i = 0; i < out.n.length; i++ ) { - if (out.n[i].text == null) { - str += "" + out.n[i] + nSpace[i] + ""; - } - else { - // `pre` initialized at top of scope - pre = ""; + if ( tests ) { + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...
         "; + } +}; - for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { - pre += "" + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -}()); - -// for CommonJS environments, export everything -if ( typeof exports !== "undefined" ) { - extend( exports, QUnit.constructor.prototype ); +// Don't load the HTML Reporter on non-Browser environments +if ( typeof window === "undefined" ) { + return; } -// get at whatever the global object is, like window in browsers -}( (function() {return this;}.call()) )); +var config = QUnit.config, + hasOwn = Object.prototype.hasOwnProperty, + defined = { + document: window.document !== undefined, + sessionStorage: (function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; + } + }()) + }, + modulesList = []; + +/** +* Escape text for attribute or text content. +*/ +function escapeText( s ) { + if ( !s ) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace( /['"<>&]/g, function( s ) { + switch ( s ) { + case "'": + return "'"; + case "\"": + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + } + }); +} + +/** + * @param {HTMLElement} elem + * @param {string} type + * @param {Function} fn + */ +function addEvent( elem, type, fn ) { + if ( elem.addEventListener ) { + + // Standards-based browsers + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + + // support: IE <9 + elem.attachEvent( "on" + type, function() { + var event = window.event; + if ( !event.target ) { + event.target = event.srcElement || document; + } + + fn.call( elem, event ); + }); + } +} + +/** + * @param {Array|NodeList} elems + * @param {string} type + * @param {Function} fn + */ +function addEvents( elems, type, fn ) { + var i = elems.length; + while ( i-- ) { + addEvent( elems[ i ], type, fn ); + } +} + +function hasClass( elem, name ) { + return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; +} + +function addClass( elem, name ) { + if ( !hasClass( elem, name ) ) { + elem.className += ( elem.className ? " " : "" ) + name; + } +} + +function toggleClass( elem, name ) { + if ( hasClass( elem, name ) ) { + removeClass( elem, name ); + } else { + addClass( elem, name ); + } +} + +function removeClass( elem, name ) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while ( set.indexOf( " " + name + " " ) >= 0 ) { + set = set.replace( " " + name + " ", " " ); + } + + // trim for prettiness + elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); +} + +function id( name ) { + return defined.document && document.getElementById && document.getElementById( name ); +} + +function getUrlConfigHtml() { + var i, j, val, + escaped, escapedTooltip, + selection = false, + len = config.urlConfig.length, + urlConfigHtml = ""; + + for ( i = 0; i < len; i++ ) { + val = config.urlConfig[ i ]; + if ( typeof val === "string" ) { + val = { + id: val, + label: val + }; + } + + escaped = escapeText( val.id ); + escapedTooltip = escapeText( val.tooltip ); + + if ( config[ val.id ] === undefined ) { + config[ val.id ] = QUnit.urlParams[ val.id ]; + } + + if ( !val.value || typeof val.value === "string" ) { + urlConfigHtml += ""; + } else { + urlConfigHtml += ""; + } + } + + return urlConfigHtml; +} + +// Handle "click" events on toolbar checkboxes and "change" for select menus. +// Updates the URL with the new state of `config.urlConfig` values. +function toolbarChanged() { + var updatedUrl, value, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ( "selectedIndex" in field ) { + value = field.options[ field.selectedIndex ].value || undefined; + } else { + value = field.checked ? ( field.defaultValue || true ) : undefined; + } + + params[ field.name ] = value; + updatedUrl = setUrl( params ); + + if ( "hidepassed" === field.name && "replaceState" in window.history ) { + config[ field.name ] = value || false; + if ( value ) { + addClass( id( "qunit-tests" ), "hidepass" ); + } else { + removeClass( id( "qunit-tests" ), "hidepass" ); + } + + // It is not necessary to refresh the whole page + window.history.replaceState( null, "", updatedUrl ); + } else { + window.location = updatedUrl; + } +} + +function setUrl( params ) { + var key, + querystring = "?"; + + params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); + + for ( key in params ) { + if ( hasOwn.call( params, key ) ) { + if ( params[ key ] === undefined ) { + continue; + } + querystring += encodeURIComponent( key ); + if ( params[ key ] !== true ) { + querystring += "=" + encodeURIComponent( params[ key ] ); + } + querystring += "&"; + } + } + return location.protocol + "//" + location.host + + location.pathname + querystring.slice( 0, -1 ); +} + +function applyUrlParams() { + var selectedModule, + modulesList = id( "qunit-modulefilter" ), + filter = id( "qunit-filter-input" ).value; + + selectedModule = modulesList ? + decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : + undefined; + + window.location = setUrl({ + module: ( selectedModule === "" ) ? undefined : selectedModule, + filter: ( filter === "" ) ? undefined : filter, + + // Remove testId filter + testId: undefined + }); +} + +function toolbarUrlConfigContainer() { + var urlConfigContainer = document.createElement( "span" ); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass( urlConfigContainer, "qunit-url-config" ); + + // For oldIE support: + // * Add handlers to the individual elements instead of the container + // * Use "click" instead of "change" for checkboxes + addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); + addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); + + return urlConfigContainer; +} + +function toolbarLooseFilter() { + var filter = document.createElement( "form" ), + label = document.createElement( "label" ), + input = document.createElement( "input" ), + button = document.createElement( "button" ); + + addClass( filter, "qunit-filter" ); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild( input ); + + filter.appendChild( label ); + filter.appendChild( button ); + addEvent( filter, "submit", function( ev ) { + applyUrlParams(); + + if ( ev && ev.preventDefault ) { + ev.preventDefault(); + } + + return false; + }); + + return filter; +} + +function toolbarModuleFilterHtml() { + var i, + moduleFilterHtml = ""; + + if ( !modulesList.length ) { + return false; + } + + modulesList.sort(function( a, b ) { + return a.localeCompare( b ); + }); + + moduleFilterHtml += "" + + ""; + + return moduleFilterHtml; +} + +function toolbarModuleFilter() { + var toolbar = id( "qunit-testrunner-toolbar" ), + moduleFilter = document.createElement( "span" ), + moduleFilterHtml = toolbarModuleFilterHtml(); + + if ( !toolbar || !moduleFilterHtml ) { + return false; + } + + moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); + moduleFilter.innerHTML = moduleFilterHtml; + + addEvent( moduleFilter.lastChild, "change", applyUrlParams ); + + toolbar.appendChild( moduleFilter ); +} + +function appendToolbar() { + var toolbar = id( "qunit-testrunner-toolbar" ); + + if ( toolbar ) { + toolbar.appendChild( toolbarUrlConfigContainer() ); + toolbar.appendChild( toolbarLooseFilter() ); + } +} + +function appendHeader() { + var header = id( "qunit-header" ); + + if ( header ) { + header.innerHTML = "" + header.innerHTML + " "; + } +} + +function appendBanner() { + var banner = id( "qunit-banner" ); + + if ( banner ) { + banner.className = ""; + } +} + +function appendTestResults() { + var tests = id( "qunit-tests" ), + result = id( "qunit-testresult" ); + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + tests.innerHTML = ""; + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...
         "; + } +} + +function storeFixture() { + var fixture = id( "qunit-fixture" ); + if ( fixture ) { + config.fixture = fixture.innerHTML; + } +} + +function appendUserAgent() { + var userAgent = id( "qunit-userAgent" ); + + if ( userAgent ) { + userAgent.innerHTML = ""; + userAgent.appendChild( + document.createTextNode( + "QUnit " + QUnit.version + "; " + navigator.userAgent + ) + ); + } +} + +function appendTestsList( modules ) { + var i, l, x, z, test, moduleObj; + + for ( i = 0, l = modules.length; i < l; i++ ) { + moduleObj = modules[ i ]; + + if ( moduleObj.name ) { + modulesList.push( moduleObj.name ); + } + + for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { + test = moduleObj.tests[ x ]; + + appendTest( test.name, test.testId, moduleObj.name ); + } + } +} + +function appendTest( name, testId, moduleName ) { + var title, rerunTrigger, testBlock, assertList, + tests = id( "qunit-tests" ); + + if ( !tests ) { + return; + } + + title = document.createElement( "strong" ); + title.innerHTML = getNameHtml( name, moduleName ); + + rerunTrigger = document.createElement( "a" ); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl({ testId: testId }); + + testBlock = document.createElement( "li" ); + testBlock.appendChild( title ); + testBlock.appendChild( rerunTrigger ); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document.createElement( "ol" ); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild( assertList ); + + tests.appendChild( testBlock ); +} + +// HTML Reporter initialization and load +QUnit.begin(function( details ) { + var qunit = id( "qunit" ); + + // Fixture is the only one necessary to run without the #qunit element + storeFixture(); + + if ( qunit ) { + qunit.innerHTML = + "

        " + escapeText( document.title ) + "

        " + + "

        " + + "
        " + + "

        " + + "
          "; + } + + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); + appendTestsList( details.modules ); + toolbarModuleFilter(); + + if ( qunit && config.hidepassed ) { + addClass( qunit.lastChild, "hidepass" ); + } +}); + +QUnit.done(function( details ) { + var i, key, + banner = id( "qunit-banner" ), + tests = id( "qunit-tests" ), + html = [ + "Tests completed in ", + details.runtime, + " milliseconds.
          ", + "", + details.passed, + " assertions of ", + details.total, + " passed, ", + details.failed, + " failed." + ].join( "" ); + + if ( banner ) { + banner.className = details.failed ? "qunit-fail" : "qunit-pass"; + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( config.altertitle && defined.document && document.title ) { + + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = [ + ( details.failed ? "\u2716" : "\u2714" ), + document.title.replace( /^[\u2714\u2716] /i, "" ) + ].join( " " ); + } + + // clear own sessionStorage items if all tests passed + if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { + for ( i = 0; i < sessionStorage.length; i++ ) { + key = sessionStorage.key( i++ ); + if ( key.indexOf( "qunit-test-" ) === 0 ) { + sessionStorage.removeItem( key ); + } + } + } + + // scroll back to top to show results + if ( config.scrolltop && window.scrollTo ) { + window.scrollTo( 0, 0 ); + } +}); + +function getNameHtml( name, module ) { + var nameHtml = ""; + + if ( module ) { + nameHtml = "" + escapeText( module ) + ": "; + } + + nameHtml += "" + escapeText( name ) + ""; + + return nameHtml; +} + +QUnit.testStart(function( details ) { + var running, testBlock, bad; + + testBlock = id( "qunit-test-output-" + details.testId ); + if ( testBlock ) { + testBlock.className = "running"; + } else { + + // Report later registered tests + appendTest( details.name, details.testId, details.module ); + } + + running = id( "qunit-testresult" ); + if ( running ) { + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); + + running.innerHTML = ( bad ? + "Rerunning previously failed test:
          " : + "Running:
          " ) + + getNameHtml( details.name, details.module ); + } + +}); + +QUnit.log(function( details ) { + var assertList, assertLi, + message, expected, actual, + testItem = id( "qunit-test-output-" + details.testId ); + + if ( !testItem ) { + return; + } + + message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); + message = "" + message + ""; + message += "@ " + details.runtime + " ms"; + + // pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if ( !details.result && hasOwn.call( details, "expected" ) ) { + expected = escapeText( QUnit.dump.parse( details.expected ) ); + actual = escapeText( QUnit.dump.parse( details.actual ) ); + message += ""; + + if ( actual !== expected ) { + message += "" + + ""; + } else { + if ( expected.indexOf( "[object Array]" ) !== -1 || + expected.indexOf( "[object Object]" ) !== -1 ) { + message += ""; + } + } + + if ( details.source ) { + message += ""; + } + + message += "
          Expected:
          " +
          +			expected +
          +			"
          Result:
          " +
          +				actual + "
          Diff:
          " +
          +				QUnit.diff( expected, actual ) + "
          Message: " + + "Diff suppressed as the depth of object is more than current max depth (" + + QUnit.config.maxDepth + ").

          Hint: Use QUnit.dump.maxDepth to " + + " run with a higher max depth or " + + "Rerun without max depth.

          Source:
          " +
          +				escapeText( details.source ) + "
          "; + + // this occours when pushFailure is set and we have an extracted stack trace + } else if ( !details.result && details.source ) { + message += "" + + "" + + "
          Source:
          " +
          +			escapeText( details.source ) + "
          "; + } + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + assertLi = document.createElement( "li" ); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild( assertLi ); +}); + +QUnit.testDone(function( details ) { + var testTitle, time, testItem, assertList, + good, bad, testCounts, skipped, + tests = id( "qunit-tests" ); + + if ( !tests ) { + return; + } + + testItem = id( "qunit-test-output-" + details.testId ); + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + good = details.passed; + bad = details.failed; + + // store result when possible + if ( config.reorder && defined.sessionStorage ) { + if ( bad ) { + sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); + } else { + sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); + } + } + + if ( bad === 0 ) { + addClass( assertList, "qunit-collapsed" ); + } + + // testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad ? + "" + bad + ", " + "" + good + ", " : + ""; + + testTitle.innerHTML += " (" + testCounts + + details.assertions.length + ")"; + + if ( details.skipped ) { + testItem.className = "skipped"; + skipped = document.createElement( "em" ); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore( skipped, testTitle ); + } else { + addEvent( testTitle, "click", function() { + toggleClass( assertList, "qunit-collapsed" ); + }); + + testItem.className = bad ? "fail" : "pass"; + + time = document.createElement( "span" ); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore( time, assertList ); + } +}); + +if ( defined.document ) { + if ( document.readyState === "complete" ) { + QUnit.load(); + } else { + addEvent( window, "load", QUnit.load ); + } +} else { + config.pageLoaded = true; + config.autorun = true; +} + +})(); diff --git a/vendor/underscore/test/vendor/runner.js b/vendor/underscore/test/vendor/runner.js deleted file mode 100644 index 4b1d38b1b..000000000 --- a/vendor/underscore/test/vendor/runner.js +++ /dev/null @@ -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); - } -})(); diff --git a/vendor/underscore/underscore-min.js b/vendor/underscore/underscore-min.js index d22f881bc..f01025b7b 100644 --- a/vendor/underscore/underscore-min.js +++ b/vendor/underscore/underscore-min.js @@ -1,6 +1,6 @@ -// Underscore.js 1.5.2 +// Underscore.js 1.8.3 // http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. -(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?(this._wrapped=n,void 0):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.5.2";var A=j.each=j.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var E="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(E);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(E);return r},j.find=j.detect=function(n,t,r){var e;return O(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var O=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:O(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,function(n){return n[t]})},j.where=function(n,t,r){return j.isEmpty(t)?r?void 0:[]:j[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},j.findWhere=function(n,t){return j.where(n,t,!0)},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);if(!t&&j.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>e.computed&&(e={value:n,computed:a})}),e.value},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);if(!t&&j.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;ae||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={},i=null==r?j.identity:k(r);return A(t,function(r,a){var o=i.call(e,r,a,t);n(u,o,r)}),u}};j.groupBy=F(function(n,t,r){(j.has(n,t)?n[t]:n[t]=[]).push(r)}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=null==r?j.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])=0})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error("bindAll must be passed function names");return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=r.leading===!1?0:new Date,a=null,i=n.apply(e,u)};return function(){var l=new Date;o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u)):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o;return function(){i=this,u=arguments,a=new Date;var c=function(){var l=new Date-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u)))},l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u)),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=w||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var I={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};I.unescape=j.invert(I.escape);var T={escape:new RegExp("["+j.keys(I.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(I.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return I[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); +(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this); //# sourceMappingURL=underscore-min.map \ No newline at end of file diff --git a/vendor/underscore/underscore-min.map b/vendor/underscore/underscore-min.map index 4fbe0ba3c..cf356bf9a 100644 --- a/vendor/underscore/underscore-min.map +++ b/vendor/underscore/underscore-min.map @@ -1 +1 @@ -{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["root","this","previousUnderscore","_","breaker","ArrayProto","Array","prototype","ObjProto","Object","FuncProto","Function","push","slice","concat","toString","hasOwnProperty","nativeForEach","forEach","nativeMap","map","nativeReduce","reduce","nativeReduceRight","reduceRight","nativeFilter","filter","nativeEvery","every","nativeSome","some","nativeIndexOf","indexOf","nativeLastIndexOf","lastIndexOf","nativeIsArray","isArray","nativeKeys","keys","nativeBind","bind","obj","_wrapped","exports","module","VERSION","each","iterator","context","length","i","call","collect","results","value","index","list","reduceError","foldl","inject","memo","initial","arguments","TypeError","foldr","find","detect","result","any","select","reject","all","identity","contains","include","target","invoke","method","args","isFunc","isFunction","apply","pluck","key","where","attrs","first","isEmpty","findWhere","max","Math","Infinity","computed","min","shuffle","rand","shuffled","random","sample","n","guard","lookupIterator","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","has","indexBy","countBy","sortedIndex","array","low","high","mid","toArray","values","size","head","take","last","rest","tail","drop","compact","flatten","input","shallow","output","isArguments","without","difference","uniq","unique","isSorted","seen","union","intersection","item","other","zip","object","from","hasIndex","range","start","stop","step","ceil","idx","ctor","func","bound","self","partial","bindAll","funcs","Error","f","memoize","hasher","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","Date","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","once","ran","wrap","wrapper","compose","after","times","pairs","invert","functions","methods","names","extend","source","prop","pick","copy","omit","defaults","clone","isObject","tap","interceptor","eq","aStack","bStack","className","String","global","multiline","ignoreCase","aCtor","constructor","bCtor","pop","isEqual","isString","isElement","nodeType","name","isFinite","isNaN","parseFloat","isNumber","isBoolean","isNull","isUndefined","noConflict","accum","floor","entityMap","escape","&","<",">","\"","'","unescape","entityRegexes","RegExp","join","string","replace","match","property","mixin","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","\t","
","
","escaper","template","text","data","settings","render","matcher","offset","variable","e","chain","_chain"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["createReduce","dir","iterator","obj","iteratee","memo","keys","index","length","currentKey","context","optimizeCb","isArrayLike","_","arguments","createPredicateIndexFinder","array","predicate","cb","getLength","createIndexFinder","predicateFind","sortedIndex","item","idx","i","Math","max","min","slice","call","isNaN","collectNonEnumProps","nonEnumIdx","nonEnumerableProps","constructor","proto","isFunction","prototype","ObjProto","prop","has","contains","push","root","this","previousUnderscore","ArrayProto","Array","Object","FuncProto","Function","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","nativeBind","bind","nativeCreate","create","Ctor","_wrapped","exports","module","VERSION","func","argCount","value","other","collection","accumulator","apply","identity","isObject","matcher","property","Infinity","createAssigner","keysFunc","undefinedOnly","source","l","key","baseCreate","result","MAX_ARRAY_INDEX","pow","each","forEach","map","collect","results","reduce","foldl","inject","reduceRight","foldr","find","detect","findIndex","findKey","filter","select","list","reject","negate","every","all","some","any","includes","include","fromIndex","guard","values","indexOf","invoke","method","args","isFunc","pluck","where","attrs","findWhere","computed","lastComputed","shuffle","rand","set","shuffled","random","sample","n","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","indexBy","countBy","toArray","size","partition","pass","fail","first","head","take","initial","last","rest","tail","drop","compact","flatten","input","shallow","strict","startIndex","output","isArguments","j","len","without","difference","uniq","unique","isSorted","isBoolean","seen","union","intersection","argsLength","zip","unzip","object","findLastIndex","low","high","mid","floor","lastIndexOf","range","start","stop","step","ceil","executeBound","sourceFunc","boundFunc","callingContext","self","TypeError","bound","concat","partial","boundArgs","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","wrap","wrapper","compose","after","times","before","once","hasEnumBug","propertyIsEnumerable","allKeys","mapObject","pairs","invert","functions","methods","names","extend","extendOwn","assign","pick","oiteratee","omit","String","defaults","props","clone","tap","interceptor","isMatch","eq","aStack","bStack","className","areArrays","aCtor","bCtor","pop","isEqual","isEmpty","isString","isElement","nodeType","type","name","Int8Array","isFinite","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","propertyOf","matches","accum","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","match","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","fallback","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeChar","template","text","settings","oldSettings","offset","variable","render","e","data","argument","chain","instance","_chain","mixin","valueOf","toJSON","define","amd"],"mappings":"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"} \ No newline at end of file diff --git a/vendor/underscore/underscore.js b/vendor/underscore/underscore.js index b50115df5..ca1cdd332 100644 --- a/vendor/underscore/underscore.js +++ b/vendor/underscore/underscore.js @@ -1,6 +1,6 @@ -// Underscore.js 1.5.2 +// Underscore.js 1.8.3 // http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { @@ -8,41 +8,35 @@ // Baseline setup // -------------- - // Establish the root object, `window` in the browser, or `exports` on the server. - var root = this; + // Establish the root object, `window` (`self`) in the browser, `global` + // on the server, or `this` in some virtual machines. We use `self` + // instead of `window` for `WebWorker` support. + var root = typeof self == 'object' && self.self === self && self || + typeof global == 'object' && global.global === global && global || + this || + {}; // Save the previous value of the `_` variable. var previousUnderscore = root._; - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + var ArrayProto = Array.prototype, ObjProto = Object.prototype; + var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; // Create quick reference variables for speed access to core prototypes. - var - push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; + var push = ArrayProto.push, + slice = ArrayProto.slice, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. - var - nativeForEach = ArrayProto.forEach, - nativeMap = ArrayProto.map, - nativeReduce = ArrayProto.reduce, - nativeReduceRight = ArrayProto.reduceRight, - nativeFilter = ArrayProto.filter, - nativeEvery = ArrayProto.every, - nativeSome = ArrayProto.some, - nativeIndexOf = ArrayProto.indexOf, - nativeLastIndexOf = ArrayProto.lastIndexOf, - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; + var nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeCreate = Object.create; + + // Naked function reference for surrogate-prototype-swapping. + var Ctor = function(){}; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { @@ -52,11 +46,12 @@ }; // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { + // backwards-compatibility for their old module API. If we're in + // the browser, add `_` as a global object. + // (`nodeType` is checked to ensure that `module` + // and `exports` are not HTML elements.) + if (typeof exports != 'undefined' && !exports.nodeType) { + if (typeof module != 'undefined' && !module.nodeType && module.exports) { exports = module.exports = _; } exports._ = _; @@ -65,261 +60,362 @@ } // Current version. - _.VERSION = '1.5.2'; + _.VERSION = '1.8.3'; + + // Internal function that returns an efficient (for current engines) version + // of the passed-in callback, to be repeatedly applied in other Underscore + // functions. + var optimizeCb = function(func, context, argCount) { + if (context === void 0) return func; + switch (argCount) { + case 1: return function(value) { + return func.call(context, value); + }; + // The 2-parameter case has been omitted only because no current consumers + // made use of it. + case null: + case 3: return function(value, index, collection) { + return func.call(context, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(context, accumulator, value, index, collection); + }; + } + return function() { + return func.apply(context, arguments); + }; + }; + + var builtinIteratee; + + // An internal function to generate callbacks that can be applied to each + // element in a collection, returning the desired result — either `identity`, + // an arbitrary callback, a property matcher, or a property accessor. + var cb = function(value, context, argCount) { + if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); + if (value == null) return _.identity; + if (_.isFunction(value)) return optimizeCb(value, context, argCount); + if (_.isObject(value) && !_.isArray(value)) return _.matcher(value); + return _.property(value); + }; + + // External wrapper for our callback generator. Users may customize + // `_.iteratee` if they want additional predicate/iteratee shorthand styles. + // This abstraction hides the internal-only argCount argument. + _.iteratee = builtinIteratee = function(value, context) { + return cb(value, context, Infinity); + }; + + // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) + // This accumulates the arguments passed into an array, after a given index. + var restArgs = function(func, startIndex) { + startIndex = startIndex == null ? func.length - 1 : +startIndex; + return function() { + var length = Math.max(arguments.length - startIndex, 0), + rest = Array(length), + index = 0; + for (; index < length; index++) { + rest[index] = arguments[index + startIndex]; + } + switch (startIndex) { + case 0: return func.call(this, rest); + case 1: return func.call(this, arguments[0], rest); + case 2: return func.call(this, arguments[0], arguments[1], rest); + } + var args = Array(startIndex + 1); + for (index = 0; index < startIndex; index++) { + args[index] = arguments[index]; + } + args[startIndex] = rest; + return func.apply(this, args); + }; + }; + + // An internal function for creating a new object that inherits from another. + var baseCreate = function(prototype) { + if (!_.isObject(prototype)) return {}; + if (nativeCreate) return nativeCreate(prototype); + Ctor.prototype = prototype; + var result = new Ctor; + Ctor.prototype = null; + return result; + }; + + var shallowProperty = function(key) { + return function(obj) { + return obj == null ? void 0 : obj[key]; + }; + }; + + var deepGet = function(obj, path) { + var length = path.length; + for (var i = 0; i < length; i++) { + if (obj == null) return void 0; + obj = obj[path[i]]; + } + return length ? obj : void 0; + }; + + // Helper for collection methods to determine whether a collection + // should be iterated as an array or as an object. + // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength + // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 + var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; + var getLength = shallowProperty('length'); + var isArrayLike = function(collection) { + var length = getLength(collection); + return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; + }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. - // Handles objects with the built-in `forEach`, arrays, and raw objects. - // Delegates to **ECMAScript 5**'s native `forEach` if available. - var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (var i = 0, length = obj.length; i < length; i++) { - if (iterator.call(context, obj[i], i, obj) === breaker) return; + // Handles raw objects in addition to array-likes. Treats all + // sparse array-likes as if they were dense. + _.each = _.forEach = function(obj, iteratee, context) { + iteratee = optimizeCb(iteratee, context); + var i, length; + if (isArrayLike(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); - for (var i = 0, length = keys.length; i < length; i++) { - if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; + for (i = 0, length = keys.length; i < length; i++) { + iteratee(obj[keys[i]], keys[i], obj); } } + return obj; }; - // Return the results of applying the iterator to each element. - // Delegates to **ECMAScript 5**'s native `map` if available. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); - each(obj, function(value, index, list) { - results.push(iterator.call(context, value, index, list)); - }); + // Return the results of applying the iteratee to each element. + _.map = _.collect = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + results = Array(length); + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + results[index] = iteratee(obj[currentKey], currentKey, obj); + } return results; }; - var reduceError = 'Reduce of empty array with no initial value'; + // Create a reducing function iterating left or right. + var createReduce = function(dir) { + // Wrap code that reassigns argument variables in a separate function than + // the one that accesses `arguments.length` to avoid a perf hit. (#1991) + var reducer = function(obj, iteratee, memo, initial) { + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + index = dir > 0 ? 0 : length - 1; + if (!initial) { + memo = obj[keys ? keys[index] : index]; + index += dir; + } + for (; index >= 0 && index < length; index += dir) { + var currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + }; + + return function(obj, iteratee, memo, context) { + var initial = arguments.length >= 3; + return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); + }; + }; // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); - } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; + // or `foldl`. + _.reduce = _.foldl = _.inject = createReduce(1); // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); - } - var length = obj.length; - if (length !== +length) { - var keys = _.keys(obj); - length = keys.length; - } - each(obj, function(value, index, list) { - index = keys ? keys[--length] : --length; - if (!initial) { - memo = obj[index]; - initial = true; - } else { - memo = iterator.call(context, memo, obj[index], index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; + _.reduceRight = _.foldr = createReduce(-1); // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; + _.find = _.detect = function(obj, predicate, context) { + var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; + var key = keyFinder(obj, predicate, context); + if (key !== void 0 && key !== -1) return obj[key]; }; // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { + _.filter = _.select = function(obj, predicate, context) { var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results.push(value); + predicate = cb(predicate, context); + _.each(obj, function(value, index, list) { + if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - return _.filter(obj, function(value, index, list) { - return !iterator.call(context, value, index, list); - }, context); + _.reject = function(obj, predicate, context) { + return _.filter(obj, _.negate(cb(predicate)), context); }; // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return !!result; + _.every = _.all = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (!predicate(obj[currentKey], currentKey, obj)) return false; + } + return true; }; // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; + _.some = _.any = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (predicate(obj[currentKey], currentKey, obj)) return true; + } + return false; }; - // Determine if the array or object contains a given value (using `===`). - // Aliased as `include`. - _.contains = _.include = function(obj, target) { - if (obj == null) return false; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - return any(obj, function(value) { - return value === target; - }); + // Determine if the array or object contains a given item (using `===`). + // Aliased as `includes` and `include`. + _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { + if (!isArrayLike(obj)) obj = _.values(obj); + if (typeof fromIndex != 'number' || guard) fromIndex = 0; + return _.indexOf(obj, item, fromIndex) >= 0; }; // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - var isFunc = _.isFunction(method); - return _.map(obj, function(value) { - return (isFunc ? method : value[method]).apply(value, args); + _.invoke = restArgs(function(obj, path, args) { + var contextPath, func; + if (_.isFunction(path)) { + func = path; + } else if (_.isArray(path)) { + contextPath = path.slice(0, -1); + path = path[path.length - 1]; + } + return _.map(obj, function(context) { + var method = func; + if (!method) { + if (contextPath && contextPath.length) { + context = deepGet(context, contextPath); + } + if (context == null) return void 0; + method = context[path]; + } + return method == null ? method : method.apply(context, args); }); - }; + }); // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); + return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. - _.where = function(obj, attrs, first) { - if (_.isEmpty(attrs)) return first ? void 0 : []; - return _[first ? 'find' : 'filter'](obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); + _.where = function(obj, attrs) { + return _.filter(obj, _.matcher(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { - return _.where(obj, attrs, true); + return _.find(obj, _.matcher(attrs)); }; - // Return the maximum element or (element-based computation). - // Can't optimize arrays of integers longer than 65,535 elements. - // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.max.apply(Math, obj); + // Return the maximum element (or element-based computation). + _.max = function(obj, iteratee, context) { + var result = -Infinity, lastComputed = -Infinity, + value, computed; + if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value != null && value > result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(v, index, list) { + computed = iteratee(v, index, list); + if (computed > lastComputed || computed === -Infinity && result === -Infinity) { + result = v; + lastComputed = computed; + } + }); } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity, value: -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed > result.computed && (result = {value : value, computed : computed}); - }); - return result.value; + return result; }; // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.min.apply(Math, obj); + _.min = function(obj, iteratee, context) { + var result = Infinity, lastComputed = Infinity, + value, computed; + if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value != null && value < result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(v, index, list) { + computed = iteratee(v, index, list); + if (computed < lastComputed || computed === Infinity && result === Infinity) { + result = v; + lastComputed = computed; + } + }); } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity, value: Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; + return result; }; - // Shuffle an array, using the modern version of the - // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + // Shuffle a collection. _.shuffle = function(obj) { - var rand; - var index = 0; - var shuffled = []; - each(obj, function(value) { - rand = _.random(index++); - shuffled[index - 1] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; + return _.sample(obj, Infinity); }; - // Sample **n** random values from an array. - // If **n** is not specified, returns a single random element from the array. + // Sample **n** random values from a collection using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { - if (arguments.length < 2 || guard) { + if (n == null || guard) { + if (!isArrayLike(obj)) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } - return _.shuffle(obj).slice(0, Math.max(0, n)); + var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj); + var length = getLength(sample); + n = Math.max(Math.min(n, length), 0); + var last = length - 1; + for (var index = 0; index < n; index++) { + var rand = _.random(index, last); + var temp = sample[index]; + sample[index] = sample[rand]; + sample[rand] = temp; + } + return sample.slice(0, n); }; - // An internal function to generate lookup iterators. - var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); - return _.pluck(_.map(obj, function(value, index, list) { + // Sort the object's values by a criterion produced by an iteratee. + _.sortBy = function(obj, iteratee, context) { + var index = 0; + iteratee = cb(iteratee, context); + return _.pluck(_.map(obj, function(value, key, list) { return { value: value, - index: index, - criteria: iterator.call(context, value, index, list) + index: index++, + criteria: iteratee(value, key, list) }; }).sort(function(left, right) { var a = left.criteria; @@ -333,13 +429,13 @@ }; // An internal function used for aggregate "group by" operations. - var group = function(behavior) { - return function(obj, value, context) { - var result = {}; - var iterator = value == null ? _.identity : lookupIterator(value); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); + var group = function(behavior, partition) { + return function(obj, iteratee, context) { + var result = partition ? [[], []] : {}; + iteratee = cb(iteratee, context); + _.each(obj, function(value, index) { + var key = iteratee(value, index, obj); + behavior(result, value, key); }); return result; }; @@ -347,50 +443,48 @@ // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. - _.groupBy = group(function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + _.groupBy = group(function(result, value, key) { + if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. - _.indexBy = group(function(result, key, value) { + _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. - _.countBy = group(function(result, key) { - _.has(result, key) ? result[key]++ : result[key] = 1; + _.countBy = group(function(result, value, key) { + if (_.has(result, key)) result[key]++; else result[key] = 1; }); - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); - var value = iterator.call(context, obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; - } - return low; - }; - + var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); - if (obj.length === +obj.length) return _.map(obj, _.identity); + if (_.isString(obj)) { + // Keep surrogate pair characters together + return obj.match(reStrSymbol); + } + if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; - return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + return isArrayLike(obj) ? obj.length : _.keys(obj).length; }; + // Split a collection into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = group(function(result, value, pass) { + result[pass ? 0 : 1].push(value); + }, true); + // Array Functions // --------------- @@ -398,130 +492,154 @@ // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { - if (array == null) return void 0; - return (n == null) || guard ? array[0] : slice.call(array, 0, n); + if (array == null || array.length < 1) return void 0; + if (n == null || guard) return array[0]; + return _.initial(array, array.length - n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. + // the array, excluding the last N. _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. + // values in the array. _.last = function(array, n, guard) { - if (array == null) return void 0; - if ((n == null) || guard) { - return array[array.length - 1]; - } else { - return slice.call(array, Math.max(array.length - n, 0)); - } + if (array == null || array.length < 1) return void 0; + if (n == null || guard) return array[array.length - 1]; + return _.rest(array, Math.max(0, array.length - n)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. The **guard** - // check allows it to work with `_.map`. + // the rest N values in the array. _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, (n == null) || guard ? 1 : n); + return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { - return _.filter(array, _.identity); + return _.filter(array, Boolean); }; // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, output) { - if (shallow && _.every(input, _.isArray)) { - return concat.apply(output, input); - } - each(input, function(value) { - if (_.isArray(value) || _.isArguments(value)) { - shallow ? push.apply(output, value) : flatten(value, shallow, output); - } else { - output.push(value); + var flatten = function(input, shallow, strict, output) { + output = output || []; + var idx = output.length; + for (var i = 0, length = getLength(input); i < length; i++) { + var value = input[i]; + if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { + // Flatten current level of array or arguments object. + if (shallow) { + var j = 0, len = value.length; + while (j < len) output[idx++] = value[j++]; + } else { + flatten(value, shallow, strict, output); + idx = output.length; + } + } else if (!strict) { + output[idx++] = value; } - }); + } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { - return flatten(array, shallow, []); + return flatten(array, shallow, false); }; // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; + _.without = restArgs(function(array, otherArrays) { + return _.difference(array, otherArrays); + }); // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator, context) { - if (_.isFunction(isSorted)) { - context = iterator; - iterator = isSorted; + _.uniq = _.unique = function(array, isSorted, iteratee, context) { + if (!_.isBoolean(isSorted)) { + context = iteratee; + iteratee = isSorted; isSorted = false; } - var initial = iterator ? _.map(array, iterator, context) : array; - var results = []; + if (iteratee != null) iteratee = cb(iteratee, context); + var result = []; var seen = []; - each(initial, function(value, index) { - if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { - seen.push(value); - results.push(array[index]); + for (var i = 0, length = getLength(array); i < length; i++) { + var value = array[i], + computed = iteratee ? iteratee(value, i, array) : value; + if (isSorted) { + if (!i || seen !== computed) result.push(value); + seen = computed; + } else if (iteratee) { + if (!_.contains(seen, computed)) { + seen.push(computed); + result.push(value); + } + } else if (!_.contains(result, value)) { + result.push(value); } - }); - return results; + } + return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. - _.union = function() { - return _.uniq(_.flatten(arguments, true)); - }; + _.union = restArgs(function(arrays) { + return _.uniq(flatten(arrays, true, true)); + }); // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); + var result = []; + var argsLength = arguments.length; + for (var i = 0, length = getLength(array); i < length; i++) { + var item = array[i]; + if (_.contains(result, item)) continue; + var j; + for (j = 1; j < argsLength; j++) { + if (!_.contains(arguments[j], item)) break; + } + if (j === argsLength) result.push(item); + } + return result; }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); - return _.filter(array, function(value){ return !_.contains(rest, value); }); + _.difference = restArgs(function(array, rest) { + rest = flatten(rest, true, true); + return _.filter(array, function(value){ + return !_.contains(rest, value); + }); + }); + + // Complement of _.zip. Unzip accepts an array of arrays and groups + // each array's elements on shared indices. + _.unzip = function(array) { + var length = array && _.max(array, getLength).length || 0; + var result = Array(length); + + for (var index = 0; index < length; index++) { + result[index] = _.pluck(array, index); + } + return result; }; // Zip together multiple lists into a single array -- elements that share // an index go together. - _.zip = function() { - var length = _.max(_.pluck(arguments, "length").concat(0)); - var results = new Array(length); - for (var i = 0; i < length; i++) { - results[i] = _.pluck(arguments, '' + i); - } - return results; - }; + _.zip = restArgs(_.unzip); // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. + // the corresponding values. Passing by pairs is the reverse of _.pairs. _.object = function(list, values) { - if (list == null) return {}; var result = {}; - for (var i = 0, length = list.length; i < length; i++) { + for (var i = 0, length = getLength(list); i < length; i++) { if (values) { result[list[i]] = values[i]; } else { @@ -531,127 +649,183 @@ return result; }; - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i = 0, length = array.length; - if (isSorted) { - if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); - } else { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; + // Generator function to create the findIndex and findLastIndex functions. + var createPredicateIndexFinder = function(dir) { + return function(array, predicate, context) { + predicate = cb(predicate, context); + var length = getLength(array); + var index = dir > 0 ? 0 : length - 1; + for (; index >= 0 && index < length; index += dir) { + if (predicate(array[index], index, array)) return index; } - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < length; i++) if (array[i] === item) return i; - return -1; + return -1; + }; }; - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item, from) { - if (array == null) return -1; - var hasIndex = from != null; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { - return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + // Returns the first index on an array-like that passes a predicate test. + _.findIndex = createPredicateIndexFinder(1); + _.findLastIndex = createPredicateIndexFinder(-1); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iteratee, context) { + iteratee = cb(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = getLength(array); + while (low < high) { + var mid = Math.floor((low + high) / 2); + if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } - var i = (hasIndex ? from : array.length); - while (i--) if (array[i] === item) return i; - return -1; + return low; }; + // Generator function to create the indexOf and lastIndexOf functions. + var createIndexFinder = function(dir, predicateFind, sortedIndex) { + return function(array, item, idx) { + var i = 0, length = getLength(array); + if (typeof idx == 'number') { + if (dir > 0) { + i = idx >= 0 ? idx : Math.max(idx + length, i); + } else { + length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; + } + } else if (sortedIndex && idx && length) { + idx = sortedIndex(array, item); + return array[idx] === item ? idx : -1; + } + if (item !== item) { + idx = predicateFind(slice.call(array, i, length), _.isNaN); + return idx >= 0 ? idx + i : -1; + } + for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { + if (array[idx] === item) return idx; + } + return -1; + }; + }; + + // Return the position of the first occurrence of an item in an array, + // or -1 if the item is not included in the array. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); + _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); + // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { - if (arguments.length <= 1) { + if (stop == null) { stop = start || 0; start = 0; } - step = arguments[2] || 1; + if (!step) { + step = stop < start ? -1 : 1; + } var length = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(length); + var range = Array(length); - while(idx < length) { - range[idx++] = start; - start += step; + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; } return range; }; + // Split an **array** into several arrays containing **count** or less elements + // of initial array. + _.chunk = function(array, count) { + if (count == null || count < 1) return []; + + var result = []; + var i = 0, length = array.length; + while (i < length) { + result.push(slice.call(array, i, i += count)); + } + return result; + }; + // Function (ahem) Functions // ------------------ - // Reusable constructor function for prototype setting. - var ctor = function(){}; + // Determines whether to execute a function as a constructor + // or a normal function with the provided arguments. + var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { + if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); + var self = baseCreate(sourceFunc.prototype); + var result = sourceFunc.apply(self, args); + if (_.isObject(result)) return result; + return self; + }; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. - _.bind = function(func, context) { - var args, bound; - if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - ctor.prototype = null; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - }; + _.bind = restArgs(function(func, context, args) { + if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); + var bound = restArgs(function(callArgs) { + return executeBound(func, bound, context, this, args.concat(callArgs)); + }); + return bound; + }); // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. - _.partial = function(func) { - var args = slice.call(arguments, 1); - return function() { - return func.apply(this, args.concat(slice.call(arguments))); + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder by default, allowing any combination of arguments to be + // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. + _.partial = restArgs(function(func, boundArgs) { + var placeholder = _.partial.placeholder; + var bound = function() { + var position = 0, length = boundArgs.length; + var args = Array(length); + for (var i = 0; i < length; i++) { + args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i]; + } + while (position < arguments.length) args.push(arguments[position++]); + return executeBound(func, bound, this, this, args); }; - }; + return bound; + }); - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length === 0) throw new Error("bindAll must be passed function names"); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; + _.partial.placeholder = _; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = restArgs(function(obj, keys) { + keys = flatten(keys, false, false); + var index = keys.length; + if (index < 1) throw new Error('bindAll must be passed function names'); + while (index--) { + var key = keys[index]; + obj[key] = _.bind(obj[key], obj); + } + }); // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + var memoize = function(key) { + var cache = memoize.cache; + var address = '' + (hasher ? hasher.apply(this, arguments) : key); + if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); + return cache[address]; }; + memoize.cache = {}; + return memoize; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(null, args); }, wait); - }; + _.delay = restArgs(function(func, wait, args) { + return setTimeout(function() { + return func.apply(null, args); + }, wait); + }); // Defers a function, scheduling it to run after the current call stack has // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; + _.defer = _.partial(_.delay, _, 1); // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run @@ -659,31 +833,44 @@ // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { - var context, args, result; - var timeout = null; + var timeout, context, args, result; var previous = 0; - options || (options = {}); + if (!options) options = {}; + var later = function() { - previous = options.leading === false ? 0 : new Date; + previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); + if (!timeout) context = args = null; }; - return function() { - var now = new Date; + + var throttled = function() { + var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } previous = now; result = func.apply(context, args); + if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; + + throttled.cancel = function() { + clearTimeout(timeout); + previous = 0; + timeout = context = args = null; + }; + + return throttled; }; // Returns a function, that, as long as it continues to be invoked, will not @@ -691,67 +878,62 @@ // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { - var timeout, args, context, timestamp, result; - return function() { - context = this; - args = arguments; - timestamp = new Date(); - var later = function() { - var last = (new Date()) - timestamp; - if (last < wait) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - if (!immediate) result = func.apply(context, args); - } - }; - var callNow = immediate && !timeout; - if (!timeout) { - timeout = setTimeout(later, wait); - } - if (callNow) result = func.apply(context, args); - return result; - }; - }; + var timeout, result; - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - memo = func.apply(this, arguments); - func = null; - return memo; + var later = function(context, args) { + timeout = null; + if (args) result = func.apply(context, args); }; + + var debounced = restArgs(function(args) { + if (timeout) clearTimeout(timeout); + if (immediate) { + var callNow = !timeout; + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(this, args); + } else { + timeout = _.delay(later, wait, this, args); + } + + return result; + }); + + debounced.cancel = function() { + clearTimeout(timeout); + timeout = null; + }; + + return debounced; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a negated version of the passed-in predicate. + _.negate = function(predicate) { return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); + return !predicate.apply(this, arguments); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { - var funcs = arguments; + var args = arguments; + var start = args.length - 1; return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; + var i = start; + var result = args[start].apply(this, arguments); + while (i--) result = args[i].call(this, result); + return result; }; }; - // Returns a function that will only be executed after being called N times. + // Returns a function that will only be executed on and after the Nth call. _.after = function(times, func) { return function() { if (--times < 1) { @@ -760,15 +942,68 @@ }; }; + // Returns a function that will only be executed up to (but not including) the Nth call. + _.before = function(times, func) { + var memo; + return function() { + if (--times > 0) { + memo = func.apply(this, arguments); + } + if (times <= 1) func = null; + return memo; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = _.partial(_.before, 2); + + _.restArgs = restArgs; + // Object Functions // ---------------- - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); + // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. + var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); + var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', + 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; + + var collectNonEnumProps = function(obj, keys) { + var nonEnumIdx = nonEnumerableProps.length; + var constructor = obj.constructor; + var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; + + // Constructor is a special case. + var prop = 'constructor'; + if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); + + while (nonEnumIdx--) { + prop = nonEnumerableProps[nonEnumIdx]; + if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { + keys.push(prop); + } + } + }; + + // Retrieve the names of an object's own properties. + // Delegates to **ECMAScript 5**'s native `Object.keys`. + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); + return keys; + }; + + // Retrieve all the property names of an object. + _.allKeys = function(obj) { + if (!_.isObject(obj)) return []; + var keys = []; + for (var key in obj) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); return keys; }; @@ -776,18 +1011,33 @@ _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; - var values = new Array(length); + var values = Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; + // Returns the results of applying the iteratee to each element of the object. + // In contrast to _.map it returns an object. + _.mapObject = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = _.keys(obj), + length = keys.length, + results = {}; + for (var index = 0; index < length; index++) { + var currentKey = keys[index]; + results[currentKey] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + // Convert an object into a list of `[key, value]` pairs. + // The opposite of _.object. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; - var pairs = new Array(length); + var pairs = Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } @@ -805,7 +1055,7 @@ }; // Return a sorted list of the function names available on the object. - // Aliased as `methods` + // Aliased as `methods`. _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { @@ -814,48 +1064,92 @@ return names.sort(); }; - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - obj[prop] = source[prop]; + // An internal function for creating assigner functions. + var createAssigner = function(keysFunc, defaults) { + return function(obj) { + var length = arguments.length; + if (defaults) obj = Object(obj); + if (length < 2 || obj == null) return obj; + for (var index = 1; index < length; index++) { + var source = arguments[index], + keys = keysFunc(source), + l = keys.length; + for (var i = 0; i < l; i++) { + var key = keys[i]; + if (!defaults || obj[key] === void 0) obj[key] = source[key]; } } - }); - return obj; + return obj; + }; + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = createAssigner(_.allKeys); + + // Assigns a given object with all the own properties in the passed-in object(s). + // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) + _.extendOwn = _.assign = createAssigner(_.keys); + + // Returns the first key on an object that passes a predicate test. + _.findKey = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = _.keys(obj), key; + for (var i = 0, length = keys.length; i < length; i++) { + key = keys[i]; + if (predicate(obj[key], key, obj)) return key; + } + }; + + // Internal pick helper function to determine if `obj` has key `key`. + var keyInObj = function(value, key, obj) { + return key in obj; }; // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - each(keys, function(key) { - if (key in obj) copy[key] = obj[key]; - }); - return copy; - }; - - // Return a copy of the object without the blacklisted properties. - _.omit = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - for (var key in obj) { - if (!_.contains(keys, key)) copy[key] = obj[key]; + _.pick = restArgs(function(obj, keys) { + var result = {}, iteratee = keys[0]; + if (obj == null) return result; + if (_.isFunction(iteratee)) { + if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]); + keys = _.allKeys(obj); + } else { + iteratee = keyInObj; + keys = flatten(keys, false, false); + obj = Object(obj); } - return copy; - }; + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i]; + var value = obj[key]; + if (iteratee(value, key, obj)) result[key] = value; + } + return result; + }); + + // Return a copy of the object without the blacklisted properties. + _.omit = restArgs(function(obj, keys) { + var iteratee = keys[0], context; + if (_.isFunction(iteratee)) { + iteratee = _.negate(iteratee); + if (keys.length > 1) context = keys[1]; + } else { + keys = _.map(flatten(keys, false, false), String); + iteratee = function(value, key) { + return !_.contains(keys, key); + }; + } + return _.pick(obj, iteratee, context); + }); // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - if (obj[prop] === void 0) obj[prop] = source[prop]; - } - } - }); - return obj; + _.defaults = createAssigner(_.allKeys, true); + + // Creates an object that inherits from the given prototype object. + // If additional properties are provided then they will be added to the + // created object. + _.create = function(prototype, props) { + var result = baseCreate(prototype); + if (props) _.extendOwn(result, props); + return result; }; // Create a (shallow-cloned) duplicate of an object. @@ -872,109 +1166,136 @@ return obj; }; + // Returns whether an object has a given set of `key:value` pairs. + _.isMatch = function(object, attrs) { + var keys = _.keys(attrs), length = keys.length; + if (object == null) return !length; + var obj = Object(object); + for (var i = 0; i < length; i++) { + var key = keys[i]; + if (attrs[key] !== obj[key] || !(key in obj)) return false; + } + return true; + }; + + // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { + var eq, deepEq; + eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; + if (a === b) return a !== 0 || 1 / a === 1 / b; + // `null` or `undefined` only equal to itself (strict comparison). + if (a == null || b == null) return false; + // `NaN`s are equivalent, but non-reflexive. + if (a !== a) return b !== b; + // Exhaust primitive checks + var type = typeof a; + if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; + return deepEq(a, b, aStack, bStack); + }; + + // Internal recursive comparison function for `isEqual`. + deepEq = function(a, b, aStack, bStack) { // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); - if (className != toString.call(b)) return false; + if (className !== toString.call(b)) return false; switch (className) { - // Strings, numbers, dates, and booleans are compared by value. + // Strings, numbers, regular expressions, dates, and booleans are compared by value. + case '[object RegExp]': + // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. - return a == String(b); + return '' + a === '' + b; case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + // `NaN`s are equivalent, but non-reflexive. + // Object(NaN) is equivalent to NaN. + if (+a !== +a) return +b !== +b; + // An `egal` comparison is performed for other numeric values. + return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; + return +a === +b; + case '[object Symbol]': + return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b); + } + + var areArrays = className === '[object Array]'; + if (!areArrays) { + if (typeof a != 'object' || typeof b != 'object') return false; + + // Objects with different constructors are not equivalent, but `Object`s or `Array`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && + _.isFunction(bCtor) && bCtor instanceof bCtor) + && ('constructor' in a && 'constructor' in b)) { + return false; + } } - if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + + // Initializing stack of traversed objects. + // It's done here since we only need them for objects and arrays comparison. + aStack = aStack || []; + bStack = bStack || []; var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. - if (aStack[length] == a) return bStack[length] == b; - } - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; + if (aStack[length] === a) return bStack[length] === b; } + // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); - var size = 0, result = true; + // Recursively compare objects and arrays. - if (className == '[object Array]') { + if (areArrays) { // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack))) break; - } + length = a.length; + if (length !== b.length) return false; + // Deep compare the contents, ignoring non-numeric properties. + while (length--) { + if (!eq(a[length], b[length], aStack, bStack)) return false; } } else { // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; + var keys = _.keys(a), key; + length = keys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (_.keys(b).length !== length) return false; + while (length--) { + // Deep compare each member + key = keys[length]; + if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); - return result; + return true; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { - return eq(a, b, [], []); + return eq(a, b); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; + if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; + return _.keys(obj).length === 0; }; // Is a given value a DOM element? @@ -985,49 +1306,52 @@ // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; + return toString.call(obj) === '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { - return obj === Object(obj); + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; }; - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. - each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) { _['is' + name] = function(obj) { - return toString.call(obj) == '[object ' + name + ']'; + return toString.call(obj) === '[object ' + name + ']'; }; }); - // Define a fallback version of the method in browsers (ahem, IE), where + // Define a fallback version of the method in browsers (ahem, IE < 9), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); + return _.has(obj, 'callee'); }; } - // Optimize `isFunction` if appropriate. - if (typeof (/./) !== 'function') { + // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, + // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236). + var nodelist = root.document && root.document.childNodes; + if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') { _.isFunction = function(obj) { - return typeof obj === 'function'; + return typeof obj == 'function' || false; }; } // Is a given object a finite number? _.isFinite = function(obj) { - return isFinite(obj) && !isNaN(parseFloat(obj)); + return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj)); }; - // Is the given value `NaN`? (NaN is the only number which does not equal itself). + // Is the given value `NaN`? _.isNaN = function(obj) { - return _.isNumber(obj) && obj != +obj; + return _.isNumber(obj) && isNaN(obj); }; // Is a given value a boolean? _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; // Is a given value equal to null? @@ -1042,8 +1366,19 @@ // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); + _.has = function(obj, path) { + if (!_.isArray(path)) { + return obj != null && hasOwnProperty.call(obj, path); + } + var length = path.length; + for (var i = 0; i < length; i++) { + var key = path[i]; + if (obj == null || !hasOwnProperty.call(obj, key)) { + return false; + } + obj = obj[key]; + } + return !!length; }; // Utility Functions @@ -1056,15 +1391,53 @@ return this; }; - // Keep the identity function around for default iterators. + // Keep the identity function around for default iteratees. _.identity = function(value) { return value; }; + // Predicate-generating functions. Often useful outside of Underscore. + _.constant = function(value) { + return function() { + return value; + }; + }; + + _.noop = function(){}; + + _.property = function(path) { + if (!_.isArray(path)) { + return shallowProperty(path); + } + return function(obj) { + return deepGet(obj, path); + }; + }; + + // Generates a function for a given object that returns a given property. + _.propertyOf = function(obj) { + if (obj == null) { + return function(){}; + } + return function(path) { + return !_.isArray(path) ? obj[path] : deepGet(obj, path); + }; + }; + + // Returns a predicate for checking whether an object has a given set of + // `key:value` pairs. + _.matcher = _.matches = function(attrs) { + attrs = _.extendOwn({}, attrs); + return function(obj) { + return _.isMatch(obj, attrs); + }; + }; + // Run a function **n** times. - _.times = function(n, iterator, context) { + _.times = function(n, iteratee, context) { var accum = Array(Math.max(0, n)); - for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + iteratee = optimizeCb(iteratee, context, 1); + for (var i = 0; i < n; i++) accum[i] = iteratee(i); return accum; }; @@ -1077,52 +1450,57 @@ return min + Math.floor(Math.random() * (max - min + 1)); }; - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - } + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { + return new Date().getTime(); }; - entityMap.unescape = _.invert(entityMap.escape); - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), - unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' }; + var unescapeMap = _.invert(escapeMap); // Functions for escaping and unescaping strings to/from HTML interpolation. - _.each(['escape', 'unescape'], function(method) { - _[method] = function(string) { - if (string == null) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped. + var source = '(?:' + _.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; }; - }); - - // If the value of the named `property` is a function then invoke it with the - // `object` as context; otherwise, return it. - _.result = function(object, property) { - if (object == null) return void 0; - var value = object[property]; - return _.isFunction(value) ? value.call(object) : value; }; + _.escape = createEscaper(escapeMap); + _.unescape = createEscaper(unescapeMap); - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - each(_.functions(obj), function(name) { - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return result.call(this, func.apply(_, args)); - }; - }); + // Traverses the children of `obj` along `path`. If a child is a function, it + // is invoked with its parent as context. Returns the value of the final + // child, or `fallback` if any child is undefined. + _.result = function(obj, path, fallback) { + if (!_.isArray(path)) path = [path]; + var length = path.length; + if (!length) { + return _.isFunction(fallback) ? fallback.call(obj) : fallback; + } + for (var i = 0; i < length; i++) { + var prop = obj == null ? void 0 : obj[path[i]]; + if (prop === void 0) { + prop = fallback; + i = length; // Ensure we don't continue iterating. + } + obj = _.isFunction(prop) ? prop.call(obj) : prop; + } + return obj; }; // Generate a unique integer id (unique within the entire client session). @@ -1136,9 +1514,9 @@ // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g + evaluate: /<%([\s\S]+?)%>/g, + interpolate: /<%=([\s\S]+?)%>/g, + escape: /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an @@ -1149,26 +1527,30 @@ // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g; + + var escapeChar = function(match) { + return '\\' + escapes[match]; + }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; + // NB: `oldSettings` only exists for backwards compatibility. + _.template = function(text, settings, oldSettings) { + if (!settings && oldSettings) settings = oldSettings; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ + var matcher = RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source @@ -1178,19 +1560,18 @@ var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); + source += text.slice(index, offset).replace(escapeRegExp, escapeChar); + index = offset + match.length; if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { + } else if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { + } else if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } - index = offset + match.length; + + // Adobe VMs need the match returned to produce the correct offset. return match; }); source += "';\n"; @@ -1200,8 +1581,9 @@ source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; + source + 'return __p;\n'; + var render; try { render = new Function(settings.variable || 'obj', '_', source); } catch (e) { @@ -1209,20 +1591,22 @@ throw e; } - if (data) return render(data, _); var template = function(data) { return render.call(this, data, _); }; - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + // Provide the compiled source as a convenience for precompilation. + var argument = settings.variable || 'obj'; + template.source = 'function(' + argument + '){\n' + source + '}'; return template; }; - // Add a "chain" function, which will delegate to the wrapper. + // Add a "chain" function. Start chaining a wrapped Underscore object. _.chain = function(obj) { - return _(obj).chain(); + var instance = _(obj); + instance._chain = true; + return instance; }; // OOP @@ -1232,45 +1616,68 @@ // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. - var result = function(obj) { - return this._chain ? _(obj).chain() : obj; + var chainResult = function(instance, obj) { + return instance._chain ? _(obj).chain() : obj; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + _.each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return chainResult(this, func.apply(_, args)); + }; + }); + return _; }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); - if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; - return result.call(this, obj); + if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; + return chainResult(this, obj); }; }); // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { + _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { - return result.call(this, method.apply(this._wrapped, arguments)); + return chainResult(this, method.apply(this._wrapped, arguments)); }; }); - _.extend(_.prototype, { + // Extracts the result from a wrapped and chained object. + _.prototype.value = function() { + return this._wrapped; + }; - // Start chaining a wrapped Underscore object. - chain: function() { - this._chain = true; - return this; - }, + // Provide unwrapping proxy for some methods used in engine operations + // such as arithmetic and JSON stringification. + _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; - // Extracts the result from a wrapped and chained object. - value: function() { - return this._wrapped; - } + _.prototype.toString = function() { + return String(this._wrapped); + }; - }); - -}).call(this); + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define == 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}());