" ).appendTo( tooltip ),
+ id = tooltip.uniqueId().attr( "id" );
+
+ this._addClass( content, "ui-tooltip-content" );
+ this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
+
+ tooltip.appendTo( this._appendTo( element ) );
+
+ return this.tooltips[ id ] = {
+ element: element,
+ tooltip: tooltip
+ };
+ },
+
+ _find: function( target ) {
+ var id = target.data( "ui-tooltip-id" );
+ return id ? this.tooltips[ id ] : null;
+ },
+
+ _removeTooltip: function( tooltip ) {
+ tooltip.remove();
+ delete this.tooltips[ tooltip.attr( "id" ) ];
+ },
+
+ _appendTo: function( target ) {
+ var element = target.closest( ".ui-front, dialog" );
+
+ if ( !element.length ) {
+ element = this.document[ 0 ].body;
+ }
+
+ return element;
+ },
+
+ _destroy: function() {
+ var that = this;
+
+ // Close open tooltips
+ $.each( this.tooltips, function( id, tooltipData ) {
+
+ // Delegate to close method to handle common cleanup
+ var event = $.Event( "blur" ),
+ element = tooltipData.element;
+ event.target = event.currentTarget = element[ 0 ];
+ that.close( event, true );
+
+ // Remove immediately; destroying an open tooltip doesn't use the
+ // hide animation
+ $( "#" + id ).remove();
+
+ // Restore the title
+ if ( element.data( "ui-tooltip-title" ) ) {
+
+ // If the title attribute has changed since open(), don't restore
+ if ( !element.attr( "title" ) ) {
+ element.attr( "title", element.data( "ui-tooltip-title" ) );
+ }
+ element.removeData( "ui-tooltip-title" );
+ }
+ } );
+ this.liveRegion.remove();
+ }
+} );
+
+// DEPRECATED
+// TODO: Switch return back to widget declaration at top of file when this is removed
+if ( $.uiBackCompat !== false ) {
+
+ // Backcompat for tooltipClass option
+ $.widget( "ui.tooltip", $.ui.tooltip, {
+ options: {
+ tooltipClass: null
+ },
+ _tooltip: function() {
+ var tooltipData = this._superApply( arguments );
+ if ( this.options.tooltipClass ) {
+ tooltipData.tooltip.addClass( this.options.tooltipClass );
+ }
+ return tooltipData;
+ }
+ } );
+}
+
+var widgetsTooltip = $.ui.tooltip;
+
+
+
+
+}));
+/**
+ * angular-ui-sortable - This directive allows you to jQueryUI Sortable.
+ * @version v0.13.4 - 2015-06-07
+ * @link http://angular-ui.github.com
+ * @license MIT
+ */
+
+(function(window, angular, undefined) {
+'use strict';
+/*
+ jQuery UI Sortable plugin wrapper
+
+ @param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
+ */
+angular.module('ui.sortable', [])
+ .value('uiSortableConfig',{})
+ .directive('uiSortable', [
+ 'uiSortableConfig', '$timeout', '$log',
+ function(uiSortableConfig, $timeout, $log) {
+ return {
+ require: '?ngModel',
+ scope: {
+ ngModel: '=',
+ uiSortable: '='
+ },
+ link: function(scope, element, attrs, ngModel) {
+ var savedNodes;
+
+ function combineCallbacks(first,second){
+ if(second && (typeof second === 'function')) {
+ return function() {
+ first.apply(this, arguments);
+ second.apply(this, arguments);
+ };
+ }
+ return first;
+ }
+
+ function getSortableWidgetInstance(element) {
+ // this is a fix to support jquery-ui prior to v1.11.x
+ // otherwise we should be using `element.sortable('instance')`
+ var data = element.data('ui-sortable');
+ if (data && typeof data === 'object' && data.widgetFullName === 'ui-sortable') {
+ return data;
+ }
+ return null;
+ }
+
+ function hasSortingHelper (element, ui) {
+ var helperOption = element.sortable('option','helper');
+ return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed());
+ }
+
+ // thanks jquery-ui
+ function isFloating (item) {
+ return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
+ }
+
+ function getElementScope(elementScopes, element) {
+ var result = null;
+ for (var i = 0; i < elementScopes.length; i++) {
+ var x = elementScopes[i];
+ if (x.element[0] === element[0]) {
+ result = x.scope;
+ break;
+ }
+ }
+ return result;
+ }
+
+ function afterStop(e, ui) {
+ ui.item.sortable._destroy();
+ }
+
+ var opts = {};
+
+ // directive specific options
+ var directiveOpts = {
+ 'ui-floating': undefined
+ };
+
+ var callbacks = {
+ receive: null,
+ remove:null,
+ start:null,
+ stop:null,
+ update:null
+ };
+
+ var wrappers = {
+ helper: null
+ };
+
+ angular.extend(opts, directiveOpts, uiSortableConfig, scope.uiSortable);
+
+ if (!angular.element.fn || !angular.element.fn.jquery) {
+ $log.error('ui.sortable: jQuery should be included before AngularJS!');
+ return;
+ }
+
+ if (ngModel) {
+
+ // When we add or remove elements, we need the sortable to 'refresh'
+ // so it can find the new/removed elements.
+ scope.$watch('ngModel.length', function() {
+ // Timeout to let ng-repeat modify the DOM
+ $timeout(function() {
+ // ensure that the jquery-ui-sortable widget instance
+ // is still bound to the directive's element
+ if (!!getSortableWidgetInstance(element)) {
+ element.sortable('refresh');
+ }
+ }, 0, false);
+ });
+
+ callbacks.start = function(e, ui) {
+ if (opts['ui-floating'] === 'auto') {
+ // since the drag has started, the element will be
+ // absolutely positioned, so we check its siblings
+ var siblings = ui.item.siblings();
+ var sortableWidgetInstance = getSortableWidgetInstance(angular.element(e.target));
+ sortableWidgetInstance.floating = isFloating(siblings);
+ }
+
+ // Save the starting position of dragged item
+ ui.item.sortable = {
+ model: ngModel.$modelValue[ui.item.index()],
+ index: ui.item.index(),
+ source: ui.item.parent(),
+ sourceModel: ngModel.$modelValue,
+ cancel: function () {
+ ui.item.sortable._isCanceled = true;
+ },
+ isCanceled: function () {
+ return ui.item.sortable._isCanceled;
+ },
+ isCustomHelperUsed: function () {
+ return !!ui.item.sortable._isCustomHelperUsed;
+ },
+ _isCanceled: false,
+ _isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed,
+ _destroy: function () {
+ angular.forEach(ui.item.sortable, function(value, key) {
+ ui.item.sortable[key] = undefined;
+ });
+ }
+ };
+ };
+
+ callbacks.activate = function(e, ui) {
+ // We need to make a copy of the current element's contents so
+ // we can restore it after sortable has messed it up.
+ // This is inside activate (instead of start) in order to save
+ // both lists when dragging between connected lists.
+ savedNodes = element.contents();
+
+ // If this list has a placeholder (the connected lists won't),
+ // don't inlcude it in saved nodes.
+ var placeholder = element.sortable('option','placeholder');
+
+ // placeholder.element will be a function if the placeholder, has
+ // been created (placeholder will be an object). If it hasn't
+ // been created, either placeholder will be false if no
+ // placeholder class was given or placeholder.element will be
+ // undefined if a class was given (placeholder will be a string)
+ if (placeholder && placeholder.element && typeof placeholder.element === 'function') {
+ var phElement = placeholder.element();
+ // workaround for jquery ui 1.9.x,
+ // not returning jquery collection
+ phElement = angular.element(phElement);
+
+ // exact match with the placeholder's class attribute to handle
+ // the case that multiple connected sortables exist and
+ // the placehoilder option equals the class of sortable items
+ var excludes = element.find('[class="' + phElement.attr('class') + '"]:not([ng-repeat], [data-ng-repeat])');
+
+ savedNodes = savedNodes.not(excludes);
+ }
+
+ // save the directive's scope so that it is accessible from ui.item.sortable
+ var connectedSortables = ui.item.sortable._connectedSortables || [];
+
+ connectedSortables.push({
+ element: element,
+ scope: scope
+ });
+
+ ui.item.sortable._connectedSortables = connectedSortables;
+ };
+
+ callbacks.update = function(e, ui) {
+ // Save current drop position but only if this is not a second
+ // update that happens when moving between lists because then
+ // the value will be overwritten with the old value
+ if(!ui.item.sortable.received) {
+ ui.item.sortable.dropindex = ui.item.index();
+ var droptarget = ui.item.parent();
+ ui.item.sortable.droptarget = droptarget;
+
+ var droptargetScope = getElementScope(ui.item.sortable._connectedSortables, droptarget);
+ ui.item.sortable.droptargetModel = droptargetScope.ngModel;
+
+ // Cancel the sort (let ng-repeat do the sort for us)
+ // Don't cancel if this is the received list because it has
+ // already been canceled in the other list, and trying to cancel
+ // here will mess up the DOM.
+ element.sortable('cancel');
+ }
+
+ // Put the nodes back exactly the way they started (this is very
+ // important because ng-repeat uses comment elements to delineate
+ // the start and stop of repeat sections and sortable doesn't
+ // respect their order (even if we cancel, the order of the
+ // comments are still messed up).
+ if (hasSortingHelper(element, ui) && !ui.item.sortable.received &&
+ element.sortable( 'option', 'appendTo' ) === 'parent') {
+ // restore all the savedNodes except .ui-sortable-helper element
+ // (which is placed last). That way it will be garbage collected.
+ savedNodes = savedNodes.not(savedNodes.last());
+ }
+ savedNodes.appendTo(element);
+
+ // If this is the target connected list then
+ // it's safe to clear the restored nodes since:
+ // update is currently running and
+ // stop is not called for the target list.
+ if(ui.item.sortable.received) {
+ savedNodes = null;
+ }
+
+ // If received is true (an item was dropped in from another list)
+ // then we add the new item to this list otherwise wait until the
+ // stop event where we will know if it was a sort or item was
+ // moved here from another list
+ if(ui.item.sortable.received && !ui.item.sortable.isCanceled()) {
+ scope.$apply(function () {
+ ngModel.$modelValue.splice(ui.item.sortable.dropindex, 0,
+ ui.item.sortable.moved);
+ });
+ }
+ };
+
+ callbacks.stop = function(e, ui) {
+ // If the received flag hasn't be set on the item, this is a
+ // normal sort, if dropindex is set, the item was moved, so move
+ // the items in the list.
+ if(!ui.item.sortable.received &&
+ ('dropindex' in ui.item.sortable) &&
+ !ui.item.sortable.isCanceled()) {
+
+ scope.$apply(function () {
+ ngModel.$modelValue.splice(
+ ui.item.sortable.dropindex, 0,
+ ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0]);
+ });
+ } else {
+ // if the item was not moved, then restore the elements
+ // so that the ngRepeat's comment are correct.
+ if ((!('dropindex' in ui.item.sortable) || ui.item.sortable.isCanceled()) &&
+ !hasSortingHelper(element, ui)) {
+ savedNodes.appendTo(element);
+ }
+ }
+
+ // It's now safe to clear the savedNodes
+ // since stop is the last callback.
+ savedNodes = null;
+ };
+
+ callbacks.receive = function(e, ui) {
+ // An item was dropped here from another list, set a flag on the
+ // item.
+ ui.item.sortable.received = true;
+ };
+
+ callbacks.remove = function(e, ui) {
+ // Workaround for a problem observed in nested connected lists.
+ // There should be an 'update' event before 'remove' when moving
+ // elements. If the event did not fire, cancel sorting.
+ if (!('dropindex' in ui.item.sortable)) {
+ element.sortable('cancel');
+ ui.item.sortable.cancel();
+ }
+
+ // Remove the item from this list's model and copy data into item,
+ // so the next list can retrive it
+ if (!ui.item.sortable.isCanceled()) {
+ scope.$apply(function () {
+ ui.item.sortable.moved = ngModel.$modelValue.splice(
+ ui.item.sortable.index, 1)[0];
+ });
+ }
+ };
+
+ wrappers.helper = function (inner) {
+ if (inner && typeof inner === 'function') {
+ return function (e, item) {
+ var innerResult = inner.apply(this, arguments);
+ item.sortable._isCustomHelperUsed = item !== innerResult;
+ return innerResult;
+ };
+ }
+ return inner;
+ };
+
+ scope.$watch('uiSortable', function(newVal /*, oldVal*/) {
+ // ensure that the jquery-ui-sortable widget instance
+ // is still bound to the directive's element
+ var sortableWidgetInstance = getSortableWidgetInstance(element);
+ if (!!sortableWidgetInstance) {
+ angular.forEach(newVal, function(value, key) {
+ // if it's a custom option of the directive,
+ // handle it approprietly
+ if (key in directiveOpts) {
+ if (key === 'ui-floating' && (value === false || value === true)) {
+ sortableWidgetInstance.floating = value;
+ }
+
+ opts[key] = value;
+ return;
+ }
+
+ if (callbacks[key]) {
+ if( key === 'stop' ){
+ // call apply after stop
+ value = combineCallbacks(
+ value, function() { scope.$apply(); });
+
+ value = combineCallbacks(value, afterStop);
+ }
+ // wrap the callback
+ value = combineCallbacks(callbacks[key], value);
+ } else if (wrappers[key]) {
+ value = wrappers[key](value);
+ }
+
+ opts[key] = value;
+ element.sortable('option', key, value);
+ });
+ }
+ }, true);
+
+ angular.forEach(callbacks, function(value, key) {
+ opts[key] = combineCallbacks(value, opts[key]);
+ if( key === 'stop' ){
+ opts[key] = combineCallbacks(opts[key], afterStop);
+ }
+ });
+
+ } else {
+ $log.info('ui.sortable: ngModel not provided!', element);
+ }
+
+ // Create sortable
+ element.sortable(opts);
+ }
+ };
+ }
+ ]);
+
+})(window, window.angular);
+
+(function (angular) {
+ 'use strict';
+
+ // RFC4122 version 4 compliant UUID generator.
+ // Based on: https://github.com/pnegri/uuid-js
+ angular.module('uuid4', []).factory('uuid4', function () {
+
+ var maxFromBits = function(bits) {
+ return Math.pow(2, bits);
+ };
+
+ var limitUI04 = maxFromBits(4);
+ var limitUI06 = maxFromBits(6);
+ var limitUI08 = maxFromBits(8);
+ var limitUI12 = maxFromBits(12);
+ var limitUI14 = maxFromBits(14);
+ var limitUI16 = maxFromBits(16);
+ var limitUI32 = maxFromBits(32);
+ var limitUI40 = maxFromBits(40);
+ var limitUI48 = maxFromBits(48);
+
+ var getRandomInt = function(min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+
+ var randomUI06 = function() {
+ return getRandomInt(0, limitUI06-1);
+ };
+
+ var randomUI08 = function() {
+ return getRandomInt(0, limitUI08-1);
+ };
+
+ var randomUI12 = function() {
+ return getRandomInt(0, limitUI12-1);
+ };
+
+ var randomUI16 = function() {
+ return getRandomInt(0, limitUI16-1);
+ };
+
+ var randomUI32 = function() {
+ return getRandomInt(0, limitUI32-1);
+ };
+
+ var randomUI48 = function() {
+ return (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << 48 - 30)) * (1 << 30);
+ };
+
+ var paddedString = function(string, length, z) {
+ string = String(string);
+ z = (!z) ? '0' : z;
+ var i = length - string.length;
+ for (; i > 0; i >>>= 1, z += z) {
+ if (i & 1) {
+ string = z + string;
+ }
+ }
+ return string;
+ };
+
+ var fromParts = function(timeLow, timeMid, timeHiAndVersion, clockSeqHiAndReserved, clockSeqLow, node) {
+ var hex = paddedString(timeLow.toString(16), 8)
+ + '-'
+ + paddedString(timeMid.toString(16), 4)
+ + '-'
+ + paddedString(timeHiAndVersion.toString(16), 4)
+ + '-'
+ + paddedString(clockSeqHiAndReserved.toString(16), 2)
+ + paddedString(clockSeqLow.toString(16), 2)
+ + '-'
+ + paddedString(node.toString(16), 12);
+ return hex;
+ };
+
+ return {
+ generate: function () {
+ return fromParts(
+ randomUI32(),
+ randomUI16(),
+ 0x4000 | randomUI12(),
+ 0x80 | randomUI06(),
+ randomUI08(),
+ randomUI48()
+ );
+ },
+
+ // addition by Ka-Jan to test for validity
+ // Based on: http://stackoverflow.com/questions/7905929/how-to-test-valid-uuid-guid
+ validate: function (uuid) {
+ var testPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+ return testPattern.test(uuid);
+ }
+ };
+ });
+
+}(angular));
+
+/* Firebase v1.0.24 - License: https://www.firebase.com/terms/terms-of-service.html */ (function() {var scope={};(function(){"use strict";function m(c,b){if(0===c.length||0===b.length)return c.concat(b);var a=c[c.length-1],d=Math.round(a/1099511627776)||32,e;if(32===d)e=c.concat(b);else{e=b;var a=a|0,f=c.slice(0,c.length-1),g;for(void 0===f&&(f=[]);32<=d;d-=32)f.push(a),a=0;if(0===d)e=f.concat(e);else{for(g=0;g
>>d),a=e[g]<<32-d;g=e.length?e[e.length-1]:0;e=Math.round(g/1099511627776)||32;f.push(n(d+e&31,32=a;a++)16<=a&&(k[a]=(k[a-3]^k[a-8]^k[a-14]^k[a-16])<<1|(k[a-3]^k[a-8]^k[a-14]^k[a-16])>>>31),d=19>=a?f&g|~f&l:39>=a?f^g^l:59>=a?f&g|f&l|g&l:79>=a?f^g^l:void 0,d=(e<<5|e>>>27)+d+q+k[a]+c.f[Math.floor(a/20)]|0,q=l,l=g,g=f<<30|f>>>2,f=e,e=d;h[0]=h[0]+e|0;h[1]=h[1]+f|0;h[2]=h[2]+g|0;h[3]=h[3]+l|0;h[4]=h[4]+q|0}
+function t(c){var b=(new r).update(c),a,d=b.b;c=b.c;d=m(d,[n(1,1)]);for(a=d.length+2;a&15;a++)d.push(0);d.push(Math.floor(b.a/4294967296));for(d.push(b.a|0);d.length;)s(b,d.splice(0,16));b.reset();var e,b="";a=0;var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",f=0,g=p(c);e&&(d=d.substr(0,62)+"-_");for(e=0;6*b.length>>a)>>>26),6>a?(f=c[e]<<6-a,a+=26,e++):(f<<=6,a-=6);for(;b.length&3;)b+="=";return b}var u=["sjclHashToBase64"],v=this;
+u[0]in v||!v.execScript||v.execScript("var "+u[0]);for(var w;u.length&&(w=u.shift());)u.length||void 0===t?v=v[w]?v[w]:v[w]={}:v[w]=t;}).apply(scope);var sjclHashToBase64=scope['sjclHashToBase64']; var h,aa=this;function m(a){return void 0!==a}function ba(){}function ca(a){a.qb=function(){return a.hd?a.hd:a.hd=new a}}
+function da(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function ea(a){return"array"==da(a)}function fa(a){var b=da(a);return"array"==b||"object"==b&&"number"==typeof a.length}function p(a){return"string"==typeof a}function ga(a){return"number"==typeof a}function ha(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ia(a,b,c){return a.call.apply(a.bind,arguments)}
+function ja(a,b,c){if(!a)throw Error();if(2b?e+="000":256>b?e+="00":4096>b&&(e+="0");return pa[a]=e+b.toString(16)}),'"')};function ra(a){return"undefined"!==typeof JSON&&m(JSON.parse)?JSON.parse(a):la(a)}function u(a){if("undefined"!==typeof JSON&&m(JSON.stringify))a=JSON.stringify(a);else{var b=[];na(new ma,a,b);a=b.join("")}return a};function sa(a){for(var b=[],c=0,d=0;d=e&&(e-=55296,d++,v(de?b[c++]=e:(2048>e?b[c++]=e>>6|192:(65536>e?b[c++]=e>>12|224:(b[c++]=e>>18|240,b[c++]=e>>12&63|128),b[c++]=e>>6&63|128),b[c++]=e&63|128)}return b};var ta={};function x(a,b,c,d){var e;dc&&(e=0===c?"none":"no more than "+c);if(e)throw Error(a+" failed: Was called with "+d+(1===d?" argument.":" arguments.")+" Expects "+e+".");}
+function y(a,b,c){var d="";switch(b){case 1:d=c?"first":"First";break;case 2:d=c?"second":"Second";break;case 3:d=c?"third":"Third";break;case 4:d=c?"fourth":"Fourth";break;default:ua.assert(!1,"errorPrefix_ called with argumentNumber > 4. Need to update it?")}return a=a+" failed: "+(d+" argument ")}function z(a,b,c,d){if((!d||m(c))&&"function"!=da(c))throw Error(y(a,b,d)+"must be a valid function.");}
+function va(a,b,c){if(m(c)&&(!ha(c)||null===c))throw Error(y(a,b,!0)+"must be a valid context object.");};function A(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function wa(a,b){if(Object.prototype.hasOwnProperty.call(a,b))return a[b]};var ua={},xa=/[\[\].#$\/\u0000-\u001F\u007F]/,ya=/[\[\].#$\u0000-\u001F\u007F]/;function za(a){return p(a)&&0!==a.length&&!xa.test(a)}function Aa(a,b,c){c&&!m(b)||Ba(y(a,1,c),b)}
+function Ba(a,b,c,d){c||(c=0);d=d||[];if(!m(b))throw Error(a+"contains undefined"+Ca(d));if("function"==da(b))throw Error(a+"contains a function"+Ca(d)+" with contents: "+b.toString());if(Da(b))throw Error(a+"contains "+b.toString()+Ca(d));if(1E310485760/3&&10485760=a)throw"Query.limit: First argument must be a positive integer.";return new D(this.m,this.path,a,this.da,this.wa,this.Ba,this.Xa)};D.prototype.limit=D.prototype.Md;D.prototype.wd=function(a,b){x("Query.startAt",0,2,arguments.length);Fa("Query.startAt",1,a,!0);Ha("Query.startAt",b);m(a)||(b=a=null);return new D(this.m,this.path,this.Da,a,b,this.Ba,this.Xa)};D.prototype.startAt=D.prototype.wd;
+D.prototype.cd=function(a,b){x("Query.endAt",0,2,arguments.length);Fa("Query.endAt",1,a,!0);Ha("Query.endAt",b);return new D(this.m,this.path,this.Da,this.da,this.wa,a,b)};D.prototype.endAt=D.prototype.cd;D.prototype.Gd=function(a,b){x("Query.equalTo",1,2,arguments.length);Fa("Query.equalTo",1,a,!1);Ha("Query.equalTo",b);return this.wd(a,b).cd(a,b)};D.prototype.equalTo=D.prototype.Gd;
+function Ka(a){var b={};m(a.da)&&(b.sp=a.da);m(a.wa)&&(b.sn=a.wa);m(a.Ba)&&(b.ep=a.Ba);m(a.Xa)&&(b.en=a.Xa);m(a.Da)&&(b.l=a.Da);m(a.da)&&m(a.wa)&&null===a.da&&null===a.wa&&(b.vf="l");return b}D.prototype.Ra=function(){var a=La(Ka(this));return"{}"===a?"default":a};
+function Ja(a,b,c){var d={};if(b&&c)d.cancel=b,z(a,3,d.cancel,!0),d.Y=c,va(a,4,d.Y);else if(b)if("object"===typeof b&&null!==b)d.Y=b;else if("function"===typeof b)d.cancel=b;else throw Error(ta.fe(a,3,!0)+"must either be a cancel callback or a context object.");return d};function F(a,b){if(1==arguments.length){this.o=a.split("/");for(var c=0,d=0;d=a.o.length?null:a.o[a.U]}function Ma(a){var b=a.U;b=this.o.length)return null;for(var a=[],b=this.U;b=this.o.length};h.length=function(){return this.o.length-this.U};
+function Oa(a,b){var c=C(a);if(null===c)return b;if(c===C(b))return Oa(Ma(a),Ma(b));throw"INTERNAL ERROR: innerPath ("+b+") is not within outerPath ("+a+")";}h.contains=function(a){var b=this.U,c=a.U;if(this.length()>a.length())return!1;for(;bb?1:0}h=Ua.prototype;h.qa=function(a,b){return new Ua(this.Va,this.ca.qa(a,b,this.Va).J(null,null,!1,null,null))};h.remove=function(a){return new Ua(this.Va,this.ca.remove(a,this.Va).J(null,null,!1,null,null))};h.get=function(a){for(var b,c=this.ca;!c.f();){b=this.Va(a,c.key);if(0===b)return c.value;0>b?c=c.left:0c?d=d.left:0d?e.J(null,null,null,e.left.qa(a,b,c),null):0===d?e.J(null,b,null,null,null):e.J(null,null,null,null,e.right.qa(a,b,c));return db(e)};function eb(a){if(a.left.f())return Wa;a.left.P()||a.left.left.P()||(a=fb(a));a=a.J(null,null,null,eb(a.left),null);return db(a)}
+h.remove=function(a,b){var c,d;c=this;if(0>b(a,c.key))c.left.f()||c.left.P()||c.left.left.P()||(c=fb(c)),c=c.J(null,null,null,c.left.remove(a,b),null);else{c.left.P()&&(c=gb(c));c.right.f()||c.right.P()||c.right.left.P()||(c=hb(c),c.left.left.P()&&(c=gb(c),c=hb(c)));if(0===b(a,c.key)){if(c.right.f())return Wa;d=cb(c.right);c=c.J(d.key,d.value,null,null,eb(c.right))}c=c.J(null,null,null,null,c.right.remove(a,b))}return db(c)};h.P=function(){return this.color};
+function db(a){a.right.P()&&!a.left.P()&&(a=ib(a));a.left.P()&&a.left.left.P()&&(a=gb(a));a.left.P()&&a.right.P()&&(a=hb(a));return a}function fb(a){a=hb(a);a.right.left.P()&&(a=a.J(null,null,null,null,gb(a.right)),a=ib(a),a=hb(a));return a}function ib(a){return a.right.J(null,null,a.color,a.J(null,null,!0,null,a.right.left),null)}function gb(a){return a.left.J(null,null,a.color,null,a.J(null,null,!0,a.left.right,null))}
+function hb(a){return a.J(null,null,!a.color,a.left.J(null,null,!a.left.color,null,null),a.right.J(null,null,!a.right.color,null,null))}function jb(){}h=jb.prototype;h.J=function(){return this};h.qa=function(a,b){return new $a(a,b,null)};h.remove=function(){return this};h.count=function(){return 0};h.f=function(){return!0};h.Ca=function(){return!1};h.Sa=function(){return!1};h.wb=function(){return null};h.ab=function(){return null};h.P=function(){return!1};var Wa=new jb;function kb(a){this.Ub=a;this.hc="firebase:"}kb.prototype.set=function(a,b){null==b?this.Ub.removeItem(this.hc+a):this.Ub.setItem(this.hc+a,u(b))};kb.prototype.get=function(a){a=this.Ub.getItem(this.hc+a);return null==a?null:ra(a)};kb.prototype.remove=function(a){this.Ub.removeItem(this.hc+a)};kb.prototype.kd=!1;function lb(){this.mb={}}lb.prototype.set=function(a,b){null==b?delete this.mb[a]:this.mb[a]=b};lb.prototype.get=function(a){return A(this.mb,a)?this.mb[a]:null};lb.prototype.remove=function(a){delete this.mb[a]};lb.prototype.kd=!0;function mb(a){try{if("undefined"!==typeof window&&"undefined"!==typeof window[a]){var b=window[a];b.setItem("firebase:sentinel","cache");b.removeItem("firebase:sentinel");return new kb(b)}}catch(c){}return new lb}var nb=mb("localStorage"),ob=mb("sessionStorage");function pb(a,b,c,d){this.host=a.toLowerCase();this.domain=this.host.substr(this.host.indexOf(".")+1);this.nc=b;this.Zb=c;this.ce=d;this.ga=nb.get("host:"+a)||this.host}function qb(a,b){b!==a.ga&&(a.ga=b,"s-"===a.ga.substr(0,2)&&nb.set("host:"+a.host,a.ga))}pb.prototype.toString=function(){return(this.nc?"https://":"http://")+this.host};var rb=Array.prototype,sb=rb.forEach?function(a,b,c){rb.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;fc?null:p(a)?a.charAt(c):a[c]};var xb;a:{var yb=aa.navigator;if(yb){var zb=yb.userAgent;if(zb){xb=zb;break a}}xb=""}function Ab(a){return-1!=xb.indexOf(a)};var Bb=Ab("Opera")||Ab("OPR"),Cb=Ab("Trident")||Ab("MSIE"),Db=Ab("Gecko")&&-1==xb.toLowerCase().indexOf("webkit")&&!(Ab("Trident")||Ab("MSIE")),Eb=-1!=xb.toLowerCase().indexOf("webkit");(function(){var a="",b;if(Bb&&aa.opera)return a=aa.opera.version,"function"==da(a)?a():a;Db?b=/rv\:([^\);]+)(\)|;)/:Cb?b=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:Eb&&(b=/WebKit\/(\S+)/);b&&(a=(a=b.exec(xb))?a[1]:"");return Cb&&(b=(b=aa.document)?b.documentMode:void 0,b>parseFloat(a))?String(b):a})();var Fb=null,Gb=null;var Hb=function(){var a=1;return function(){return a++}}();function v(a,b){if(!a)throw Error("Firebase INTERNAL ASSERT FAILED:"+b);}function Ib(a){for(var b="",c=0;cb?1:-1:0}function Rb(a,b){if(a===b)return 0;var c=Sb(a),d=Sb(b);return null!==c?null!==d?0==c-d?a.length-b.length:c-d:-1:null!==d?1:aa?c.push(a.substring(d,a.length)):c.push(a.substring(d,d+b));return c}function Vb(a,b){if(ea(a))for(var c=0;ca,a=Math.abs(a),a>=Math.pow(2,-1022)?(d=Math.min(Math.floor(Math.log(a)/Math.LN2),1023),c=d+1023,d=Math.round(a*Math.pow(2,52-d)-Math.pow(2,52))):(c=0,d=Math.round(a/Math.pow(2,-1074))));e=[];for(a=52;a;a-=1)e.push(d%2?1:0),d=Math.floor(d/2);for(a=11;a;a-=1)e.push(c%2?1:0),c=Math.floor(c/2);e.push(b?1:0);e.reverse();b=e.join("");c="";for(a=0;64>a;a+=8)d=parseInt(b.substr(a,8),2).toString(16),1===d.length&&
+(d="0"+d),c+=d;return c.toLowerCase()}function Zb(a){var b="Unknown Error";"too_big"===a?b="The data requested exceeds the maximum size that can be accessed with a single request.":"permission_denied"==a?b="Client doesn't have permission to access the desired data.":"unavailable"==a&&(b="The service is unavailable");b=Error(a+": "+b);b.code=a.toUpperCase();return b}var $b=/^-?\d{1,10}$/;function Sb(a){return $b.test(a)&&(a=Number(a),-2147483648<=a&&2147483647>=a)?a:null}
+function ac(a){try{a()}catch(b){setTimeout(function(){throw b;},Math.floor(0))}};function bc(a,b){this.F=a;v(null!==this.F,"LeafNode shouldn't be created with null value.");this.fb="undefined"!==typeof b?b:null}h=bc.prototype;h.O=function(){return!0};h.k=function(){return this.fb};h.Ha=function(a){return new bc(this.F,a)};h.N=function(){return M};h.K=function(a){return null===C(a)?this:M};h.fa=function(){return null};h.H=function(a,b){return(new N).H(a,b).Ha(this.fb)};h.ya=function(a,b){var c=C(a);return null===c?b:this.H(c,M.ya(Ma(a),b))};h.f=function(){return!1};h.ac=function(){return 0};
+h.V=function(a){return a&&null!==this.k()?{".value":this.j(),".priority":this.k()}:this.j()};h.hash=function(){var a="";null!==this.k()&&(a+="priority:"+cc(this.k())+":");var b=typeof this.F,a=a+(b+":"),a="number"===b?a+Yb(this.F):a+this.F;return sjclHashToBase64(a)};h.j=function(){return this.F};h.toString=function(){return"string"===typeof this.F?this.F:'"'+this.F+'"'};function dc(a,b){return Qb(a.ja,b.ja)||Rb(a.name,b.name)}function ec(a,b){return Rb(a.name,b.name)}function fc(a,b){return Rb(a,b)};function N(a,b){this.n=a||new Ua(fc);this.fb="undefined"!==typeof b?b:null}h=N.prototype;h.O=function(){return!1};h.k=function(){return this.fb};h.Ha=function(a){return new N(this.n,a)};h.H=function(a,b){var c=this.n.remove(a);b&&b.f()&&(b=null);null!==b&&(c=c.qa(a,b));return b&&null!==b.k()?new gc(c,null,this.fb):new N(c,this.fb)};h.ya=function(a,b){var c=C(a);if(null===c)return b;var d=this.N(c).ya(Ma(a),b);return this.H(c,d)};h.f=function(){return this.n.f()};h.ac=function(){return this.n.count()};
+var ic=/^(0|[1-9]\d*)$/;h=N.prototype;h.V=function(a){if(this.f())return null;var b={},c=0,d=0,e=!0;this.A(function(f,g){b[f]=g.V(a);c++;e&&ic.test(f)?d=Math.max(d,Number(f)):e=!1});if(!a&&e&&d<2*c){var f=[],g;for(g in b)f[g]=b[g];return f}a&&null!==this.k()&&(b[".priority"]=this.k());return b};h.hash=function(){var a="";null!==this.k()&&(a+="priority:"+cc(this.k())+":");this.A(function(b,c){var d=c.hash();""!==d&&(a+=":"+b+":"+d)});return""===a?"":sjclHashToBase64(a)};
+h.N=function(a){a=this.n.get(a);return null===a?M:a};h.K=function(a){var b=C(a);return null===b?this:this.N(b).K(Ma(a))};h.fa=function(a){return Xa(this.n,a)};h.ed=function(){return this.n.wb()};h.gd=function(){return this.n.ab()};h.A=function(a){return this.n.Ca(a)};h.Bc=function(a){return this.n.Sa(a)};h.$a=function(){return this.n.$a()};h.toString=function(){var a="{",b=!0;this.A(function(c,d){b?b=!1:a+=", ";a+='"'+c+'" : '+d.toString()});return a+="}"};var M=new N;function gc(a,b,c){N.call(this,a,c);null===b&&(b=new Ua(dc),a.Ca(function(a,c){b=b.qa({name:a,ja:c.k()},c)}));this.va=b}ka(gc,N);h=gc.prototype;h.H=function(a,b){var c=this.N(a),d=this.n,e=this.va;null!==c&&(d=d.remove(a),e=e.remove({name:a,ja:c.k()}));b&&b.f()&&(b=null);null!==b&&(d=d.qa(a,b),e=e.qa({name:a,ja:b.k()},b));return new gc(d,e,this.k())};h.fa=function(a,b){var c=Xa(this.va,{name:a,ja:b.k()});return c?c.name:null};h.A=function(a){return this.va.Ca(function(b,c){return a(b.name,c)})};
+h.Bc=function(a){return this.va.Sa(function(b,c){return a(b.name,c)})};h.$a=function(){return this.va.$a(function(a,b){return{key:a.name,value:b}})};h.ed=function(){return this.va.f()?null:this.va.wb().name};h.gd=function(){return this.va.f()?null:this.va.ab().name};function O(a,b){if(null===a)return M;var c=null;"object"===typeof a&&".priority"in a?c=a[".priority"]:"undefined"!==typeof b&&(c=b);v(null===c||"string"===typeof c||"number"===typeof c||"object"===typeof c&&".sv"in c,"Invalid priority type found: "+typeof c);"object"===typeof a&&".value"in a&&null!==a[".value"]&&(a=a[".value"]);if("object"!==typeof a||".sv"in a)return new bc(a,c);if(a instanceof Array){var d=M,e=a;Wb(e,function(a,b){if(A(e,b)&&"."!==b.substring(0,1)){var c=O(a);if(c.O()||!c.f())d=
+d.H(b,c)}});return d.Ha(c)}var f=[],g={},k=!1,l=a;Vb(l,function(a,b){if("string"!==typeof b||"."!==b.substring(0,1)){var c=O(l[b]);c.f()||(k=k||null!==c.k(),f.push({name:b,ja:c.k()}),g[b]=c)}});var n=jc(f,g,!1);if(k){var r=jc(f,g,!0);return new gc(n,r,c)}return new N(n,c)}var kc=Math.log(2);function lc(a){this.count=parseInt(Math.log(a+1)/kc,10);this.ad=this.count-1;this.Dd=a+1&parseInt(Array(this.count+1).join("1"),2)}function mc(a){var b=!(a.Dd&1<=a.length){var b=Number(a);if(!isNaN(b)){c.Xc=b;c.frames=[];a=null;break a}}c.Xc=1;c.frames=[]}null!==a&&Ec(c,a)}};this.W.onerror=function(a){c.e("WebSocket error. Closing connection.");(a=a.message||a.data)&&c.e(a);c.Qa()}};Q.prototype.start=function(){};Q.isAvailable=function(){var a=!1;if("undefined"!==typeof navigator&&navigator.userAgent){var b=navigator.userAgent.match(/Android ([0-9]{0,}\.[0-9]{0,})/);b&&1parseFloat(b[1])&&(a=!0)}return!a&&null!==Bc&&!Cc};
+Q.responsesRequiredToBeHealthy=2;Q.healthyTimeout=3E4;h=Q.prototype;h.Xb=function(){nb.remove("previous_websocket_failure")};function Ec(a,b){a.frames.push(b);if(a.frames.length==a.Xc){var c=a.frames.join("");a.frames=null;c=ra(c);a.Qd(c)}}h.send=function(a){Dc(this);a=u(a);this.Ja+=a.length;uc(this.ea,"bytes_sent",a.length);a=Ub(a,16384);1b;b++)Fb[b]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b),Gb[b]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.".charAt(b)}for(var b=Gb,c=[],d=0;d>2,e=(e&3)<<4|g>>4,g=(g&15)<<
+2|l>>6,l=l&63;k||(l=64,f||(g=64));c.push(b[n],b[e],b[g],b[l])}a=Ub(c.join(""),1840);for(b=0;bdocument.domain="'+document.domain+'";\x3c/script>');a=""+a+"";try{this.Z.Aa.open(),this.Z.Aa.write(a),this.Z.Aa.close()}catch(f){K("frame writing exception"),f.stack&&K(f.stack),K(f)}}
+Mc.prototype.close=function(){this.uc=!1;if(this.Z){this.Z.Aa.body.innerHTML="";var a=this;setTimeout(function(){null!==a.Z&&(document.body.removeChild(a.Z),a.Z=null)},Math.floor(0))}var b=this.ia;b&&(this.ia=null,b())};
+function Pc(a){if(a.uc&&a.oc&&a.Nc.count()<(0=a.Fb[0].bd.length+30+c.length){var e=a.Fb.shift(),c=c+"&seg"+d+"="+e.Yd+"&ts"+d+"="+e.be+"&d"+d+"="+e.bd;d++}else break;Sc(a,b+c,a.zc);return!0}return!1}function Sc(a,b,c){function d(){a.Nc.remove(c);Pc(a)}a.Nc.add(c);var e=setTimeout(d,Math.floor(25E3));Oc(a,b,function(){clearTimeout(e);d()})}
+function Oc(a,b,c){setTimeout(function(){try{if(a.oc){var d=a.Z.Aa.createElement("script");d.type="text/javascript";d.async=!0;d.src=b;d.onload=d.onreadystatechange=function(){var a=d.readyState;a&&"loaded"!==a&&"complete"!==a||(d.onload=d.onreadystatechange=null,d.parentNode&&d.parentNode.removeChild(d),c())};d.onerror=function(){K("Long-poll script failed to load: "+b);a.oc=!1;a.close()};a.Z.Aa.body.appendChild(d)}}catch(e){}},Math.floor(1))};function Tc(a){Uc(this,a)}var Vc=[Jc,Q];function Uc(a,b){var c=Q&&Q.isAvailable(),d=c&&!(nb.kd||!0===nb.get("previous_websocket_failure"));b.ce&&(c||L("wss:// URL used, but browser isn't known to support websockets. Trying anyway."),d=!0);if(d)a.Mb=[Q];else{var e=a.Mb=[];Vb(Vc,function(a,b){b&&b.isAvailable()&&e.push(b)})}}function Wc(a){if(0=a.td?(a.e("Secondary connection is healthy."),a.Oa=!0,a.w.Xb(),a.w.start(),a.e("sending client ack on secondary"),a.w.send({t:"c",d:{t:"a",d:{}}}),a.e("Ending transmission on primary"),a.B.send({t:"c",d:{t:"n",d:{}}}),a.Nb=a.w,cd(a)):(a.e("sending ping on secondary."),a.w.send({t:"c",d:{t:"p",d:{}}}))}Xc.prototype.dc=function(a){ed(this);this.Lc(a)};function ed(a){a.Oa||(a.Pc--,0>=a.Pc&&(a.e("Primary connection is healthy."),a.Oa=!0,a.B.Xb()))}
+function bd(a,b){a.w=new b("c:"+a.id+":"+a.Zc++,a.M,a.pc);a.td=b.responsesRequiredToBeHealthy||0;a.w.open(Zc(a,a.w),$c(a,a.w));setTimeout(function(){a.w&&(a.e("Timed out trying to upgrade."),a.w.close())},Math.floor(6E4))}function ad(a,b,c){a.e("Realtime connection established.");a.B=b;a.ma=1;a.Ab&&(a.Ab(c),a.Ab=null);0===a.Pc?(a.e("Primary connection is healthy."),a.Oa=!0):setTimeout(function(){fd(a)},Math.floor(5E3))}
+function fd(a){a.Oa||1!==a.ma||(a.e("sending ping on primary."),hd(a,{t:"c",d:{t:"p",d:{}}}))}function hd(a,b){if(1!==a.ma)throw"Connection is not connected";a.Nb.send(b)}Xc.prototype.close=function(){2!==this.ma&&(this.e("Closing realtime connection."),this.ma=2,dd(this),this.S&&(this.S(),this.S=null))};function dd(a){a.e("Shutting down all connections");a.B&&(a.B.close(),a.B=null);a.w&&(a.w.close(),a.w=null);a.Vb&&(clearTimeout(a.Vb),a.Vb=null)};function id(a,b,c,d,e,f){this.id=jd++;this.e=Mb("p:"+this.id+":");this.Ta=!0;this.ha={};this.T=[];this.Cb=0;this.zb=[];this.R=!1;this.sa=1E3;this.Yb=3E5;this.ec=b||ba;this.cc=c||ba;this.yb=d||ba;this.Mc=e||ba;this.Dc=f||ba;this.M=a;this.Rc=null;this.Jb={};this.Xd=0;this.ub=this.Hc=null;kd(this,0);pc.qb().eb("visible",this.Sd,this);-1===a.host.indexOf("fblocal")&&qc.qb().eb("online",this.Rd,this)}var jd=0,ld=0;h=id.prototype;
+h.Fa=function(a,b,c){var d=++this.Xd;a={r:d,a:a,b:b};this.e(u(a));v(this.R,"sendRequest_ call when we're not connected not allowed.");this.ka.ud(a);c&&(this.Jb[d]=c)};function md(a,b,c){var d=b.toString(),e=b.path().toString();a.ha[e]=a.ha[e]||{};v(!a.ha[e][d],"listen() called twice for same path/queryId.");a.ha[e][d]={gb:b.gb(),D:c};a.R&&nd(a,e,d,b.gb(),c)}
+function nd(a,b,c,d,e){a.e("Listen on "+b+" for "+c);var f={p:b};d=tb(d,function(a){return Ka(a)});"{}"!==c&&(f.q=d);f.h=a.Dc(b);a.Fa("l",f,function(d){a.e("listen response",d);d=d.s;"ok"!==d&&od(a,b,c);e&&e(d)})}
+h.lb=function(a,b,c){this.La={Fd:a,dd:!1,aa:b,Rb:c};this.e("Authenticating using credential: "+this.La);pd(this);if(!(b=40==a.length))a:{var d;try{var e=a.split(".");if(3!==e.length){b=!1;break a}var f;b:{try{if("undefined"!==typeof atob){f=atob(e[1]);break b}}catch(g){K("base64DecodeIfNativeSupport failed: ",g)}f=null}null!==f&&(d=ra(f))}catch(k){K("isAdminAuthToken_ failed",k)}b="object"===typeof d&&!0===wa(d,"admin")}b&&(this.e("Admin auth credential detected. Reducing max reconnect time."),this.Yb=
+3E4)};h.Ob=function(a){delete this.La;this.yb(!1);this.R&&this.Fa("unauth",{},function(b){a(b.s,b.d)})};function pd(a){var b=a.La;a.R&&b&&a.Fa("auth",{cred:b.Fd},function(c){var d=c.s;c=c.d||"error";"ok"!==d&&a.La===b&&delete a.La;a.yb("ok"===d);b.dd?"ok"!==d&&b.Rb&&b.Rb(d,c):(b.dd=!0,b.aa&&b.aa(d,c))})}function qd(a,b,c,d){b=b.toString();od(a,b,c)&&a.R&&rd(a,b,c,d)}function rd(a,b,c,d){a.e("Unlisten on "+b+" for "+c);b={p:b};d=tb(d,function(a){return Ka(a)});"{}"!==c&&(b.q=d);a.Fa("u",b)}
+function sd(a,b,c,d){a.R?td(a,"o",b,c,d):a.zb.push({Oc:b,action:"o",data:c,D:d})}function ud(a,b,c,d){a.R?td(a,"om",b,c,d):a.zb.push({Oc:b,action:"om",data:c,D:d})}h.Jc=function(a,b){this.R?td(this,"oc",a,null,b):this.zb.push({Oc:a,action:"oc",data:null,D:b})};function td(a,b,c,d,e){c={p:c,d:d};a.e("onDisconnect "+b,c);a.Fa(b,c,function(a){e&&setTimeout(function(){e(a.s,a.d)},Math.floor(0))})}h.put=function(a,b,c,d){vd(this,"p",a,b,c,d)};function wd(a,b,c,d){vd(a,"m",b,c,d,void 0)}
+function vd(a,b,c,d,e,f){c={p:c,d:d};m(f)&&(c.h=f);a.T.push({action:b,qd:c,D:e});a.Cb++;b=a.T.length-1;a.R&&xd(a,b)}function xd(a,b){var c=a.T[b].action,d=a.T[b].qd,e=a.T[b].D;a.T[b].Ud=a.R;a.Fa(c,d,function(d){a.e(c+" response",d);delete a.T[b];a.Cb--;0===a.Cb&&(a.T=[]);e&&e(d.s,d.d)})}
+h.dc=function(a){if("r"in a){this.e("from server: "+u(a));var b=a.r,c=this.Jb[b];c&&(delete this.Jb[b],c(a.b))}else{if("error"in a)throw"A server-side error has occurred: "+a.error;"a"in a&&(b=a.a,c=a.b,this.e("handleServerMessage",b,c),"d"===b?this.ec(c.p,c.d,!1):"m"===b?this.ec(c.p,c.d,!0):"c"===b?yd(this,c.p,c.q):"ac"===b?(a=c.s,b=c.d,c=this.La,delete this.La,c&&c.Rb&&c.Rb(a,b),this.yb(!1)):"sd"===b?this.Rc?this.Rc(c):"msg"in c&&"undefined"!==typeof console&&console.log("FIREBASE: "+c.msg.replace("\n",
+"\nFIREBASE: ")):Nb("Unrecognized action received from server: "+u(b)+"\nAre you using the latest client?"))}};h.Ab=function(a){this.e("connection ready");this.R=!0;this.ub=(new Date).getTime();this.Mc({serverTimeOffset:a-(new Date).getTime()});pd(this);for(var b in this.ha)for(var c in this.ha[b])a=this.ha[b][c],nd(this,b,c,a.gb,a.D);for(b=0;bc)f=wa(t,G.key),m(f)?(n.push({Cc:G,Wc:k[f]}),k[f]=null):(s[G.key]=l.length,l.push(G)),f=!0,G=Za(w);else{if(0c||0===c&&0>=Rb(a,d.Xa)}):c.push(function(a,b){return 0>=Qb(b,d.Ba)}));var e=null,f=null;if(m(this.Q.Da))if(m(this.Q.da)){if(e=fe(a,c,this.Q.Da,!1)){var g=a.N(e).k();c.push(function(a,b){var c=Qb(b,g);return 0>c||0===c&&
+0>=Rb(a,e)})}}else if(f=fe(a,c,this.Q.Da,!0)){var k=a.N(f).k();c.push(function(a,b){var c=Qb(b,k);return 0f;f++)b[f]=Math.floor(64*Math.random());for(f=0;12>f;f++)c+="-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(b[f]);v(20===c.length,"NextPushId: Length should be 20.");
+return c}}();function E(a,b){var c,d;if(a instanceof we)c=a,d=b;else{x("new Firebase",1,2,arguments.length);var e=arguments[0];d=c="";var f=!0,g="";if(p(e)){var k=e.indexOf("//");if(0<=k)var l=e.substring(0,k-1),e=e.substring(k+2);k=e.indexOf("/");-1===k&&(k=e.length);c=e.substring(0,k);var e=e.substring(k+1),n=c.split(".");if(3==n.length){k=n[2].indexOf(":");f=0<=k?"https"===l||"wss"===l:!0;if("firebase"===n[1])Ob(c+" is no longer supported. Please use .firebaseio.com instead");else for(d=n[0],
+g="",e=("/"+e).split("/"),k=0;k.firebaseio.com")}f||"undefined"!==typeof window&&window.location&&window.location.protocol&&-1!==window.location.protocol.indexOf("https:")&&L("Insecure Firebase access from a secure page. Please use https in calls to new Firebase().");c=new pb(c,f,d,"ws"===l||"wss"===l);d=new F(g);
+f=d.toString();!(l=!p(c.host)||0===c.host.length||!za(c.Zb))&&(l=0!==f.length)&&(f&&(f=f.replace(/^\/*\.info(\/|$)/,"/")),l=!(p(f)&&0!==f.length&&!ya.test(f)));if(l)throw Error(y("new Firebase",1,!1)+'must be a valid firebase URL and the path can\'t contain ".", "#", "$", "[", or "]".');if(b)if(b instanceof Y)f=b;else throw Error("Expected a valid Firebase.Context for second argument to new Firebase()");else f=Y.qb();l=c.toString();e=wa(f.hb,l);e||(e=new we(c),f.hb[l]=e);c=e}D.call(this,c,d)}
+ka(E,D);var Te=E,Ue=["Firebase"],Ve=aa;Ue[0]in Ve||!Ve.execScript||Ve.execScript("var "+Ue[0]);for(var We;Ue.length&&(We=Ue.shift());)!Ue.length&&m(Te)?Ve[We]=Te:Ve=Ve[We]?Ve[We]:Ve[We]={};E.prototype.name=function(){x("Firebase.name",0,0,arguments.length);return this.path.f()?null:Na(this.path)};E.prototype.name=E.prototype.name;
+E.prototype.G=function(a){x("Firebase.child",1,1,arguments.length);if(ga(a))a=String(a);else if(!(a instanceof F))if(null===C(this.path)){var b=a;b&&(b=b.replace(/^\/*\.info(\/|$)/,"/"));Ia("Firebase.child",b)}else Ia("Firebase.child",a);return new E(this.m,this.path.G(a))};E.prototype.child=E.prototype.G;E.prototype.parent=function(){x("Firebase.parent",0,0,arguments.length);var a=this.path.parent();return null===a?null:new E(this.m,a)};E.prototype.parent=E.prototype.parent;
+E.prototype.root=function(){x("Firebase.ref",0,0,arguments.length);for(var a=this;null!==a.parent();)a=a.parent();return a};E.prototype.root=E.prototype.root;E.prototype.toString=function(){x("Firebase.toString",0,0,arguments.length);var a;if(null===this.parent())a=this.m.toString();else{a=this.parent().toString()+"/";var b=this.name();a+=encodeURIComponent(String(b))}return a};E.prototype.toString=E.prototype.toString;
+E.prototype.set=function(a,b){x("Firebase.set",1,2,arguments.length);B("Firebase.set",this.path);Aa("Firebase.set",a,!1);z("Firebase.set",2,b,!0);this.m.jb(this.path,a,null,b)};E.prototype.set=E.prototype.set;
+E.prototype.update=function(a,b){x("Firebase.update",1,2,arguments.length);B("Firebase.update",this.path);if(ea(a)){for(var c={},d=0;d\x3c/script>');
+return!0}return!1},goog.writeScripts_=function(){function a(g){if(!(g in f.written)){if(!(g in f.visited)&&(f.visited[g]=!0,g in f.requires))for(var k in f.requires[g])if(!goog.isProvided_(k))if(k in f.nameToPath)a(f.nameToPath[k]);else throw Error("Undefined nameToPath for "+k);g in e||(e[g]=!0,d.push(g))}}var d=[],e={},f=goog.dependencies_,g;for(g in goog.included_)f.written[g]||a(g);for(g=0;g>>0);goog.uidCounter_=0;goog.getHashCode=goog.getUid;goog.removeHashCode=goog.removeUid;goog.cloneObject=function(a){var d=goog.typeOf(a);if("object"==d||"array"==d){if(a.clone)return a.clone();var d="array"==d?[]:{},e;for(e in a)d[e]=goog.cloneObject(a[e]);return d}return a};goog.bindNative_=function(a,d,e){return a.call.apply(a.bind,arguments)};
+goog.bindJs_=function(a,d,e){if(!a)throw Error();if(2d?g+="000":256>d?g+="00":4096>d&&(g+="0");return goog.json.Serializer.charToJsonCharCache_[a]=g+d.toString(16)}),'"')};goog.json.Serializer.prototype.serializeNumber_=function(a,d){d.push(isFinite(a)&&!isNaN(a)?a:"null")};
+goog.json.Serializer.prototype.serializeArray=function(a,d){var e=a.length;d.push("[");for(var f="",g=0;gf;f++){var k=a[f];".."===k?d.pop():"."!==k&&d.push(k)}return d.join("/")}if(d[e])return d[e];if(d[e]={},!a[e])throw Error("Could not find module "+e);for(var g,h=a[e],k=h.deps,h=h.callback,l=[],n=0,q=k.length;q>n;n++)l.push("exports"===k[n]?g={}:c(f(k[n])));k=h.apply(this,l);return d[e]=g||k};c.entries=a}();
+b("rsvp/all-settled",["./promise","./utils","exports"],function(a,d,e){var f=a["default"],g=d.isArray,h=d.isNonThenable;e["default"]=function(a,d){return new f(function(d){function e(a){return function(d){m(a,{state:"fulfilled",value:d})}}function l(a){return function(d){m(a,{state:"rejected",reason:d})}}function m(a,e){u[a]=e;0===--r&&d(u)}if(!g(a))throw new TypeError("You must pass an array to allSettled.");var p,r=a.length;if(0===r)return void d([]);for(var u=Array(r),v=0;ve;e++)if(a[e]===d)return e;return-1}function e(a){var d=a._promiseCallbacks;return d||(d=a._promiseCallbacks={}),d}a["default"]={mixin:function(a){return a.on=this.on,a.off=this.off,a.trigger=this.trigger,a._promiseCallbacks=void 0,a},on:function(a,g){var h,k=e(this);(h=k[a])||(h=k[a]=[]);-1===d(h,g)&&h.push(g)},off:function(a,g){var h,k,l=e(this);return g?(h=l[a],k=d(h,g),void(-1!==k&&h.splice(k,1))):void(l[a]=[])},
+trigger:function(a,d){var h;if(h=e(this)[a])for(var k=0;km;m++)s[m]=d(a[m]);return f.all(s,e).then(function(d){for(var e=Array(h),f=0,g=0;h>g;g++)!0===d[g]&&(e[f]=a[g],f++);return e.length=f,e})})}});
+b("rsvp/hash-settled",["./promise","./utils","exports"],function(a,d,e){var f=a["default"],g=d.isNonThenable,h=d.keysOf;e["default"]=function(a){return new f(function(d){function e(a){return function(d){s(a,{state:"fulfilled",value:d})}}function q(a){return function(d){s(a,{state:"rejected",reason:d})}}function s(a,e){r[a]=e;0===--v&&d(r)}var m,p,r={},u=h(a),v=u.length;if(0===v)return void d(r);for(var t=0;tm;m++)s[m]=d(a[m]);return f.all(s,e)})}});
+b("rsvp/node",["./promise","./utils","exports"],function(a,d,e){var f=a["default"],g=d.isArray;e["default"]=function(a,d){function e(){for(var g=arguments.length,m=Array(g),l=0;g>l;l++)m[l]=arguments[l];var r;return n||q||!d?r=this:(console.warn('Deprecation: RSVP.denodeify() doesn\'t allow setting the "this" binding anymore. Use yourFunction.bind(yourThis) instead.'),r=d),f.all(m).then(function(e){return new f(function(f,g){e.push(function(){for(var a=arguments.length,e=Array(a),h=0;a>h;h++)e[h]=
+arguments[h];a=e[0];h=e[1];if(a)g(a);else if(n)f(e.slice(1));else if(q){for(var a={},l=e.slice(1),h=0;hd)?!0:!1};fb.simplelogin.util.env.isFennec=function(){try{var a=navigator.userAgent;return-1!=a.indexOf("Fennec/")||-1!=a.indexOf("Firefox/")&&-1!=a.indexOf("Android")}catch(d){}return!1};fb.simplelogin.transports.XHR_=function(){};
+fb.simplelogin.transports.XHR_.prototype.open=function(a,d,e){var f={contentType:"application/json"},g=new XMLHttpRequest,h=(f.method||"GET").toUpperCase(),k=f.contentType||"application/x-www-form-urlencoded",l=!1,n;g.onreadystatechange=function(){if(!l&&4===g.readyState){var a,d;l=!0;if(200<=g.status&&300>g.status||304==g.status||1223==g.status)try{a=fb.simplelogin.util.json.parse(g.responseText),d=a.error||null,delete a.error}catch(f){d="UNKNOWN_ERROR"}else d="RESPONSE_PAYLOAD_ERROR";return e&&
+e(d,a)}};d&&("GET"===h?(-1===a.indexOf("?")&&(a+="?"),a+=this.formatQueryString(d),d=null):("application/json"===k&&(d=fb.simplelogin.util.json.stringify(d)),"application/x-www-form-urlencoded"===k&&(d=this.formatQueryString(d))));g.open(h,a,!0);a={"X-Requested-With":"XMLHttpRequest",Accept:"application/json;text/plain","Content-Type":k};f.headers=f.headers||{};for(n in f.headers)a[n]=f.headers[n];for(n in a)g.setRequestHeader(n,a[n]);g.send(d)};
+fb.simplelogin.transports.XHR_.prototype.isAvailable=function(){return window.XMLHttpRequest&&"function"===typeof window.XMLHttpRequest&&!fb.simplelogin.util.env.isIeLT10()};fb.simplelogin.transports.XHR_.prototype.formatQueryString=function(a){if(!a)return"";var d=[],e;for(e in a)d.push(encodeURIComponent(e)+"="+encodeURIComponent(a[e]));return d.join("&")};fb.simplelogin.transports.XHR=new fb.simplelogin.transports.XHR_;fb.simplelogin.util.validation={};var VALID_EMAIL_REGEX_=/^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,6})+$/;fb.simplelogin.util.validation.validateArgCount=function(a,d,e,f){var g;fe&&(g=0===e?"none":"no more than "+e);if(g)throw Error(a+" failed: Was called with "+f+(1===f?" argument.":" arguments.")+" Expects "+g+".");};fb.simplelogin.util.validation.isValidEmail=function(a){return goog.isString(a)&&VALID_EMAIL_REGEX_.test(a)};
+fb.simplelogin.util.validation.isValidPassword=function(a){return goog.isString(a)};fb.simplelogin.util.validation.isValidNamespace=function(a){return goog.isString(a)};
+fb.simplelogin.util.validation.errorPrefix_=function(a,d,e){var f="";switch(d){case 1:f=e?"first":"First";break;case 2:f=e?"second":"Second";break;case 3:f=e?"third":"Third";break;case 4:f=e?"fourth":"Fourth";break;default:fb.core.util.validation.assert(!1,"errorPrefix_ called with argumentNumber > 4. Need to update it?")}return a=a+" failed: "+(f+" argument ")};
+fb.simplelogin.util.validation.validateNamespace=function(a,d,e,f){if((!f||goog.isDef(e))&&!goog.isString(e))throw Error(fb.simplelogin.util.validation.errorPrefix_(a,d,f)+"must be a valid firebase namespace.");};fb.simplelogin.util.validation.validateCallback=function(a,d,e,f){if((!f||goog.isDef(e))&&!goog.isFunction(e))throw Error(fb.simplelogin.util.validation.errorPrefix_(a,d,f)+"must be a valid function.");};
+fb.simplelogin.util.validation.validateString=function(a,d,e,f){if((!f||goog.isDef(e))&&!goog.isString(e))throw Error(fb.simplelogin.util.validation.errorPrefix_(a,d,f)+"must be a valid string.");};fb.simplelogin.util.validation.validateContextObject=function(a,d,e,f){if(!f||goog.isDef(e))if(!goog.isObject(e)||null===e)throw Error(fb.simplelogin.util.validation.errorPrefix_(a,d,f)+"must be a valid context object.");};var CALLBACK_NAMESPACE="_FirebaseSimpleLoginJSONP";fb.simplelogin.transports.JSONP_=function(){window[CALLBACK_NAMESPACE]=window[CALLBACK_NAMESPACE]||{}};fb.simplelogin.transports.JSONP_.prototype.open=function(a,d,e){a+=/\?/.test(a)?"":"?";a+="&transport=jsonp";for(var f in d)a+="&"+encodeURIComponent(f)+"="+encodeURIComponent(d[f]);d=this.generateRequestId_();a+="&callback="+encodeURIComponent(CALLBACK_NAMESPACE+"."+d);this.registerCallback_(d,e);this.writeScriptTag_(d,a,e)};
+fb.simplelogin.transports.JSONP_.prototype.generateRequestId_=function(){return"_FirebaseJSONP"+(new Date).getTime()+Math.floor(100*Math.random())};fb.simplelogin.transports.JSONP_.prototype.registerCallback_=function(a,d){var e=this;window[CALLBACK_NAMESPACE][a]=function(f){var g=f.error||null;delete f.error;d(g,f);e.removeCallback_(a)}};
+fb.simplelogin.transports.JSONP_.prototype.removeCallback_=function(a){setTimeout(function(){delete window[CALLBACK_NAMESPACE][a];var d=document.getElementById(a);d&&d.parentNode.removeChild(d)},0)};
+fb.simplelogin.transports.JSONP_.prototype.writeScriptTag_=function(a,d,e){var f=this;setTimeout(function(){try{var g=document.createElement("script");g.type="text/javascript";g.id=a;g.async=!0;g.src=d;g.onerror=function(){var d=document.getElementById(a);null!==d&&d.parentNode.removeChild(d);e&&e(f.formatError_({code:"SERVER_ERROR",message:"An unknown server error occurred."}))};document.getElementsByTagName("head")[0].appendChild(g)}catch(h){e&&e(f.formatError_({code:"SERVER_ERROR",message:"An unknown server error occurred."}))}},
+0)};fb.simplelogin.transports.JSONP_.prototype.formatError_=function(a){var d;a?(d=Error(a.message),d.code=a.code||"UNKNOWN_ERROR"):(d=Error(),d.code="UNKNOWN_ERROR");return d};fb.simplelogin.transports.JSONP=new fb.simplelogin.transports.JSONP_;fb.simplelogin.providers={};fb.simplelogin.providers.Password_=function(){};fb.simplelogin.providers.Password_.prototype.getTransport_=function(){return fb.simplelogin.transports.XHR.isAvailable()?fb.simplelogin.transports.XHR:fb.simplelogin.transports.JSONP};
+fb.simplelogin.providers.Password_.prototype.login=function(a,d){var e=fb.simplelogin.Vars.getApiHost()+"/auth/firebase";if(!fb.simplelogin.util.validation.isValidNamespace(a.firebase))return d&&d("INVALID_FIREBASE");this.getTransport_().open(e,a,d)};
+fb.simplelogin.providers.Password_.prototype.createUser=function(a,d){var e=fb.simplelogin.Vars.getApiHost()+"/auth/firebase/create";if(!fb.simplelogin.util.validation.isValidNamespace(a.firebase))return d&&d("INVALID_FIREBASE");if(!fb.simplelogin.util.validation.isValidEmail(a.email))return d&&d("INVALID_EMAIL");if(!fb.simplelogin.util.validation.isValidPassword(a.password))return d&&d("INVALID_PASSWORD");this.getTransport_().open(e,a,d)};
+fb.simplelogin.providers.Password_.prototype.changePassword=function(a,d){var e=fb.simplelogin.Vars.getApiHost()+"/auth/firebase/update";if(!fb.simplelogin.util.validation.isValidNamespace(a.firebase))return d&&d("INVALID_FIREBASE");if(!fb.simplelogin.util.validation.isValidEmail(a.email))return d&&d("INVALID_EMAIL");if(!fb.simplelogin.util.validation.isValidPassword(a.newPassword))return d&&d("INVALID_PASSWORD");this.getTransport_().open(e,a,d)};
+fb.simplelogin.providers.Password_.prototype.removeUser=function(a,d){var e=fb.simplelogin.Vars.getApiHost()+"/auth/firebase/remove";if(!fb.simplelogin.util.validation.isValidNamespace(a.firebase))return d&&d("INVALID_FIREBASE");if(!fb.simplelogin.util.validation.isValidEmail(a.email))return d&&d("INVALID_EMAIL");if(!fb.simplelogin.util.validation.isValidPassword(a.password))return d&&d("INVALID_PASSWORD");this.getTransport_().open(e,a,d)};
+fb.simplelogin.providers.Password_.prototype.sendPasswordResetEmail=function(a,d){var e=fb.simplelogin.Vars.getApiHost()+"/auth/firebase/reset_password";if(!fb.simplelogin.util.validation.isValidNamespace(a.firebase))return d&&d("INVALID_FIREBASE");if(!fb.simplelogin.util.validation.isValidEmail(a.email))return d&&d("INVALID_EMAIL");this.getTransport_().open(e,a,d)};fb.simplelogin.providers.Password=new fb.simplelogin.providers.Password_;fb.simplelogin.transports.WindowsMetroAuthBroker_=function(){};
+fb.simplelogin.transports.WindowsMetroAuthBroker_.prototype.open=function(a,d,e){var f,g,h,k,l,n;try{f=window.Windows.Foundation.Uri,g=window.Windows.Security.Authentication.Web.WebAuthenticationOptions,h=window.Windows.Security.Authentication.Web.WebAuthenticationBroker,k=h.authenticateAsync}catch(q){return e({code:"WINDOWS_METRO",message:'"Windows.Security.Authentication.Web.WebAuthenticationBroker" required when using Firebase Simple Login in Windows Metro context'})}l=!1;n=function(){var a=Array.prototype.slice.apply(arguments);
+l||(l=!0,e.apply(null,a))};a=new f(a+"&transport=internal-redirect-hash");f=new f(fb.simplelogin.Vars.getApiHost()+"/blank/page.html");k(g.none,a,f).done(function(a){var d;if(a&&a.responseData)try{var e=fb.simplelogin.util.misc.parseUrl(a.responseData),f=fb.simplelogin.util.misc.parseQuerystring(e.hash);a={};for(var g in f)a[g]=fb.simplelogin.util.json.parse(decodeURIComponent(f[g]));d=a}catch(h){}d&&d.token&&d.user?n(null,d):d&&d.error?n(d.error):n({code:"RESPONSE_PAYLOAD_ERROR",message:"Unable to parse response payload for Windows."})},
+function(a){n({code:"UNKNOWN_ERROR",message:"An unknown error occurred for Windows."})})};fb.simplelogin.transports.WindowsMetroAuthBroker=new fb.simplelogin.transports.WindowsMetroAuthBroker_;goog.string={};goog.string.Unicode={NBSP:"\u00a0"};goog.string.startsWith=function(a,d){return 0==a.lastIndexOf(d,0)};goog.string.endsWith=function(a,d){var e=a.length-d.length;return 0<=e&&a.indexOf(d,e)==e};goog.string.caseInsensitiveStartsWith=function(a,d){return 0==goog.string.caseInsensitiveCompare(d,a.substr(0,d.length))};goog.string.caseInsensitiveEndsWith=function(a,d){return 0==goog.string.caseInsensitiveCompare(d,a.substr(a.length-d.length,d.length))};
+goog.string.caseInsensitiveEquals=function(a,d){return a.toLowerCase()==d.toLowerCase()};goog.string.subs=function(a,d){for(var e=a.split("%s"),f="",g=Array.prototype.slice.call(arguments,1);g.length&&1=a||"\u0080"<=a&&"\ufffd">=a};goog.string.stripNewlines=function(a){return a.replace(/(\r\n|\r|\n)+/g," ")};
+goog.string.canonicalizeNewlines=function(a){return a.replace(/(\r\n|\r|\n)/g,"\n")};goog.string.normalizeWhitespace=function(a){return a.replace(/\xa0|\s/g," ")};goog.string.normalizeSpaces=function(a){return a.replace(/\xa0|[ \t]+/g," ")};goog.string.collapseBreakingSpaces=function(a){return a.replace(/[\t\r\n ]+/g," ").replace(/^[\t\r\n ]+|[\t\r\n ]+$/g,"")};goog.string.trim=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};
+goog.string.trimLeft=function(a){return a.replace(/^[\s\xa0]+/,"")};goog.string.trimRight=function(a){return a.replace(/[\s\xa0]+$/,"")};goog.string.caseInsensitiveCompare=function(a,d){var e=String(a).toLowerCase(),f=String(d).toLowerCase();return e":"
")};
+goog.string.htmlEscape=function(a,d){if(d)return a.replace(goog.string.amperRe_,"&").replace(goog.string.ltRe_,"<").replace(goog.string.gtRe_,">").replace(goog.string.quotRe_,""").replace(goog.string.singleQuoteRe_,"'");if(!goog.string.allRe_.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(goog.string.amperRe_,"&"));-1!=a.indexOf("<")&&(a=a.replace(goog.string.ltRe_,"<"));-1!=a.indexOf(">")&&(a=a.replace(goog.string.gtRe_,">"));-1!=a.indexOf('"')&&(a=a.replace(goog.string.quotRe_,
+"""));-1!=a.indexOf("'")&&(a=a.replace(goog.string.singleQuoteRe_,"'"));return a};goog.string.amperRe_=/&/g;goog.string.ltRe_=//g;goog.string.quotRe_=/"/g;goog.string.singleQuoteRe_=/'/g;goog.string.allRe_=/[&<>"']/;goog.string.unescapeEntities=function(a){return goog.string.contains(a,"&")?"document"in goog.global?goog.string.unescapeEntitiesUsingDom_(a):goog.string.unescapePureXmlEntities_(a):a};
+goog.string.unescapeEntitiesWithDocument=function(a,d){return goog.string.contains(a,"&")?goog.string.unescapeEntitiesUsingDom_(a,d):a};
+goog.string.unescapeEntitiesUsingDom_=function(a,d){var e={"&":"&","<":"<",">":">",""":'"'},f;f=d?d.createElement("div"):document.createElement("div");return a.replace(goog.string.HTML_ENTITY_PATTERN_,function(a,d){var k=e[a];if(k)return k;if("#"==d.charAt(0)){var l=Number("0"+d.substr(1));isNaN(l)||(k=String.fromCharCode(l))}k||(f.innerHTML=a+" ",k=f.firstChild.nodeValue.slice(0,-1));return e[a]=k})};
+goog.string.unescapePureXmlEntities_=function(a){return a.replace(/&([^;]+);/g,function(a,e){switch(e){case "amp":return"&";case "lt":return"<";case "gt":return">";case "quot":return'"';default:if("#"==e.charAt(0)){var f=Number("0"+e.substr(1));if(!isNaN(f))return String.fromCharCode(f)}return a}})};goog.string.HTML_ENTITY_PATTERN_=/&([^;\s<&]+);?/g;goog.string.whitespaceEscape=function(a,d){return goog.string.newLineToBr(a.replace(/ /g," "),d)};
+goog.string.stripQuotes=function(a,d){for(var e=d.length,f=0;fd&&(a=a.substring(0,d-3)+"...");e&&(a=goog.string.htmlEscape(a));return a};
+goog.string.truncateMiddle=function(a,d,e,f){e&&(a=goog.string.unescapeEntities(a));if(f&&a.length>d){f>d&&(f=d);var g=a.length-f;a=a.substring(0,d-f)+"..."+a.substring(g)}else a.length>d&&(f=Math.floor(d/2),g=a.length-f,a=a.substring(0,f+d%2)+"..."+a.substring(g));e&&(a=goog.string.htmlEscape(a));return a};goog.string.specialEscapeChars_={"\x00":"\\0","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\x0B":"\\x0B",'"':'\\"',"\\":"\\\\"};goog.string.jsEscapeCache_={"'":"\\'"};
+goog.string.quote=function(a){a=String(a);if(a.quote)return a.quote();for(var d=['"'],e=0;eg?f:goog.string.escapeChar(f))}d.push('"');return d.join("")};goog.string.escapeString=function(a){for(var d=[],e=0;ee)d=a;else{if(256>e){if(d="\\x",16>e||256e&&(d+="0");d+=e.toString(16).toUpperCase()}return goog.string.jsEscapeCache_[a]=d};goog.string.toMap=function(a){for(var d={},e=0;ed?1:0};goog.string.HASHCODE_MAX_=4294967296;goog.string.hashCode=function(a){for(var d=0,e=0;e)).");var g=fb.simplelogin.util.misc.parseSubdomain(a.toString());if(!goog.isString(g))throw Error("new FirebaseSimpleLogin(): First argument must be a valid Firebase reference (i.e. new Firebase()).");
+var h=new fb.simplelogin.client(a,d,e,f);return{setApiHost:function(a){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.setApiHost",1,1,arguments.length);return h.setApiHost(a)},login:function(){return h.login.apply(h,arguments)},logout:function(){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.logout",0,0,arguments.length);return h.logout()},createUser:function(a,d,e){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.createUser",2,3,arguments.length);
+fb.simplelogin.util.validation.validateCallback("FirebaseSimpleLogin.createUser",3,e,!0);return h.createUser(a,d,e)},changePassword:function(a,d,e,f){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.changePassword",3,4,arguments.length);fb.simplelogin.util.validation.validateCallback("FirebaseSimpleLogin.changePassword",4,f,!0);return h.changePassword(a,d,e,f)},removeUser:function(a,d,e){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.removeUser",2,3,arguments.length);
+fb.simplelogin.util.validation.validateCallback("FirebaseSimpleLogin.removeUser",3,e,!0);return h.removeUser(a,d,e)},sendPasswordResetEmail:function(a,d){fb.simplelogin.util.validation.validateArgCount("FirebaseSimpleLogin.sendPasswordResetEmail",1,2,arguments.length);fb.simplelogin.util.validation.validateCallback("FirebaseSimpleLogin.sendPasswordResetEmail",2,d,!0);return h.sendPasswordResetEmail(a,d)}}};goog.exportSymbol("FirebaseSimpleLogin",FirebaseSimpleLogin);FirebaseSimpleLogin.onOpen=function(a){fb.simplelogin.client.onOpen(a)};
+goog.exportProperty(FirebaseSimpleLogin,"onOpen",FirebaseSimpleLogin.onOpen);FirebaseSimpleLogin.VERSION=fb.simplelogin.client.VERSION();})();
+
+/** mockfirebase - v0.2.9
+https://github.com/katowulf/mockfirebase
+* Copyright (c) 2014 Kato
+* License: MIT */
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.mockfirebase=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>> 24)) & 0x00FF00FF |
+ ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00;
+ }
+
+ // Padding
+ m[l >>> 5] |= 0x80 << (l % 32);
+ m[(((l + 64) >>> 9) << 4) + 14] = l;
+
+ // Method shortcuts
+ var FF = md5._ff,
+ GG = md5._gg,
+ HH = md5._hh,
+ II = md5._ii;
+
+ for (var i = 0; i < m.length; i += 16) {
+
+ var aa = a,
+ bb = b,
+ cc = c,
+ dd = d;
+
+ a = FF(a, b, c, d, m[i+ 0], 7, -680876936);
+ d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
+ c = FF(c, d, a, b, m[i+ 2], 17, 606105819);
+ b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
+ a = FF(a, b, c, d, m[i+ 4], 7, -176418897);
+ d = FF(d, a, b, c, m[i+ 5], 12, 1200080426);
+ c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
+ b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
+ a = FF(a, b, c, d, m[i+ 8], 7, 1770035416);
+ d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
+ c = FF(c, d, a, b, m[i+10], 17, -42063);
+ b = FF(b, c, d, a, m[i+11], 22, -1990404162);
+ a = FF(a, b, c, d, m[i+12], 7, 1804603682);
+ d = FF(d, a, b, c, m[i+13], 12, -40341101);
+ c = FF(c, d, a, b, m[i+14], 17, -1502002290);
+ b = FF(b, c, d, a, m[i+15], 22, 1236535329);
+
+ a = GG(a, b, c, d, m[i+ 1], 5, -165796510);
+ d = GG(d, a, b, c, m[i+ 6], 9, -1069501632);
+ c = GG(c, d, a, b, m[i+11], 14, 643717713);
+ b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
+ a = GG(a, b, c, d, m[i+ 5], 5, -701558691);
+ d = GG(d, a, b, c, m[i+10], 9, 38016083);
+ c = GG(c, d, a, b, m[i+15], 14, -660478335);
+ b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
+ a = GG(a, b, c, d, m[i+ 9], 5, 568446438);
+ d = GG(d, a, b, c, m[i+14], 9, -1019803690);
+ c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
+ b = GG(b, c, d, a, m[i+ 8], 20, 1163531501);
+ a = GG(a, b, c, d, m[i+13], 5, -1444681467);
+ d = GG(d, a, b, c, m[i+ 2], 9, -51403784);
+ c = GG(c, d, a, b, m[i+ 7], 14, 1735328473);
+ b = GG(b, c, d, a, m[i+12], 20, -1926607734);
+
+ a = HH(a, b, c, d, m[i+ 5], 4, -378558);
+ d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
+ c = HH(c, d, a, b, m[i+11], 16, 1839030562);
+ b = HH(b, c, d, a, m[i+14], 23, -35309556);
+ a = HH(a, b, c, d, m[i+ 1], 4, -1530992060);
+ d = HH(d, a, b, c, m[i+ 4], 11, 1272893353);
+ c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
+ b = HH(b, c, d, a, m[i+10], 23, -1094730640);
+ a = HH(a, b, c, d, m[i+13], 4, 681279174);
+ d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
+ c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
+ b = HH(b, c, d, a, m[i+ 6], 23, 76029189);
+ a = HH(a, b, c, d, m[i+ 9], 4, -640364487);
+ d = HH(d, a, b, c, m[i+12], 11, -421815835);
+ c = HH(c, d, a, b, m[i+15], 16, 530742520);
+ b = HH(b, c, d, a, m[i+ 2], 23, -995338651);
+
+ a = II(a, b, c, d, m[i+ 0], 6, -198630844);
+ d = II(d, a, b, c, m[i+ 7], 10, 1126891415);
+ c = II(c, d, a, b, m[i+14], 15, -1416354905);
+ b = II(b, c, d, a, m[i+ 5], 21, -57434055);
+ a = II(a, b, c, d, m[i+12], 6, 1700485571);
+ d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
+ c = II(c, d, a, b, m[i+10], 15, -1051523);
+ b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
+ a = II(a, b, c, d, m[i+ 8], 6, 1873313359);
+ d = II(d, a, b, c, m[i+15], 10, -30611744);
+ c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
+ b = II(b, c, d, a, m[i+13], 21, 1309151649);
+ a = II(a, b, c, d, m[i+ 4], 6, -145523070);
+ d = II(d, a, b, c, m[i+11], 10, -1120210379);
+ c = II(c, d, a, b, m[i+ 2], 15, 718787259);
+ b = II(b, c, d, a, m[i+ 9], 21, -343485551);
+
+ a = (a + aa) >>> 0;
+ b = (b + bb) >>> 0;
+ c = (c + cc) >>> 0;
+ d = (d + dd) >>> 0;
+ }
+
+ return crypt.endian([a, b, c, d]);
+ };
+
+ // Auxiliary functions
+ md5._ff = function (a, b, c, d, x, s, t) {
+ var n = a + (b & c | ~b & d) + (x >>> 0) + t;
+ return ((n << s) | (n >>> (32 - s))) + b;
+ };
+ md5._gg = function (a, b, c, d, x, s, t) {
+ var n = a + (b & d | c & ~d) + (x >>> 0) + t;
+ return ((n << s) | (n >>> (32 - s))) + b;
+ };
+ md5._hh = function (a, b, c, d, x, s, t) {
+ var n = a + (b ^ c ^ d) + (x >>> 0) + t;
+ return ((n << s) | (n >>> (32 - s))) + b;
+ };
+ md5._ii = function (a, b, c, d, x, s, t) {
+ var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
+ return ((n << s) | (n >>> (32 - s))) + b;
+ };
+
+ // Package private blocksize
+ md5._blocksize = 16;
+ md5._digestsize = 16;
+
+ module.exports = function (message, options) {
+ if(typeof message == 'undefined')
+ return;
+
+ var digestbytes = crypt.wordsToBytes(md5(message, options));
+ return options && options.asBytes ? digestbytes :
+ options && options.asString ? bin.bytesToString(digestbytes) :
+ crypt.bytesToHex(digestbytes);
+ };
+
+})();
+
+}).call(this,_dereq_("buffer").Buffer)
+},{"buffer":4,"charenc":2,"crypt":3}],2:[function(_dereq_,module,exports){
+var charenc = {
+ // UTF-8 encoding
+ utf8: {
+ // Convert a string to a byte array
+ stringToBytes: function(str) {
+ return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
+ },
+
+ // Convert a byte array to a string
+ bytesToString: function(bytes) {
+ return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
+ }
+ },
+
+ // Binary encoding
+ bin: {
+ // Convert a string to a byte array
+ stringToBytes: function(str) {
+ for (var bytes = [], i = 0; i < str.length; i++)
+ bytes.push(str.charCodeAt(i) & 0xFF);
+ return bytes;
+ },
+
+ // Convert a byte array to a string
+ bytesToString: function(bytes) {
+ for (var str = [], i = 0; i < bytes.length; i++)
+ str.push(String.fromCharCode(bytes[i]));
+ return str.join('');
+ }
+ }
+};
+
+module.exports = charenc;
+
+},{}],3:[function(_dereq_,module,exports){
+(function() {
+ var base64map
+ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
+
+ crypt = {
+ // Bit-wise rotation left
+ rotl: function(n, b) {
+ return (n << b) | (n >>> (32 - b));
+ },
+
+ // Bit-wise rotation right
+ rotr: function(n, b) {
+ return (n << (32 - b)) | (n >>> b);
+ },
+
+ // Swap big-endian to little-endian and vice versa
+ endian: function(n) {
+ // If number given, swap endian
+ if (n.constructor == Number) {
+ return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
+ }
+
+ // Else, assume array and swap all items
+ for (var i = 0; i < n.length; i++)
+ n[i] = crypt.endian(n[i]);
+ return n;
+ },
+
+ // Generate an array of any length of random bytes
+ randomBytes: function(n) {
+ for (var bytes = []; n > 0; n--)
+ bytes.push(Math.floor(Math.random() * 256));
+ return bytes;
+ },
+
+ // Convert a byte array to big-endian 32-bit words
+ bytesToWords: function(bytes) {
+ for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
+ words[b >>> 5] |= bytes[i] << (24 - b % 32);
+ return words;
+ },
+
+ // Convert big-endian 32-bit words to a byte array
+ wordsToBytes: function(words) {
+ for (var bytes = [], b = 0; b < words.length * 32; b += 8)
+ bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
+ return bytes;
+ },
+
+ // Convert a byte array to a hex string
+ bytesToHex: function(bytes) {
+ for (var hex = [], i = 0; i < bytes.length; i++) {
+ hex.push((bytes[i] >>> 4).toString(16));
+ hex.push((bytes[i] & 0xF).toString(16));
+ }
+ return hex.join('');
+ },
+
+ // Convert a hex string to a byte array
+ hexToBytes: function(hex) {
+ for (var bytes = [], c = 0; c < hex.length; c += 2)
+ bytes.push(parseInt(hex.substr(c, 2), 16));
+ return bytes;
+ },
+
+ // Convert a byte array to a base-64 string
+ bytesToBase64: function(bytes) {
+ for (var base64 = [], i = 0; i < bytes.length; i += 3) {
+ var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
+ for (var j = 0; j < 4; j++)
+ if (i * 8 + j * 6 <= bytes.length * 8)
+ base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
+ else
+ base64.push('=');
+ }
+ return base64.join('');
+ },
+
+ // Convert a base-64 string to a byte array
+ base64ToBytes: function(base64) {
+ // Remove non-base-64 characters
+ base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');
+
+ for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
+ imod4 = ++i % 4) {
+ if (imod4 == 0) continue;
+ bytes.push(((base64map.indexOf(base64.charAt(i - 1))
+ & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
+ | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
+ }
+ return bytes;
+ }
+ };
+
+ module.exports = crypt;
+})();
+
+},{}],4:[function(_dereq_,module,exports){
+/*!
+ * The buffer module from node.js, for the browser.
+ *
+ * @author Feross Aboukhadijeh
+ * @license MIT
+ */
+
+var base64 = _dereq_('base64-js')
+var ieee754 = _dereq_('ieee754')
+
+exports.Buffer = Buffer
+exports.SlowBuffer = Buffer
+exports.INSPECT_MAX_BYTES = 50
+Buffer.poolSize = 8192
+
+/**
+ * If `TYPED_ARRAY_SUPPORT`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Use Object implementation (most compatible, even IE6)
+ *
+ * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
+ * Opera 11.6+, iOS 4.2+.
+ *
+ * Note:
+ *
+ * - Implementation must support adding new properties to `Uint8Array` instances.
+ * Firefox 4-29 lacked support, fixed in Firefox 30+.
+ * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
+ *
+ * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
+ *
+ * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
+ * incorrect length in some situations.
+ *
+ * We detect these buggy browsers and set `TYPED_ARRAY_SUPPORT` to `false` so they will
+ * get the Object implementation, which is slower but will work correctly.
+ */
+var TYPED_ARRAY_SUPPORT = (function () {
+ try {
+ var buf = new ArrayBuffer(0)
+ var arr = new Uint8Array(buf)
+ arr.foo = function () { return 42 }
+ return 42 === arr.foo() && // typed array instances can be augmented
+ typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
+ new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
+ } catch (e) {
+ return false
+ }
+})()
+
+/**
+ * Class: Buffer
+ * =============
+ *
+ * The Buffer constructor returns instances of `Uint8Array` that are augmented
+ * with function properties for all the node `Buffer` API functions. We use
+ * `Uint8Array` so that square bracket notation works as expected -- it returns
+ * a single octet.
+ *
+ * By augmenting the instances, we can avoid modifying the `Uint8Array`
+ * prototype.
+ */
+function Buffer (subject, encoding, noZero) {
+ if (!(this instanceof Buffer))
+ return new Buffer(subject, encoding, noZero)
+
+ var type = typeof subject
+
+ // Find the length
+ var length
+ if (type === 'number')
+ length = subject > 0 ? subject >>> 0 : 0
+ else if (type === 'string') {
+ if (encoding === 'base64')
+ subject = base64clean(subject)
+ length = Buffer.byteLength(subject, encoding)
+ } else if (type === 'object' && subject !== null) { // assume object is array-like
+ if (subject.type === 'Buffer' && isArray(subject.data))
+ subject = subject.data
+ length = +subject.length > 0 ? Math.floor(+subject.length) : 0
+ } else
+ throw new Error('First argument needs to be a number, array or string.')
+
+ var buf
+ if (TYPED_ARRAY_SUPPORT) {
+ // Preferred: Return an augmented `Uint8Array` instance for best performance
+ buf = Buffer._augment(new Uint8Array(length))
+ } else {
+ // Fallback: Return THIS instance of Buffer (created by `new`)
+ buf = this
+ buf.length = length
+ buf._isBuffer = true
+ }
+
+ var i
+ if (TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
+ // Speed optimization -- use set if we're copying from a typed array
+ buf._set(subject)
+ } else if (isArrayish(subject)) {
+ // Treat array-ish objects as a byte array
+ if (Buffer.isBuffer(subject)) {
+ for (i = 0; i < length; i++)
+ buf[i] = subject.readUInt8(i)
+ } else {
+ for (i = 0; i < length; i++)
+ buf[i] = ((subject[i] % 256) + 256) % 256
+ }
+ } else if (type === 'string') {
+ buf.write(subject, 0, encoding)
+ } else if (type === 'number' && !TYPED_ARRAY_SUPPORT && !noZero) {
+ for (i = 0; i < length; i++) {
+ buf[i] = 0
+ }
+ }
+
+ return buf
+}
+
+// STATIC METHODS
+// ==============
+
+Buffer.isEncoding = function (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'binary':
+ case 'base64':
+ case 'raw':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.isBuffer = function (b) {
+ return !!(b != null && b._isBuffer)
+}
+
+Buffer.byteLength = function (str, encoding) {
+ var ret
+ str = str.toString()
+ switch (encoding || 'utf8') {
+ case 'hex':
+ ret = str.length / 2
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = utf8ToBytes(str).length
+ break
+ case 'ascii':
+ case 'binary':
+ case 'raw':
+ ret = str.length
+ break
+ case 'base64':
+ ret = base64ToBytes(str).length
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = str.length * 2
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.concat = function (list, totalLength) {
+ assert(isArray(list), 'Usage: Buffer.concat(list[, length])')
+
+ if (list.length === 0) {
+ return new Buffer(0)
+ } else if (list.length === 1) {
+ return list[0]
+ }
+
+ var i
+ if (totalLength === undefined) {
+ totalLength = 0
+ for (i = 0; i < list.length; i++) {
+ totalLength += list[i].length
+ }
+ }
+
+ var buf = new Buffer(totalLength)
+ var pos = 0
+ for (i = 0; i < list.length; i++) {
+ var item = list[i]
+ item.copy(buf, pos)
+ pos += item.length
+ }
+ return buf
+}
+
+Buffer.compare = function (a, b) {
+ assert(Buffer.isBuffer(a) && Buffer.isBuffer(b), 'Arguments must be Buffers')
+ var x = a.length
+ var y = b.length
+ for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
+ if (i !== len) {
+ x = a[i]
+ y = b[i]
+ }
+ if (x < y) {
+ return -1
+ }
+ if (y < x) {
+ return 1
+ }
+ return 0
+}
+
+// BUFFER INSTANCE METHODS
+// =======================
+
+function hexWrite (buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ // must be an even number of digits
+ var strLen = string.length
+ assert(strLen % 2 === 0, 'Invalid hex string')
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; i++) {
+ var byte = parseInt(string.substr(i * 2, 2), 16)
+ assert(!isNaN(byte), 'Invalid hex string')
+ buf[offset + i] = byte
+ }
+ return i
+}
+
+function utf8Write (buf, string, offset, length) {
+ var charsWritten = blitBuffer(utf8ToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+function asciiWrite (buf, string, offset, length) {
+ var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+function binaryWrite (buf, string, offset, length) {
+ return asciiWrite(buf, string, offset, length)
+}
+
+function base64Write (buf, string, offset, length) {
+ var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+function utf16leWrite (buf, string, offset, length) {
+ var charsWritten = blitBuffer(utf16leToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.prototype.write = function (string, offset, length, encoding) {
+ // Support both (string, offset, length, encoding)
+ // and the legacy (string, encoding, offset, length)
+ if (isFinite(offset)) {
+ if (!isFinite(length)) {
+ encoding = length
+ length = undefined
+ }
+ } else { // legacy
+ var swap = encoding
+ encoding = offset
+ offset = length
+ length = swap
+ }
+
+ offset = Number(offset) || 0
+ var remaining = this.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+ encoding = String(encoding || 'utf8').toLowerCase()
+
+ var ret
+ switch (encoding) {
+ case 'hex':
+ ret = hexWrite(this, string, offset, length)
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = utf8Write(this, string, offset, length)
+ break
+ case 'ascii':
+ ret = asciiWrite(this, string, offset, length)
+ break
+ case 'binary':
+ ret = binaryWrite(this, string, offset, length)
+ break
+ case 'base64':
+ ret = base64Write(this, string, offset, length)
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = utf16leWrite(this, string, offset, length)
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.prototype.toString = function (encoding, start, end) {
+ var self = this
+
+ encoding = String(encoding || 'utf8').toLowerCase()
+ start = Number(start) || 0
+ end = (end === undefined) ? self.length : Number(end)
+
+ // Fastpath empty strings
+ if (end === start)
+ return ''
+
+ var ret
+ switch (encoding) {
+ case 'hex':
+ ret = hexSlice(self, start, end)
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = utf8Slice(self, start, end)
+ break
+ case 'ascii':
+ ret = asciiSlice(self, start, end)
+ break
+ case 'binary':
+ ret = binarySlice(self, start, end)
+ break
+ case 'base64':
+ ret = base64Slice(self, start, end)
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = utf16leSlice(self, start, end)
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.prototype.toJSON = function () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+Buffer.prototype.equals = function (b) {
+ assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.compare = function (b) {
+ assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
+ return Buffer.compare(this, b)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function (target, target_start, start, end) {
+ var source = this
+
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (!target_start) target_start = 0
+
+ // Copy 0 bytes; we're done
+ if (end === start) return
+ if (target.length === 0 || source.length === 0) return
+
+ // Fatal error conditions
+ assert(end >= start, 'sourceEnd < sourceStart')
+ assert(target_start >= 0 && target_start < target.length,
+ 'targetStart out of bounds')
+ assert(start >= 0 && start < source.length, 'sourceStart out of bounds')
+ assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length)
+ end = this.length
+ if (target.length - target_start < end - start)
+ end = target.length - target_start + start
+
+ var len = end - start
+
+ if (len < 100 || !TYPED_ARRAY_SUPPORT) {
+ for (var i = 0; i < len; i++) {
+ target[i + target_start] = this[i + start]
+ }
+ } else {
+ target._set(this.subarray(start, start + len), target_start)
+ }
+}
+
+function base64Slice (buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+function utf8Slice (buf, start, end) {
+ var res = ''
+ var tmp = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ if (buf[i] <= 0x7F) {
+ res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
+ tmp = ''
+ } else {
+ tmp += '%' + buf[i].toString(16)
+ }
+ }
+
+ return res + decodeUtf8Char(tmp)
+}
+
+function asciiSlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+function binarySlice (buf, start, end) {
+ return asciiSlice(buf, start, end)
+}
+
+function hexSlice (buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; i++) {
+ out += toHex(buf[i])
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ for (var i = 0; i < bytes.length; i += 2) {
+ res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
+ }
+ return res
+}
+
+Buffer.prototype.slice = function (start, end) {
+ var len = this.length
+ start = ~~start
+ end = end === undefined ? len : ~~end
+
+ if (start < 0) {
+ start += len;
+ if (start < 0)
+ start = 0
+ } else if (start > len) {
+ start = len
+ }
+
+ if (end < 0) {
+ end += len
+ if (end < 0)
+ end = 0
+ } else if (end > len) {
+ end = len
+ }
+
+ if (end < start)
+ end = start
+
+ if (TYPED_ARRAY_SUPPORT) {
+ return Buffer._augment(this.subarray(start, end))
+ } else {
+ var sliceLen = end - start
+ var newBuf = new Buffer(sliceLen, undefined, true)
+ for (var i = 0; i < sliceLen; i++) {
+ newBuf[i] = this[i + start]
+ }
+ return newBuf
+ }
+}
+
+// `get` will be removed in Node 0.13+
+Buffer.prototype.get = function (offset) {
+ console.log('.get() is deprecated. Access using array indexes instead.')
+ return this.readUInt8(offset)
+}
+
+// `set` will be removed in Node 0.13+
+Buffer.prototype.set = function (v, offset) {
+ console.log('.set() is deprecated. Access using array indexes instead.')
+ return this.writeUInt8(v, offset)
+}
+
+Buffer.prototype.readUInt8 = function (offset, noAssert) {
+ if (!noAssert) {
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset < this.length, 'Trying to read beyond buffer length')
+ }
+
+ if (offset >= this.length)
+ return
+
+ return this[offset]
+}
+
+function readUInt16 (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val
+ if (littleEndian) {
+ val = buf[offset]
+ if (offset + 1 < len)
+ val |= buf[offset + 1] << 8
+ } else {
+ val = buf[offset] << 8
+ if (offset + 1 < len)
+ val |= buf[offset + 1]
+ }
+ return val
+}
+
+Buffer.prototype.readUInt16LE = function (offset, noAssert) {
+ return readUInt16(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readUInt16BE = function (offset, noAssert) {
+ return readUInt16(this, offset, false, noAssert)
+}
+
+function readUInt32 (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val
+ if (littleEndian) {
+ if (offset + 2 < len)
+ val = buf[offset + 2] << 16
+ if (offset + 1 < len)
+ val |= buf[offset + 1] << 8
+ val |= buf[offset]
+ if (offset + 3 < len)
+ val = val + (buf[offset + 3] << 24 >>> 0)
+ } else {
+ if (offset + 1 < len)
+ val = buf[offset + 1] << 16
+ if (offset + 2 < len)
+ val |= buf[offset + 2] << 8
+ if (offset + 3 < len)
+ val |= buf[offset + 3]
+ val = val + (buf[offset] << 24 >>> 0)
+ }
+ return val
+}
+
+Buffer.prototype.readUInt32LE = function (offset, noAssert) {
+ return readUInt32(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readUInt32BE = function (offset, noAssert) {
+ return readUInt32(this, offset, false, noAssert)
+}
+
+Buffer.prototype.readInt8 = function (offset, noAssert) {
+ if (!noAssert) {
+ assert(offset !== undefined && offset !== null,
+ 'missing offset')
+ assert(offset < this.length, 'Trying to read beyond buffer length')
+ }
+
+ if (offset >= this.length)
+ return
+
+ var neg = this[offset] & 0x80
+ if (neg)
+ return (0xff - this[offset] + 1) * -1
+ else
+ return this[offset]
+}
+
+function readInt16 (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val = readUInt16(buf, offset, littleEndian, true)
+ var neg = val & 0x8000
+ if (neg)
+ return (0xffff - val + 1) * -1
+ else
+ return val
+}
+
+Buffer.prototype.readInt16LE = function (offset, noAssert) {
+ return readInt16(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readInt16BE = function (offset, noAssert) {
+ return readInt16(this, offset, false, noAssert)
+}
+
+function readInt32 (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val = readUInt32(buf, offset, littleEndian, true)
+ var neg = val & 0x80000000
+ if (neg)
+ return (0xffffffff - val + 1) * -1
+ else
+ return val
+}
+
+Buffer.prototype.readInt32LE = function (offset, noAssert) {
+ return readInt32(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readInt32BE = function (offset, noAssert) {
+ return readInt32(this, offset, false, noAssert)
+}
+
+function readFloat (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ return ieee754.read(buf, offset, littleEndian, 23, 4)
+}
+
+Buffer.prototype.readFloatLE = function (offset, noAssert) {
+ return readFloat(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readFloatBE = function (offset, noAssert) {
+ return readFloat(this, offset, false, noAssert)
+}
+
+function readDouble (buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset + 7 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ return ieee754.read(buf, offset, littleEndian, 52, 8)
+}
+
+Buffer.prototype.readDoubleLE = function (offset, noAssert) {
+ return readDouble(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readDoubleBE = function (offset, noAssert) {
+ return readDouble(this, offset, false, noAssert)
+}
+
+Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset < this.length, 'trying to write beyond buffer length')
+ verifuint(value, 0xff)
+ }
+
+ if (offset >= this.length) return
+
+ this[offset] = value
+ return offset + 1
+}
+
+function writeUInt16 (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 1 < buf.length, 'trying to write beyond buffer length')
+ verifuint(value, 0xffff)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) {
+ buf[offset + i] =
+ (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
+ (littleEndian ? i : 1 - i) * 8
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
+ return writeUInt16(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
+ return writeUInt16(this, value, offset, false, noAssert)
+}
+
+function writeUInt32 (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 3 < buf.length, 'trying to write beyond buffer length')
+ verifuint(value, 0xffffffff)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) {
+ buf[offset + i] =
+ (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
+ return writeUInt32(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
+ return writeUInt32(this, value, offset, false, noAssert)
+}
+
+Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset < this.length, 'Trying to write beyond buffer length')
+ verifsint(value, 0x7f, -0x80)
+ }
+
+ if (offset >= this.length)
+ return
+
+ if (value >= 0)
+ this.writeUInt8(value, offset, noAssert)
+ else
+ this.writeUInt8(0xff + value + 1, offset, noAssert)
+ return offset + 1
+}
+
+function writeInt16 (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 1 < buf.length, 'Trying to write beyond buffer length')
+ verifsint(value, 0x7fff, -0x8000)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ if (value >= 0)
+ writeUInt16(buf, value, offset, littleEndian, noAssert)
+ else
+ writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
+ return writeInt16(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
+ return writeInt16(this, value, offset, false, noAssert)
+}
+
+function writeInt32 (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
+ verifsint(value, 0x7fffffff, -0x80000000)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ if (value >= 0)
+ writeUInt32(buf, value, offset, littleEndian, noAssert)
+ else
+ writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert)
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
+ return writeInt32(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
+ return writeInt32(this, value, offset, false, noAssert)
+}
+
+function writeFloat (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
+ verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
+ return writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
+ return writeFloat(this, value, offset, false, noAssert)
+}
+
+function writeDouble (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ assert(value !== undefined && value !== null, 'missing value')
+ assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ assert(offset !== undefined && offset !== null, 'missing offset')
+ assert(offset + 7 < buf.length,
+ 'Trying to write beyond buffer length')
+ verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
+ return writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
+ return writeDouble(this, value, offset, false, noAssert)
+}
+
+// fill(value, start=0, end=buffer.length)
+Buffer.prototype.fill = function (value, start, end) {
+ if (!value) value = 0
+ if (!start) start = 0
+ if (!end) end = this.length
+
+ assert(end >= start, 'end < start')
+
+ // Fill 0 bytes; we're done
+ if (end === start) return
+ if (this.length === 0) return
+
+ assert(start >= 0 && start < this.length, 'start out of bounds')
+ assert(end >= 0 && end <= this.length, 'end out of bounds')
+
+ var i
+ if (typeof value === 'number') {
+ for (i = start; i < end; i++) {
+ this[i] = value
+ }
+ } else {
+ var bytes = utf8ToBytes(value.toString())
+ var len = bytes.length
+ for (i = start; i < end; i++) {
+ this[i] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+Buffer.prototype.inspect = function () {
+ var out = []
+ var len = this.length
+ for (var i = 0; i < len; i++) {
+ out[i] = toHex(this[i])
+ if (i === exports.INSPECT_MAX_BYTES) {
+ out[i + 1] = '...'
+ break
+ }
+ }
+ return ''
+}
+
+/**
+ * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
+ * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
+ */
+Buffer.prototype.toArrayBuffer = function () {
+ if (typeof Uint8Array !== 'undefined') {
+ if (TYPED_ARRAY_SUPPORT) {
+ return (new Buffer(this)).buffer
+ } else {
+ var buf = new Uint8Array(this.length)
+ for (var i = 0, len = buf.length; i < len; i += 1) {
+ buf[i] = this[i]
+ }
+ return buf.buffer
+ }
+ } else {
+ throw new Error('Buffer.toArrayBuffer not supported in this browser')
+ }
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var BP = Buffer.prototype
+
+/**
+ * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
+ */
+Buffer._augment = function (arr) {
+ arr._isBuffer = true
+
+ // save reference to original Uint8Array get/set methods before overwriting
+ arr._get = arr.get
+ arr._set = arr.set
+
+ // deprecated, will be removed in node 0.13+
+ arr.get = BP.get
+ arr.set = BP.set
+
+ arr.write = BP.write
+ arr.toString = BP.toString
+ arr.toLocaleString = BP.toString
+ arr.toJSON = BP.toJSON
+ arr.equals = BP.equals
+ arr.compare = BP.compare
+ arr.copy = BP.copy
+ arr.slice = BP.slice
+ arr.readUInt8 = BP.readUInt8
+ arr.readUInt16LE = BP.readUInt16LE
+ arr.readUInt16BE = BP.readUInt16BE
+ arr.readUInt32LE = BP.readUInt32LE
+ arr.readUInt32BE = BP.readUInt32BE
+ arr.readInt8 = BP.readInt8
+ arr.readInt16LE = BP.readInt16LE
+ arr.readInt16BE = BP.readInt16BE
+ arr.readInt32LE = BP.readInt32LE
+ arr.readInt32BE = BP.readInt32BE
+ arr.readFloatLE = BP.readFloatLE
+ arr.readFloatBE = BP.readFloatBE
+ arr.readDoubleLE = BP.readDoubleLE
+ arr.readDoubleBE = BP.readDoubleBE
+ arr.writeUInt8 = BP.writeUInt8
+ arr.writeUInt16LE = BP.writeUInt16LE
+ arr.writeUInt16BE = BP.writeUInt16BE
+ arr.writeUInt32LE = BP.writeUInt32LE
+ arr.writeUInt32BE = BP.writeUInt32BE
+ arr.writeInt8 = BP.writeInt8
+ arr.writeInt16LE = BP.writeInt16LE
+ arr.writeInt16BE = BP.writeInt16BE
+ arr.writeInt32LE = BP.writeInt32LE
+ arr.writeInt32BE = BP.writeInt32BE
+ arr.writeFloatLE = BP.writeFloatLE
+ arr.writeFloatBE = BP.writeFloatBE
+ arr.writeDoubleLE = BP.writeDoubleLE
+ arr.writeDoubleBE = BP.writeDoubleBE
+ arr.fill = BP.fill
+ arr.inspect = BP.inspect
+ arr.toArrayBuffer = BP.toArrayBuffer
+
+ return arr
+}
+
+var INVALID_BASE64_RE = /[^+\/0-9A-z]/g
+
+function base64clean (str) {
+ // Node strips out invalid characters like \n and \t from the string, base64-js does not
+ str = stringtrim(str).replace(INVALID_BASE64_RE, '')
+ // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
+ while (str.length % 4 !== 0) {
+ str = str + '='
+ }
+ return str
+}
+
+function stringtrim (str) {
+ if (str.trim) return str.trim()
+ return str.replace(/^\s+|\s+$/g, '')
+}
+
+function isArray (subject) {
+ return (Array.isArray || function (subject) {
+ return Object.prototype.toString.call(subject) === '[object Array]'
+ })(subject)
+}
+
+function isArrayish (subject) {
+ return isArray(subject) || Buffer.isBuffer(subject) ||
+ subject && typeof subject === 'object' &&
+ typeof subject.length === 'number'
+}
+
+function toHex (n) {
+ if (n < 16) return '0' + n.toString(16)
+ return n.toString(16)
+}
+
+function utf8ToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ var b = str.charCodeAt(i)
+ if (b <= 0x7F) {
+ byteArray.push(b)
+ } else {
+ var start = i
+ if (b >= 0xD800 && b <= 0xDFFF) i++
+ var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%')
+ for (var j = 0; j < h.length; j++) {
+ byteArray.push(parseInt(h[j], 16))
+ }
+ }
+ }
+ return byteArray
+}
+
+function asciiToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+function utf16leToBytes (str) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+function base64ToBytes (str) {
+ return base64.toByteArray(str)
+}
+
+function blitBuffer (src, dst, offset, length) {
+ for (var i = 0; i < length; i++) {
+ if ((i + offset >= dst.length) || (i >= src.length))
+ break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+function decodeUtf8Char (str) {
+ try {
+ return decodeURIComponent(str)
+ } catch (err) {
+ return String.fromCharCode(0xFFFD) // UTF 8 invalid char
+ }
+}
+
+/*
+ * We have to make sure that the value is a valid integer. This means that it
+ * is non-negative. It has no fractional component and that it does not
+ * exceed the maximum allowed value.
+ */
+function verifuint (value, max) {
+ assert(typeof value === 'number', 'cannot write a non-number as a number')
+ assert(value >= 0, 'specified a negative value for writing an unsigned value')
+ assert(value <= max, 'value is larger than maximum value for type')
+ assert(Math.floor(value) === value, 'value has a fractional component')
+}
+
+function verifsint (value, max, min) {
+ assert(typeof value === 'number', 'cannot write a non-number as a number')
+ assert(value <= max, 'value larger than maximum allowed value')
+ assert(value >= min, 'value smaller than minimum allowed value')
+ assert(Math.floor(value) === value, 'value has a fractional component')
+}
+
+function verifIEEE754 (value, max, min) {
+ assert(typeof value === 'number', 'cannot write a non-number as a number')
+ assert(value <= max, 'value larger than maximum allowed value')
+ assert(value >= min, 'value smaller than minimum allowed value')
+}
+
+function assert (test, message) {
+ if (!test) throw new Error(message || 'Failed assertion')
+}
+
+},{"base64-js":5,"ieee754":6}],5:[function(_dereq_,module,exports){
+var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+;(function (exports) {
+ 'use strict';
+
+ var Arr = (typeof Uint8Array !== 'undefined')
+ ? Uint8Array
+ : Array
+
+ var PLUS = '+'.charCodeAt(0)
+ var SLASH = '/'.charCodeAt(0)
+ var NUMBER = '0'.charCodeAt(0)
+ var LOWER = 'a'.charCodeAt(0)
+ var UPPER = 'A'.charCodeAt(0)
+
+ function decode (elt) {
+ var code = elt.charCodeAt(0)
+ if (code === PLUS)
+ return 62 // '+'
+ if (code === SLASH)
+ return 63 // '/'
+ if (code < NUMBER)
+ return -1 //no match
+ if (code < NUMBER + 10)
+ return code - NUMBER + 26 + 26
+ if (code < UPPER + 26)
+ return code - UPPER
+ if (code < LOWER + 26)
+ return code - LOWER + 26
+ }
+
+ function b64ToByteArray (b64) {
+ var i, j, l, tmp, placeHolders, arr
+
+ if (b64.length % 4 > 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // the number of equal signs (place holders)
+ // if there are two placeholders, than the two characters before it
+ // represent one byte
+ // if there is only one, then the three characters before it represent 2 bytes
+ // this is just a cheap hack to not do indexOf twice
+ var len = b64.length
+ placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
+
+ // base64 is 4/3 + up to two characters of the original data
+ arr = new Arr(b64.length * 3 / 4 - placeHolders)
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ l = placeHolders > 0 ? b64.length - 4 : b64.length
+
+ var L = 0
+
+ function push (v) {
+ arr[L++] = v
+ }
+
+ for (i = 0, j = 0; i < l; i += 4, j += 3) {
+ tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
+ push((tmp & 0xFF0000) >> 16)
+ push((tmp & 0xFF00) >> 8)
+ push(tmp & 0xFF)
+ }
+
+ if (placeHolders === 2) {
+ tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
+ push(tmp & 0xFF)
+ } else if (placeHolders === 1) {
+ tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
+ push((tmp >> 8) & 0xFF)
+ push(tmp & 0xFF)
+ }
+
+ return arr
+ }
+
+ function uint8ToBase64 (uint8) {
+ var i,
+ extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
+ output = "",
+ temp, length
+
+ function encode (num) {
+ return lookup.charAt(num)
+ }
+
+ function tripletToBase64 (num) {
+ return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
+ }
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
+ temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
+ output += tripletToBase64(temp)
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ switch (extraBytes) {
+ case 1:
+ temp = uint8[uint8.length - 1]
+ output += encode(temp >> 2)
+ output += encode((temp << 4) & 0x3F)
+ output += '=='
+ break
+ case 2:
+ temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
+ output += encode(temp >> 10)
+ output += encode((temp >> 4) & 0x3F)
+ output += encode((temp << 2) & 0x3F)
+ output += '='
+ break
+ }
+
+ return output
+ }
+
+ exports.toByteArray = b64ToByteArray
+ exports.fromByteArray = uint8ToBase64
+}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
+
+},{}],6:[function(_dereq_,module,exports){
+exports.read = function(buffer, offset, isLE, mLen, nBytes) {
+ var e, m,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = isLE ? (nBytes - 1) : 0,
+ d = isLE ? -1 : 1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = isLE ? 0 : (nBytes - 1),
+ d = isLE ? 1 : -1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e + eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
+
+},{}],7:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * @license
+ * Lo-Dash 2.4.1 (Custom Build)
+ * Build: `lodash modern -o ./dist/lodash.js`
+ * Copyright 2012-2013 The Dojo Foundation
+ * Based on Underscore.js 1.5.2
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
+
+ /** Used to pool arrays and objects used internally */
+ var arrayPool = [],
+ objectPool = [];
+
+ /** Used to generate unique IDs */
+ var idCounter = 0;
+
+ /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+ var keyPrefix = +new Date + '';
+
+ /** Used as the size when optimizations are enabled for large arrays */
+ var largeArraySize = 75;
+
+ /** Used as the max size of the `arrayPool` and `objectPool` */
+ var maxPoolSize = 40;
+
+ /** Used to detect and test whitespace */
+ var whitespace = (
+ // whitespace
+ ' \t\x0B\f\xA0\ufeff' +
+
+ // line terminators
+ '\n\r\u2028\u2029' +
+
+ // unicode category "Zs" space separators
+ '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+ );
+
+ /** Used to match empty string literals in compiled template source */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+ /**
+ * Used to match ES6 template delimiters
+ * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
+
+ /** Used to detected named functions */
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /** Used to match leading whitespace and zeros to be removed */
+ var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
+
+ /** Used to detect functions containing a `this` reference */
+ var reThis = /\bthis\b/;
+
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** Used to assign default `context` object properties */
+ var contextProps = [
+ 'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
+ 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+ 'parseInt', 'setTimeout'
+ ];
+
+ /** Used to make template sourceURLs easier to identify */
+ var templateCounter = 0;
+
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
+
+ /** Used to identify object classifications that `_.clone` supports */
+ var cloneableClasses = {};
+ cloneableClasses[funcClass] = false;
+ cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+ cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+ cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+ cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+ /** Used as an internal `_.debounce` options object */
+ var debounceOptions = {
+ 'leading': false,
+ 'maxWait': 0,
+ 'trailing': false
+ };
+
+ /** Used as the property descriptor for `__bindData__` */
+ var descriptor = {
+ 'configurable': false,
+ 'enumerable': false,
+ 'value': null,
+ 'writable': false
+ };
+
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /** 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 the popular CommonJS extension `module.exports` */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+ var freeGlobal = objectTypes[typeof global] && global;
+ if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches
+ * or `fromIndex` constraints.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * An implementation of `_.contains` for cache objects that mimics the return
+ * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+ *
+ * @private
+ * @param {Object} cache The cache object to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `0` if `value` is found, else `-1`.
+ */
+ function cacheIndexOf(cache, value) {
+ var type = typeof value;
+ cache = cache.cache;
+
+ if (type == 'boolean' || value == null) {
+ return cache[value] ? 0 : -1;
+ }
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value;
+ cache = (cache = cache[type]) && cache[key];
+
+ return type == 'object'
+ ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+ : (cache ? 0 : -1);
+ }
+
+ /**
+ * Adds a given value to the corresponding cache object.
+ *
+ * @private
+ * @param {*} value The value to add to the cache.
+ */
+ function cachePush(value) {
+ var cache = this.cache,
+ type = typeof value;
+
+ if (type == 'boolean' || value == null) {
+ cache[value] = true;
+ } else {
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value,
+ typeCache = cache[type] || (cache[type] = {});
+
+ if (type == 'object') {
+ (typeCache[key] || (typeCache[key] = [])).push(value);
+ } else {
+ typeCache[key] = true;
+ }
+ }
+ }
+
+ /**
+ * Used by `_.max` and `_.min` as the default callback when a given
+ * collection is a string value.
+ *
+ * @private
+ * @param {string} value The character to inspect.
+ * @returns {number} Returns the code unit of given character.
+ */
+ function charAtCallback(value) {
+ return value.charCodeAt(0);
+ }
+
+ /**
+ * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ac = a.criteria,
+ bc = b.criteria,
+ index = -1,
+ length = ac.length;
+
+ while (++index < length) {
+ var value = ac[index],
+ other = bc[index];
+
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
+ return 1;
+ }
+ if (value < other || typeof other == 'undefined') {
+ return -1;
+ }
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to return the same value for
+ // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See http://code.google.com/p/v8/issues/detail?id=90
+ return a.index - b.index;
+ }
+
+ /**
+ * Creates a cache object to optimize linear searches of large arrays.
+ *
+ * @private
+ * @param {Array} [array=[]] The array to search.
+ * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+ */
+ function createCache(array) {
+ var index = -1,
+ length = array.length,
+ first = array[0],
+ mid = array[(length / 2) | 0],
+ last = array[length - 1];
+
+ if (first && typeof first == 'object' &&
+ mid && typeof mid == 'object' && last && typeof last == 'object') {
+ return false;
+ }
+ var cache = getObject();
+ cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+ var result = getObject();
+ result.array = array;
+ result.cache = cache;
+ result.push = cachePush;
+
+ while (++index < length) {
+ result.push(array[index]);
+ }
+ return result;
+ }
+
+ /**
+ * Used by `template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(match) {
+ return '\\' + stringEscapes[match];
+ }
+
+ /**
+ * Gets an array from the array pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Array} The array from the pool.
+ */
+ function getArray() {
+ return arrayPool.pop() || [];
+ }
+
+ /**
+ * Gets an object from the object pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Object} The object from the pool.
+ */
+ function getObject() {
+ return objectPool.pop() || {
+ 'array': null,
+ 'cache': null,
+ 'criteria': null,
+ 'false': false,
+ 'index': 0,
+ 'null': false,
+ 'number': null,
+ 'object': null,
+ 'push': null,
+ 'string': null,
+ 'true': false,
+ 'undefined': false,
+ 'value': null
+ };
+ }
+
+ /**
+ * Releases the given array back to the array pool.
+ *
+ * @private
+ * @param {Array} [array] The array to release.
+ */
+ function releaseArray(array) {
+ array.length = 0;
+ if (arrayPool.length < maxPoolSize) {
+ arrayPool.push(array);
+ }
+ }
+
+ /**
+ * Releases the given object back to the object pool.
+ *
+ * @private
+ * @param {Object} [object] The object to release.
+ */
+ function releaseObject(object) {
+ var cache = object.cache;
+ if (cache) {
+ releaseObject(cache);
+ }
+ object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+ if (objectPool.length < maxPoolSize) {
+ objectPool.push(object);
+ }
+ }
+
+ /**
+ * Slices the `collection` from the `start` index up to, but not including,
+ * the `end` index.
+ *
+ * Note: This function is used instead of `Array#slice` to support node lists
+ * in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to slice.
+ * @param {number} start The start index.
+ * @param {number} end The end index.
+ * @returns {Array} Returns the new array.
+ */
+ function slice(array, start, end) {
+ start || (start = 0);
+ if (typeof end == 'undefined') {
+ end = array ? array.length : 0;
+ }
+ var index = -1,
+ length = end - start || 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Create a new `lodash` function using the given context object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} [context=root] The context object.
+ * @returns {Function} Returns the `lodash` function.
+ */
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.io/#x11.1.5.
+ context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Boolean = context.Boolean,
+ Date = context.Date,
+ Function = context.Function,
+ Math = context.Math,
+ Number = context.Number,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String,
+ TypeError = context.TypeError;
+
+ /**
+ * Used for `Array` method references.
+ *
+ * Normally `Array.prototype` would suffice, however, using an array literal
+ * avoids issues in Narwhal.
+ */
+ var arrayRef = [];
+
+ /** Used for native method references */
+ var objectProto = Object.prototype;
+
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = context._;
+
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(toString)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
+ );
+
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ fnToString = Function.prototype.toString,
+ getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ hasOwnProperty = objectProto.hasOwnProperty,
+ push = arrayRef.push,
+ setTimeout = context.setTimeout,
+ splice = arrayRef.splice,
+ unshift = arrayRef.unshift;
+
+ /** Used to set meta data on functions */
+ var defineProperty = (function() {
+ // IE 8 only accepts DOM elements
+ try {
+ var o = {},
+ func = isNative(func = Object.defineProperty) && func,
+ result = func(o, o, o) && func;
+ } catch(e) { }
+ return result;
+ }());
+
+ /* Native method shortcuts for methods with the same name as other `lodash` methods */
+ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeIsFinite = context.isFinite,
+ nativeIsNaN = context.isNaN,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Used to lookup a built-in constructor by [[Class]] */
+ var ctorByClass = {};
+ ctorByClass[arrayClass] = Array;
+ ctorByClass[boolClass] = Boolean;
+ ctorByClass[dateClass] = Date;
+ ctorByClass[funcClass] = Function;
+ ctorByClass[objectClass] = Object;
+ ctorByClass[numberClass] = Number;
+ ctorByClass[regexpClass] = RegExp;
+ ctorByClass[stringClass] = String;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps the given value to enable intuitive
+ * method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
+ *
+ * The non-chainable wrapper functions are:
+ * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+ * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+ * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+ * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+ * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+ * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+ * `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * provided, otherwise they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the `_.chain` method.
+ *
+ * @name _
+ * @constructor
+ * @category Chaining
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(num) {
+ * return num * num;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+ return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+ ? value
+ : new lodashWrapper(value);
+ }
+
+ /**
+ * A fast path for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @param {boolean} chainAll A flag to enable chaining for all methods
+ * @returns {Object} Returns a `lodash` instance.
+ */
+ function lodashWrapper(value, chainAll) {
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
+
+ /**
+ * An object used to flag environments features.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = lodash.support = {};
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': /<%-([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': '',
+
+ /**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type Object
+ */
+ 'imports': {
+
+ /**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type Function
+ */
+ '_': lodash
+ }
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ // avoid `arguments` object deoptimizations by using `slice` instead
+ // of `Array.prototype.slice.call` and not assigning `arguments` to a
+ // variable as a ternary expression
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.clone` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, isDeep, callback, stackA, stackB) {
+ if (callback) {
+ var result = callback(value);
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ }
+ // inspect [[Class]]
+ var isObj = isObject(value);
+ if (isObj) {
+ var className = toString.call(value);
+ if (!cloneableClasses[className]) {
+ return value;
+ }
+ var ctor = ctorByClass[className];
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return new ctor(+value);
+
+ case numberClass:
+ case stringClass:
+ return new ctor(value);
+
+ case regexpClass:
+ result = ctor(value.source, reFlags.exec(value));
+ result.lastIndex = value.lastIndex;
+ return result;
+ }
+ } else {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isDeep) {
+ // check for circular references and return corresponding clone
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ result = isArr ? ctor(value.length) : {};
+ }
+ else {
+ result = isArr ? slice(value) : assign({}, value);
+ }
+ // add array properties assigned by `RegExp#exec`
+ if (isArr) {
+ if (hasOwnProperty.call(value, 'index')) {
+ result.index = value.index;
+ }
+ if (hasOwnProperty.call(value, 'input')) {
+ result.input = value.input;
+ }
+ }
+ // exit for shallow clone
+ if (!isDeep) {
+ return result;
+ }
+ // add the source value to the stack of traversed objects
+ // and associate it with its clone
+ stackA.push(value);
+ stackB.push(result);
+
+ // recursively populate clone (susceptible to call stack limits)
+ (isArr ? forEach : forOwn)(value, function(objValue, key) {
+ result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+ });
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+ }
+
+ /**
+ * The base implementation of `_.createCallback` without support for creating
+ * "_.pluck" or "_.where" style callbacks.
+ *
+ * @private
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ */
+ function baseCreateCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+ return func;
+ }
+ var bindData = func.__bindData__;
+ if (typeof bindData == 'undefined') {
+ if (support.funcNames) {
+ bindData = !func.name;
+ }
+ bindData = bindData || !support.funcDecomp;
+ if (!bindData) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ bindData = !reFuncName.test(source);
+ }
+ if (!bindData) {
+ // checks if `func` references the `this` keyword and stores the result
+ bindData = reThis.test(source);
+ setBindData(func, bindData);
+ }
+ }
+ }
+ // exit early if there are no `this` references or `func` is bound
+ if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 2: return function(a, b) {
+ return func.call(thisArg, a, b);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return bind(func, thisArg);
+ }
+
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+ result = [];
+
+ if (isLarge) {
+ var cache = createCache(values);
+ if (cache) {
+ indexOf = cacheIndexOf;
+ values = cache;
+ } else {
+ isLarge = false;
+ }
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseObject(values);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` without support for callback
+ * shorthands or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns a new flattened array.
+ */
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (value && typeof value == 'object' && typeof value.length == 'number'
+ && (isArray(value) || isArguments(value))) {
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ value = baseFlatten(value, isShallow, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length,
+ resIndex = result.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[resIndex++] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+ * that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+ // used to indicate that when comparing objects, `a` has at least the properties of `b`
+ if (callback) {
+ var result = callback(a, b);
+ if (typeof result != 'undefined') {
+ return !!result;
+ }
+ }
+ // exit early for identical values
+ if (a === b) {
+ // treat `+0` vs. `-0` as not equal
+ return a !== 0 || (1 / a == 1 / b);
+ }
+ var type = typeof a,
+ otherType = typeof b;
+
+ // exit early for unlike primitive values
+ if (a === a &&
+ !(a && objectTypes[type]) &&
+ !(b && objectTypes[otherType])) {
+ return false;
+ }
+ // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+ // http://es5.github.io/#x15.3.4.4
+ if (a == null || b == null) {
+ return a === b;
+ }
+ // compare [[Class]] names
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className == argsClass) {
+ className = objectClass;
+ }
+ if (otherClass == argsClass) {
+ otherClass = objectClass;
+ }
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ // coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+ return +a == +b;
+
+ case numberClass:
+ // treat `NaN` vs. `NaN` as equal
+ return (a != +a)
+ ? b != +b
+ // but treat `+0` vs. `-0` as not equal
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+ // treat string primitives and their corresponding object instances as equal
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ // unwrap any `lodash` wrapped values
+ var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+ bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+ }
+ // exit for functions and DOM nodes
+ if (className != objectClass) {
+ return false;
+ }
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var ctorA = a.constructor,
+ ctorB = b.constructor;
+
+ // non `Object` object instances with different constructors are not equal
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
+ return false;
+ }
+ }
+ // assume cyclic structures are equal
+ // the algorithm for detecting cyclic structures is adapted from ES 5.1
+ // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var size = 0;
+ result = true;
+
+ // add `a` and `b` to the stack of traversed objects
+ stackA.push(a);
+ stackB.push(b);
+
+ // recursively compare objects and arrays (susceptible to call stack limits)
+ if (isArr) {
+ // compare lengths to determine if a deep comparison is necessary
+ length = a.length;
+ size = b.length;
+ result = size == length;
+
+ if (result || isWhere) {
+ // deep compare the contents, ignoring non-numeric properties
+ while (size--) {
+ var index = length,
+ value = b[size];
+
+ if (isWhere) {
+ while (index--) {
+ if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+ // which, in this case, is more costly
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ // count the number of properties.
+ size++;
+ // deep compare each property value.
+ return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+ }
+ });
+
+ if (result && !isWhere) {
+ // ensure both objects have the same number of properties
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ // `size` will be `-1` if `a` has more properties than `b`
+ return (result = --size > -1);
+ }
+ });
+ }
+ }
+ stackA.pop();
+ stackB.pop();
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.merge` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ */
+ function baseMerge(object, source, callback, stackA, stackB) {
+ (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+ var found,
+ isArr,
+ result = source,
+ value = object[key];
+
+ if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+ // avoid merging previously merged cyclic sources
+ var stackLength = stackA.length;
+ while (stackLength--) {
+ if ((found = stackA[stackLength] == source)) {
+ value = stackB[stackLength];
+ break;
+ }
+ }
+ if (!found) {
+ var isShallow;
+ if (callback) {
+ result = callback(value, source);
+ if ((isShallow = typeof result != 'undefined')) {
+ value = result;
+ }
+ }
+ if (!isShallow) {
+ value = isArr
+ ? (isArray(value) ? value : [])
+ : (isPlainObject(value) ? value : {});
+ }
+ // add `source` and associated `value` to the stack of traversed objects
+ stackA.push(source);
+ stackB.push(value);
+
+ // recursively merge objects and arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ baseMerge(value, source, callback, stackA, stackB);
+ }
+ }
+ }
+ else {
+ if (callback) {
+ result = callback(value, source);
+ if (typeof result == 'undefined') {
+ result = source;
+ }
+ }
+ if (typeof result != 'undefined') {
+ value = result;
+ }
+ }
+ object[key] = value;
+ });
+ }
+
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Array} Returns a duplicate-value-free array.
+ */
+ function baseUniq(array, isSorted, callback) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+ seen = (callback || isLarge) ? getArray() : result;
+
+ if (isLarge) {
+ var cache = createCache(seen);
+ indexOf = cacheIndexOf;
+ seen = cache;
+ }
+ while (++index < length) {
+ var value = array[index],
+ computed = callback ? callback(value, index, array) : value;
+
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ if (callback || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseArray(seen.array);
+ releaseObject(seen);
+ } else if (callback) {
+ releaseArray(seen);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an object composed
+ * of keys generated from the results of running each element of the collection
+ * through a callback. The given `setter` function sets the keys and values
+ * of the composed object.
+ *
+ * @private
+ * @param {Function} setter The setter function.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter) {
+ return function(collection, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, callback(value, index, collection), collection);
+ }
+ } else {
+ forOwn(collection, function(value, key, collection) {
+ setter(result, value, callback(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, either curries or invokes `func`
+ * with an optional `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of method flags to compose.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry`
+ * 8 - `_.curry` (bound)
+ * 16 - `_.partial`
+ * 32 - `_.partialRight`
+ * @param {Array} [partialArgs] An array of arguments to prepend to those
+ * provided to the new function.
+ * @param {Array} [partialRightArgs] An array of arguments to append to those
+ * provided to the new function.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new function.
+ */
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ isPartial = bitmask & 16,
+ isPartialRight = bitmask & 32;
+
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError;
+ }
+ if (isPartial && !partialArgs.length) {
+ bitmask &= ~16;
+ isPartial = partialArgs = false;
+ }
+ if (isPartialRight && !partialRightArgs.length) {
+ bitmask &= ~32;
+ isPartialRight = partialRightArgs = false;
+ }
+ var bindData = func && func.__bindData__;
+ if (bindData && bindData !== true) {
+ // clone `bindData`
+ bindData = slice(bindData);
+ if (bindData[2]) {
+ bindData[2] = slice(bindData[2]);
+ }
+ if (bindData[3]) {
+ bindData[3] = slice(bindData[3]);
+ }
+ // set `thisBinding` is not previously bound
+ if (isBind && !(bindData[1] & 1)) {
+ bindData[4] = thisArg;
+ }
+ // set if previously bound but not currently (subsequent curried functions)
+ if (!isBind && bindData[1] & 1) {
+ bitmask |= 8;
+ }
+ // set curried arity if not yet set
+ if (isCurry && !(bindData[1] & 4)) {
+ bindData[5] = arity;
+ }
+ // append partial left arguments
+ if (isPartial) {
+ push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+ }
+ // append partial right arguments
+ if (isPartialRight) {
+ unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+ }
+ // merge flags
+ bindData[1] |= bitmask;
+ return createWrapper.apply(null, bindData);
+ }
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+ }
+
+ /**
+ * Used by `escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(match) {
+ return htmlEscapes[match];
+ }
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized, this method returns the custom method, otherwise it returns
+ * the `baseIndexOf` function.
+ *
+ * @private
+ * @returns {Function} Returns the "indexOf" function.
+ */
+ function getIndexOf() {
+ var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ */
+ function isNative(value) {
+ return typeof value == 'function' && reNative.test(value);
+ }
+
+ /**
+ * Sets `this` binding data on a given function.
+ *
+ * @private
+ * @param {Function} func The function to set data on.
+ * @param {Array} value The data array to set.
+ */
+ var setBindData = !defineProperty ? noop : function(func, value) {
+ descriptor.value = value;
+ defineProperty(func, '__bindData__', descriptor);
+ };
+
+ /**
+ * A fallback implementation of `isPlainObject` which checks if a given value
+ * is an object created by the `Object` constructor, assuming objects created
+ * by the `Object` constructor have no inherited enumerable properties and that
+ * there are no `Object.prototype` extensions.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+ function shimIsPlainObject(value) {
+ var ctor,
+ result;
+
+ // avoid non Object objects, `arguments` objects, and DOM elements
+ if (!(value && toString.call(value) == objectClass) ||
+ (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+ return false;
+ }
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ forIn(value, function(value, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+ }
+
+ /**
+ * 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];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass || false;
+ }
+
+ /**
+ * Checks if `value` is an array.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @example
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ */
+ var isArray = nativeIsArray || function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass || false;
+ };
+
+ /**
+ * A fallback implementation of `Object.keys` which produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ */
+ var shimKeys = function(object) {
+ var index, iterable = object, result = [];
+ if (!iterable) return result;
+ if (!(objectTypes[typeof object])) return result;
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ result.push(index);
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates an array composed of the own enumerable property names of an object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ * @example
+ *
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ return nativeKeys(object);
+ };
+
+ /**
+ * Used to convert characters to HTML entities:
+ *
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+ */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /** Used to match HTML entities and HTML characters */
+ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+ reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a callback is provided it will be executed to produce the
+ * assigned values. The callback is bound to `thisArg` and invoked with two
+ * arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
+ *
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var assign = function(object, source, guard) {
+ var index, iterable = object, result = iterable;
+ if (!iterable) return result;
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = typeof guard == 'number' ? 2 : args.length;
+ if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
+ var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
+ } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
+ callback = args[--argsLength];
+ }
+ while (++argsIndex < argsLength) {
+ iterable = args[argsIndex];
+ if (iterable && objectTypes[typeof iterable]) {
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+ }
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a callback
+ * is provided it will be executed to produce the cloned values. If the
+ * callback returns `undefined` cloning will be handled by the method instead.
+ * The callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
+ * // => true
+ *
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
+ */
+ function clone(value, isDeep, callback, thisArg) {
+ // allows working with "Collections" methods without using their `index`
+ // and `collection` arguments for `isDeep` and `callback`
+ if (typeof isDeep != 'boolean' && isDeep != null) {
+ thisArg = callback;
+ callback = isDeep;
+ isDeep = false;
+ }
+ return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates a deep clone of `value`. If a callback is provided it will be
+ * executed to produce the cloned values. If the callback returns `undefined`
+ * cloning will be handled by the method instead. The callback is bound to
+ * `thisArg` and invoked with one argument; (value).
+ *
+ * Note: This method is loosely based on the structured clone algorithm. Functions
+ * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+ * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+ * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var deep = _.cloneDeep(characters);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'node': element
+ * };
+ *
+ * var clone = _.cloneDeep(view, function(value) {
+ * return _.isElement(value) ? value.cloneNode(true) : undefined;
+ * });
+ *
+ * clone.node == view.node;
+ * // => false
+ */
+ function cloneDeep(value, callback, thisArg) {
+ return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties ? assign(result, properties) : result;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var defaults = function(object, source, guard) {
+ var index, iterable = object, result = iterable;
+ if (!iterable) return result;
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = typeof guard == 'number' ? 2 : args.length;
+ while (++argsIndex < argsLength) {
+ iterable = args[argsIndex];
+ if (iterable && objectTypes[typeof iterable]) {
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ if (typeof result[index] == 'undefined') result[index] = iterable[index];
+ }
+ }
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.findIndex` except that it returns the key of the
+ * first element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': false },
+ * 'fred': { 'age': 40, 'blocked': true },
+ * 'pebbles': { 'age': 1, 'blocked': false }
+ * };
+ *
+ * _.findKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => 'barney' (property order is not guaranteed across environments)
+ *
+ * // using "_.where" callback shorthand
+ * _.findKey(characters, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findKey(characters, 'blocked');
+ * // => 'fred'
+ */
+ function findKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': true },
+ * 'fred': { 'age': 40, 'blocked': false },
+ * 'pebbles': { 'age': 1, 'blocked': true }
+ * };
+ *
+ * _.findLastKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastKey(characters, { 'age': 40 });
+ * // => 'fred'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastKey(characters, 'blocked');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwnRight(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object,
+ * executing the callback for each property. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, key, object). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forIn(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ */
+ var forIn = function(collection, callback, thisArg) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ for (index in iterable) {
+ if (callback(iterable[index], index, collection) === false) return result;
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.forIn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forInRight(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+ */
+ function forInRight(object, callback, thisArg) {
+ var pairs = [];
+
+ forIn(object, function(value, key) {
+ pairs.push(key, value);
+ });
+
+ var length = pairs.length;
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(pairs[length--], pairs[length], object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Iterates over own enumerable properties of an object, executing the callback
+ * for each property. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, key, object). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+ */
+ var forOwn = function(collection, callback, thisArg) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ if (callback(iterable[index], index, collection) === false) return result;
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.forOwn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+ */
+ function forOwnRight(object, callback, thisArg) {
+ var props = keys(object),
+ length = props.length;
+
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ var key = props[length];
+ if (callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a sorted array of property names of all enumerable properties,
+ * own and inherited, of `object` that have function values.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names that have function values.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+ */
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
+ }
+
+ /**
+ * Checks if the specified property name exists as a direct property of `object`,
+ * instead of an inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to check.
+ * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of the given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a boolean value.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @example
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ }
+
+ /**
+ * Checks if `value` is a date.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ */
+ function isDate(value) {
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value && value.nodeType === 1 || false;
+ }
+
+ /**
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({});
+ * // => true
+ *
+ * _.isEmpty('');
+ * // => true
+ */
+ function isEmpty(value) {
+ var result = true;
+ if (!value) {
+ return result;
+ }
+ var className = toString.call(value),
+ length = value.length;
+
+ if ((className == arrayClass || className == stringClass || className == argsClass ) ||
+ (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ return !length;
+ }
+ forOwn(value, function() {
+ return (result = false);
+ });
+ return result;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If a callback is provided it will be executed
+ * to compare values. If the callback returns `undefined` comparisons will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (a, b).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
+ *
+ * object == copy;
+ * // => false
+ *
+ * _.isEqual(object, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b, callback, thisArg) {
+ return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+ }
+
+ /**
+ * Checks if `value` is, or can be coerced to, a finite number.
+ *
+ * Note: This is not the same as native `isFinite` which will return true for
+ * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+ }
+
+ /**
+ * Checks if `value` is a function.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ */
+ function isFunction(value) {
+ return typeof value == 'function';
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.io/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return !!(value && objectTypes[typeof value]);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * Note: This is not the same as native `isNaN` which will return `true` for
+ * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // `NaN` as a primitive is the only value that is not equal to itself
+ // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
+ }
+
+ /**
+ * Checks if `value` is an object created by the `Object` constructor.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * _.isPlainObject(new Shape);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && toString.call(value) == objectClass)) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/fred/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('fred');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates an object with the same keys as `object` and values generated by
+ * running each own enumerable property of `object` through the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+ * @example
+ *
+ * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ *
+ * var characters = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.mapValues(characters, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 }
+ */
+ function mapValues(object, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ forOwn(object, function(value, key, object) {
+ result[key] = callback(value, key, object);
+ });
+ return result;
+ }
+
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined` into the destination object. Subsequent sources
+ * will overwrite property assignments of previous sources. If a callback is
+ * provided it will be executed to produce the merged values of the destination
+ * and source properties. If the callback returns `undefined` merging will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var names = {
+ * 'characters': [
+ * { 'name': 'barney' },
+ * { 'name': 'fred' }
+ * ]
+ * };
+ *
+ * var ages = {
+ * 'characters': [
+ * { 'age': 36 },
+ * { 'age': 40 }
+ * ]
+ * };
+ *
+ * _.merge(names, ages);
+ * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+ *
+ * var food = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var otherFood = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(food, otherFood, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+ */
+ function merge(object) {
+ var args = arguments,
+ length = 2;
+
+ if (!isObject(object)) {
+ return object;
+ }
+ // allows working with `_.reduce` and `_.reduceRight` without using
+ // their `index` and `collection` arguments
+ if (typeof args[2] != 'number') {
+ length = args.length;
+ }
+ if (length > 3 && typeof args[length - 2] == 'function') {
+ var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+ } else if (length > 2 && typeof args[length - 1] == 'function') {
+ callback = args[--length];
+ }
+ var sources = slice(arguments, 1, length),
+ index = -1,
+ stackA = getArray(),
+ stackB = getArray();
+
+ while (++index < length) {
+ baseMerge(object, sources[index], callback, stackA, stackB);
+ }
+ releaseArray(stackA);
+ releaseArray(stackB);
+ return object;
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` omitting the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The properties to omit or the
+ * function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function omit(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (!callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of an object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` picking the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The function called per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function pick(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = isObject(object) ? props.length : 0;
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * An alternative to `_.reduce` this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own
+ * enumerable properties through a callback, with each callback execution
+ * potentially mutating the `accumulator` object. The callback is bound to
+ * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+ * Callbacks may exit iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+ * num *= num;
+ * if (num % 2) {
+ * return result.push(num) < 3;
+ * }
+ * });
+ * // => [1, 9, 25]
+ *
+ * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function transform(object, callback, accumulator, thisArg) {
+ var isArr = isArray(object);
+ if (accumulator == null) {
+ if (isArr) {
+ accumulator = [];
+ } else {
+ var ctor = object && object.constructor,
+ proto = ctor && ctor.prototype;
+
+ accumulator = baseCreate(proto);
+ }
+ }
+ if (callback) {
+ callback = lodash.createCallback(callback, thisArg, 4);
+ (isArr ? forEach : forOwn)(object, function(value, index, object) {
+ return callback(accumulator, value, index, object);
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (property order is not guaranteed across environments)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements from the specified indexes, or keys, of the
+ * `collection`. Indexes may be specified as individual arguments or as arrays
+ * of indexes.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+ * to retrieve, specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns a new array of elements corresponding to the
+ * provided indexes.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
+ */
+ function at(collection) {
+ var args = arguments,
+ index = -1,
+ props = baseFlatten(args, true, false, 1),
+ length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+ result = Array(length);
+
+ while(++index < length) {
+ result[index] = collection[props[index]];
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a given value is present in a collection using strict equality
+ * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+ * offset from the end of the collection.
+ *
+ * @static
+ * @memberOf _
+ * @alias include
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {*} target The value to check for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @example
+ *
+ * _.contains([1, 2, 3], 1);
+ * // => true
+ *
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.contains('pebbles', 'eb');
+ * // => true
+ */
+ function contains(collection, target, fromIndex) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = collection ? collection.length : 0,
+ result = false;
+
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+ if (isArray(collection)) {
+ result = indexOf(collection, target, fromIndex) > -1;
+ } else if (typeof length == 'number') {
+ result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+ } else {
+ forOwn(collection, function(value) {
+ if (++index >= fromIndex) {
+ return !(result = value === target);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through the callback. The corresponding value
+ * of each key is the number of times the key was returned by the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+
+ /**
+ * Checks if the given callback returns truey value for **all** elements of
+ * a collection. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(characters, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(characters, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return (result = !!callback(value, index, collection));
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning an array of all elements
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning the first element that
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect, findWhere
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
+ * // using "_.where" callback shorthand
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+ */
+ function find(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
+ }
+ } else {
+ var result;
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+ }
+
+ /**
+ * This method is like `_.find` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(num) {
+ * return num % 2 == 1;
+ * });
+ * // => 3
+ */
+ function findLast(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forEachRight(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, executing the callback for each
+ * element. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+ * // => logs each number and returns '1,2,3'
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+ * // => logs each number and returns the object (property order is not guaranteed across environments)
+ */
+ function forEach(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, callback);
+ }
+ return collection;
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+ * // => logs each number from right to left and returns '3,2,1'
+ */
+ function forEachRight(collection, callback, thisArg) {
+ var length = collection ? collection.length : 0;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ while (length--) {
+ if (callback(collection[length], length, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ var props = keys(collection);
+ length = props.length;
+ forOwn(collection, function(value, key, collection) {
+ key = props ? props[--length] : --length;
+ return callback(collection[key], key, collection);
+ });
+ }
+ return collection;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of a collection through the callback. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of the collection through the given callback. The corresponding
+ * value of each key is the last element responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keys = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keys, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be provided to each invoked method. If `methodName` is a function it
+ * will be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [arg] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
+
+ /**
+ * Creates an array of values by running each element in the collection
+ * through the callback. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (property order is not guaranteed across environments)
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ result = [];
+ forOwn(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the maximum value of a collection. If the collection is empty or
+ * falsey `-Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of a collection. If the collection is empty or
+ * falsey `Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the value of a specified property from all elements in the collection.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} property The name of the property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ var pluck = map;
+
+ /**
+ * Reduces a collection to a value which is the accumulated result of running
+ * each element in the collection through the callback, where each successive
+ * callback execution consumes the return value of the previous execution. If
+ * `accumulator` is not provided the first element of the collection will be
+ * used as the initial `accumulator` value. The callback is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ if (!collection) return accumulator;
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+
+ var index = -1,
+ length = collection.length;
+
+ if (typeof length == 'number') {
+ if (noaccum) {
+ accumulator = collection[++index];
+ }
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+ forEachRight(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The opposite of `_.filter` this method returns the elements of a
+ * collection that the callback does **not** return truey for.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that failed the callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Retrieves a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
+ * @returns {Array} Returns the random sample(s) of `collection`.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (collection && typeof collection.length != 'number') {
+ collection = values(collection);
+ }
+ if (n == null || guard) {
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(nativeMax(0, n), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ var rand = baseRandom(0, ++index);
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
+ }
+
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if the callback returns a truey value for **any** element of a
+ * collection. The function returns as soon as it finds a passing value and
+ * does not iterate over the entire collection. The callback is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if any element passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(characters, 'blocked');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(characters, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return !(result = callback(value, index, collection));
+ });
+ }
+ return !!result;
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through the callback. This method
+ * performs a stable sort, that is, it will preserve the original sort order
+ * of equal elements. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an array of property names is provided for `callback` the collection
+ * will be sorted by each property value.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 26 },
+ * { 'name': 'fred', 'age': 30 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(_.sortBy(characters, 'age'), _.values);
+ * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+ *
+ * // sorting by multiple properties
+ * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+ * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ isArr = isArray(callback),
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ if (!isArr) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ forEach(collection, function(value, key, collection) {
+ var object = result[++index] = getObject();
+ if (isArr) {
+ object.criteria = map(callback, function(key) { return value[key]; });
+ } else {
+ (object.criteria = getArray())[0] = callback(value, key, collection);
+ }
+ object.index = index;
+ object.value = value;
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ var object = result[length];
+ result[length] = object.value;
+ if (!isArr) {
+ releaseArray(object.criteria);
+ }
+ releaseObject(object);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (collection && typeof collection.length == 'number') {
+ return slice(collection);
+ }
+ return values(collection);
+ }
+
+ /**
+ * Performs a deep comparison of each element in a `collection` to the given
+ * `properties` object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} props The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given properties.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ *
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ */
+ var where = filter;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using strict
+ * equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.findIndex(characters, function(chr) {
+ * return chr.age < 20;
+ * });
+ * // => 2
+ *
+ * // using "_.where" callback shorthand
+ * _.findIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findIndex(characters, 'blocked');
+ * // => 1
+ */
+ function findIndex(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ if (callback(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': true },
+ * { 'name': 'fred', 'age': 40, 'blocked': false },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': true }
+ * ];
+ *
+ * _.findLastIndex(characters, function(chr) {
+ * return chr.age > 30;
+ * });
+ * // => 1
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastIndex(characters, 'blocked');
+ * // => 2
+ */
+ function findLastIndex(array, callback, thisArg) {
+ var length = array ? array.length : 0;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(array[length], length, array)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the first element or first `n` elements of an array. If a callback
+ * is provided elements at the beginning of the array are returned as long
+ * as the callback returns truey. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function first(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[0] : undefined;
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ }
+
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truey, the array will only be flattened a single level. If a callback
+ * is provided each element of the array is passed through the callback before
+ * flattening. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
+ */
+ function flatten(array, isShallow, callback, thisArg) {
+ // juggle arguments
+ if (typeof isShallow != 'boolean' && isShallow != null) {
+ thisArg = callback;
+ callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+ isShallow = false;
+ }
+ if (callback != null) {
+ array = map(array, callback, thisArg);
+ }
+ return baseFlatten(array, isShallow);
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If the array is already sorted
+ * providing `true` for `fromIndex` will run a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ if (typeof fromIndex == 'number') {
+ var length = array ? array.length : 0;
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+ } else if (fromIndex) {
+ var index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element or last `n` elements of an array. If a
+ * callback is provided elements at the end of the array are excluded from
+ * the result as long as the callback returns truey. The callback is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function initial(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : callback || n;
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+ }
+
+ /**
+ * Creates an array of unique values present in all provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length,
+ caches = getArray(),
+ indexOf = getIndexOf(),
+ trustIndexOf = indexOf === baseIndexOf,
+ seen = getArray();
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ caches.push(trustIndexOf && value.length >= largeArraySize &&
+ createCache(argsIndex ? args[argsIndex] : seen));
+ }
+ }
+ var array = args[0],
+ index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ outer:
+ while (++index < length) {
+ var cache = caches[0];
+ value = array[index];
+
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+ argsIndex = argsLength;
+ (cache || seen).push(value);
+ while (--argsIndex) {
+ cache = caches[argsIndex];
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ }
+ while (argsLength--) {
+ cache = caches[argsLength];
+ if (cache) {
+ releaseObject(cache);
+ }
+ }
+ releaseArray(caches);
+ releaseArray(seen);
+ return result;
+ }
+
+ /**
+ * Gets the last element or last `n` elements of an array. If a callback is
+ * provided elements at the end of the array are returned as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function last(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[length - 1] : undefined;
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
+ }
+
+ /**
+ * Gets the index at which the last occurrence of `value` is found using strict
+ * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+ * as the offset from the end of the collection.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all provided values from the given array using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {...*} [value] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ * _.pull(array, 2, 3);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ function pull(array) {
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = args.length,
+ length = array ? array.length : 0;
+
+ while (++argsIndex < argsLength) {
+ var index = -1,
+ value = args[argsIndex];
+ while (++index < length) {
+ if (array[index] === value) {
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = typeof step == 'number' ? step : (+step || 1);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / (step || 1))),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * Removes all elements from an array that the callback returns truey for
+ * and returns an array of removed elements. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4, 5, 6];
+ * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+ *
+ * console.log(array);
+ * // => [1, 3, 5]
+ *
+ * console.log(evens);
+ * // => [2, 4, 6]
+ */
+ function remove(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (callback(value, index, array)) {
+ result.push(value);
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The opposite of `_.initial` this method gets all but the first element or
+ * first `n` elements of an array. If a callback function is provided elements
+ * at the beginning of the array are excluded from the result as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+ }
+ return slice(array, n);
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which a value
+ * should be inserted into a given sorted array in order to maintain the sort
+ * order of the array. If a callback is provided it will be executed for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
+ }
+ return low;
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, true, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using strict equality
+ * for comparisons, i.e. `===`. If the array is sorted, providing
+ * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+ * each element of `array` is passed through the callback before uniqueness
+ * is computed. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+ * // => ['A', 'b', 'C']
+ *
+ * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2.5, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ // juggle arguments
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = callback;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+ isSorted = false;
+ }
+ if (callback != null) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ return baseUniq(array, isSorted, callback);
+ }
+
+ /**
+ * Creates an array excluding all provided values using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {...*} [value] The values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, slice(arguments, 1));
+ }
+
+ /**
+ * Creates an array that is the symmetric difference of the provided arrays.
+ * See http://en.wikipedia.org/wiki/Symmetric_difference.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of values.
+ * @example
+ *
+ * _.xor([1, 2, 3], [5, 2, 1, 4]);
+ * // => [3, 5, 4]
+ *
+ * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+ * // => [1, 4, 5]
+ */
+ function xor() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var array = arguments[index];
+ if (isArray(array) || isArguments(array)) {
+ var result = result
+ ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+ : array;
+ }
+ }
+ return result || [];
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second
+ * elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @alias unzip
+ * @category Arrays
+ * @param {...Array} [array] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var array = arguments.length > 1 ? arguments : arguments[0],
+ index = -1,
+ length = array ? max(pluck(array, 'length')) : 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = pluck(array, index);
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Provide
+ * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
+
+ if (!values && length && !isArray(keys[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that executes `func`, with the `this` binding and
+ * arguments of the created function, only after being called `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {number} n The number of times the function must be called before
+ * `func` is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('Done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'Done saving!', after all saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * provided to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
+ * func();
+ * // => 'hi fred'
+ */
+ function bind(func, thisArg) {
+ return arguments.length > 2
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all the function properties
+ * of `object` will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...string} [methodName] The object method names to
+ * bind, specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+ index = -1,
+ length = funcs.length;
+
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = createWrapper(object[key], 1, null, null, object);
+ }
+ return object;
+ }
+
+ /**
+ * Creates a function that, when called, invokes the method at `object[key]`
+ * and prepends any additional `bindKey` arguments to those provided to the bound
+ * function. This method differs from `_.bind` by allowing bound functions to
+ * reference methods that will be redefined or don't yet exist.
+ * See http://michaux.ca/articles/lazy-function-definition-pattern.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object the method belongs to.
+ * @param {string} key The key of the method.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'name': 'fred',
+ * 'greet': function(greeting) {
+ * return greeting + ' ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bindKey(object, 'greet', 'hi');
+ * func();
+ * // => 'hi fred'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + 'ya ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hiya fred!'
+ */
+ function bindKey(object, key) {
+ return arguments.length > 2
+ ? createWrapper(key, 19, slice(arguments, 2), null, object)
+ : createWrapper(key, 3, null, null, object);
+ }
+
+ /**
+ * Creates a function that is the composition of the provided functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {...Function} [func] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var realNameMap = {
+ * 'pebbles': 'penelope'
+ * };
+ *
+ * var format = function(name) {
+ * name = realNameMap[name.toLowerCase()] || name;
+ * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+ * };
+ *
+ * var greet = function(formatted) {
+ * return 'Hiya ' + formatted + '!';
+ * };
+ *
+ * var welcome = _.compose(greet, format);
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
+ */
+ function compose() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ if (!isFunction(funcs[length])) {
+ throw new TypeError;
+ }
+ }
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * Creates a function which accepts one or more arguments of `func` that when
+ * invoked either executes `func` returning its result, if all `func` arguments
+ * have been provided, or returns a function that accepts one or more of the
+ * remaining `func` arguments, and so on. The arity of `func` can be specified
+ * if `func.length` is not sufficient.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var curried = _.curry(function(a, b, c) {
+ * console.log(a + b + c);
+ * });
+ *
+ * curried(1)(2)(3);
+ * // => 6
+ *
+ * curried(1, 2)(3);
+ * // => 6
+ *
+ * curried(1, 2, 3);
+ * // => 6
+ */
+ function curry(func, arity) {
+ arity = typeof arity == 'number' ? arity : (+arity || func.length);
+ return createWrapper(func, 4, null, null, null, arity);
+ }
+
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked.
+ * Provide an options object to indicate that `func` should be invoked on
+ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+ * to the debounced function will return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * var lazyLayout = _.debounce(calculateLayout, 150);
+ * jQuery(window).on('resize', lazyLayout);
+ *
+ * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * });
+ *
+ * // ensure `batchLog` is executed once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * source.addEventListener('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }, false);
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ wait = nativeMax(0, wait) || 0;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ var delayed = function() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ };
+
+ var maxDelayed = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ };
+
+ return function() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay execution.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it will be used to determine the cache key for storing the result
+ * based on the arguments provided to the memoized function. By default, the
+ * first argument provided to the memoized function is used as the cache key.
+ * The `func` is executed with the `this` binding of the memoized function.
+ * The result cache is exposed as the `cache` property on the memoized function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ *
+ * fibonacci(9)
+ * // => 34
+ *
+ * var data = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // modifying the result cache
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
+ *
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
+ */
+ function memoize(func, resolver) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var memoized = function() {
+ var cache = memoized.cache,
+ key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ }
+ memoized.cache = {};
+ return memoized;
+ }
+
+ /**
+ * Creates a function that is restricted to execute `func` once. Repeat calls to
+ * the function will return the value of the first call. The `func` is executed
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
+
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those provided to the new function. This
+ * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('fred');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ return createWrapper(func, 16, slice(arguments, 1));
+ }
+
+ /**
+ * This method is like `_.partial` except that `partial` arguments are
+ * appended to those provided to the new function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ *
+ * var options = {
+ * 'variable': 'data',
+ * 'imports': { 'jq': $ }
+ * };
+ *
+ * defaultsDeep(options, _.templateSettings);
+ *
+ * options.variable
+ * // => 'data'
+ *
+ * options.imports
+ * // => { '_': _, 'jq': $ }
+ */
+ function partialRight(func) {
+ return createWrapper(func, 32, null, slice(arguments, 1));
+ }
+
+ /**
+ * Creates a function that, when executed, will only call the `func` function
+ * at most once per every `wait` milliseconds. Provide an options object to
+ * indicate that `func` should be invoked on the leading and/or trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle executions to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ *
+ * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+ * 'trailing': false
+ * }));
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? options.leading : leading;
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ debounceOptions.leading = leading;
+ debounceOptions.maxWait = wait;
+ debounceOptions.trailing = trailing;
+
+ return debounce(func, wait, debounceOptions);
+ }
+
+ /**
+ * 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.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '' + func(text) + '
';
+ * });
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles
'
+ */
+ function wrap(value, wrapper) {
+ return createWrapper(wrapper, 16, [value]);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var getter = _.constant(object);
+ * getter() === object;
+ * // => true
+ */
+ function constant(value) {
+ return function() {
+ return value;
+ };
+ }
+
+ /**
+ * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * name the created callback will return the property value for a given element.
+ * If `func` is an object the created callback will return `true` for elements
+ * that contain the equivalent object properties, otherwise it will return `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
+ */
+ function createCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (func == null || type == 'function') {
+ return baseCreateCallback(func, thisArg, argCount);
+ }
+ // handle "_.pluck" style callback shorthands
+ if (type != 'object') {
+ return property(func);
+ }
+ var props = keys(func),
+ key = props[0],
+ a = func[key];
+
+ // handle "_.where" style callback shorthands
+ if (props.length == 1 && a === a && !isObject(a)) {
+ // fast path the common case of providing an object with a single
+ // property containing a primitive value
+ return function(object) {
+ var b = object[key];
+ return a === b && (a !== 0 || (1 / a == 1 / b));
+ };
+ }
+ return function(object) {
+ var length = props.length,
+ result = false;
+
+ while (length--) {
+ if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+ break;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
+
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Adds function properties of a source object to the destination object.
+ * If `object` is a function methods will be added to its prototype as well.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @example
+ *
+ * function capitalize(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ *
+ * _.mixin({ 'capitalize': capitalize });
+ * _.capitalize('fred');
+ * // => 'Fred'
+ *
+ * _('fred').capitalize().value();
+ * // => 'Fred'
+ *
+ * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+ * _('fred').capitalize();
+ * // => 'Fred'
+ */
+ function mixin(object, source, options) {
+ var chain = true,
+ methodNames = source && functions(source);
+
+ if (!source || (!options && !methodNames.length)) {
+ if (options == null) {
+ options = source;
+ }
+ ctor = lodashWrapper;
+ source = object;
+ object = lodash;
+ methodNames = functions(source);
+ }
+ if (options === false) {
+ chain = false;
+ } else if (isObject(options) && 'chain' in options) {
+ chain = options.chain;
+ }
+ var ctor = object,
+ isFunc = isFunction(ctor);
+
+ forEach(methodNames, function(methodName) {
+ var func = object[methodName] = source[methodName];
+ if (isFunc) {
+ ctor.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ args = [value];
+
+ push.apply(args, arguments);
+ var result = func.apply(object, args);
+ if (chain || chainAll) {
+ if (value === result && isObject(result)) {
+ return this;
+ }
+ result = new ctor(result);
+ result.__chain__ = chainAll;
+ }
+ return result;
+ };
+ }
+ });
+ }
+
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ context._ = oldDash;
+ return this;
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var stamp = _.now();
+ * _.defer(function() { console.log(_.now() - stamp); });
+ * // => logs the number of milliseconds it took for the deferred function to be called
+ */
+ var now = isNative(now = Date.now) && now || function() {
+ return new Date().getTime();
+ };
+
+ /**
+ * Converts the given value into an integer of the specified radix.
+ * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+ * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+ *
+ * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+ * implementations. See http://es5.github.io/#E.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} value The value to parse.
+ * @param {number} [radix] The radix used to interpret the value to parse.
+ * @returns {number} Returns the new integer value.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ */
+ var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+ // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+ return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+ };
+
+ /**
+ * Creates a "_.pluck" style function, which returns the `key` value of a
+ * given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} key The name of the property to retrieve.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 36 }
+ * ];
+ *
+ * var getName = _.property('name');
+ *
+ * _.map(characters, getName);
+ * // => ['barney', 'fred']
+ *
+ * _.sortBy(characters, getName);
+ * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
+ */
+ function property(key) {
+ return function(object) {
+ return object[key];
+ };
+ }
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number will be
+ * returned. If `floating` is truey or either `min` or `max` are floats a
+ * floating-point number will be returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating=false] Specify returning a floating-point number.
+ * @returns {number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max, floating) {
+ var noMin = min == null,
+ noMax = max == null;
+
+ if (floating == null) {
+ if (typeof min == 'boolean' && noMax) {
+ floating = min;
+ min = 1;
+ }
+ else if (!noMax && typeof max == 'boolean') {
+ floating = max;
+ noMax = true;
+ }
+ }
+ if (noMin && noMax) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (noMax) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+ }
+ return baseRandom(min, max);
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If `key` is a function
+ * it will be invoked with the `this` binding of `object` and its result returned,
+ * else the property value is returned. If `object` is falsey then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to resolve.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, key) {
+ if (object) {
+ var value = object[key];
+ return isFunction(value) ? object[key]() : value;
+ }
+ }
+
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * For more information on precompiling templates see:
+ * http://lodash.com/custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} text The template text.
+ * @param {Object} data The data object used to populate the text.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as local variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [variable] The data object variable name.
+ * @returns {Function|string} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<%- value %>', { 'value': '
+ *
+ *
+ *
+ * // in node.js
+ * var Firebase = require('../lib/MockFirebase');
+ *
+ * ## Usage Examples
+ *
+ * var fb = new MockFirebase('Mock://foo/bar');
+ * fb.on('value', function(snap) {
+ * console.log(snap.val());
+ * });
+ *
+ * // do things async or synchronously, like fb.child('foo').set('bar')...
+ *
+ * // trigger callbacks and event listeners
+ * fb.flush();
+ *
+ * // spy on methods
+ * expect(fb.on.called).toBe(true);
+ *
+ * ## Trigger events automagically instead of calling flush()
+ *
+ * var fb = new MockFirebase('Mock://hello/world');
+ * fb.autoFlush(1000); // triggers events after 1 second (asynchronous)
+ * fb.autoFlush(); // triggers events immediately (synchronous)
+ *
+ * ## Simulating Errors
+ *
+ * var fb = new MockFirebase('Mock://fails/a/lot');
+ * fb.failNext('set', new Error('PERMISSION_DENIED'); // create an error to be invoked on the next set() op
+ * fb.set({foo: bar}, function(err) {
+ * // err.message === 'PERMISSION_DENIED'
+ * });
+ * fb.flush();
+ *
+ * ## Building with custom data
+ *
+ * // change data for all mocks
+ * MockFirebase.DEFAULT_DATA = {foo: { bar: 'baz'}};
+ * var fb = new MockFirebase('Mock://foo');
+ * fb.once('value', function(snap) {
+ * snap.name(); // foo
+ * snap.val(); // {bar: 'baz'}
+ * });
+ *
+ * // customize for a single instance
+ * var fb = new MockFirebase('Mock://foo', {foo: 'bar'});
+ * fb.once('value', function(snap) {
+ * snap.name(); // foo
+ * snap.val(); // 'bar'
+ * });
+ *
+ * @param {string} [currentPath] use a relative path here or a url, all .child() calls will append to this
+ * @param {Object} [data] specify the data in this Firebase instance (defaults to MockFirebase.DEFAULT_DATA)
+ * @param {MockFirebase} [parent] for internal use
+ * @param {string} [name] for internal use
+ * @constructor
+ */
+function MockFirebase(currentPath, data, parent, name) {
+ // represents the fake url
+ //todo should unwrap nested paths; Firebase
+ //todo accepts sub-paths, mock should too
+ this.currentPath = currentPath || 'Mock://';
+
+ // see failNext()
+ this.errs = {};
+
+ // used for setPriorty and moving records
+ this.priority = null;
+
+ // null for the root path
+ this.myName = parent? name : extractName(currentPath);
+
+ // see autoFlush() and flush()
+ this.flushDelay = parent? parent.flushDelay : false;
+ this.flushQueue = parent? parent.flushQueue : new FlushQueue();
+
+ // stores the listeners for various event types
+ this._events = { value: [], child_added: [], child_removed: [], child_changed: [], child_moved: [] };
+
+ // allows changes to be propagated between child/parent instances
+ this.parentRef = parent||null;
+ this.children = {};
+ if (parent) parent.children[this.name()] = this;
+
+ // stores sorted keys in data for priority ordering
+ this.sortedDataKeys = [];
+
+ // do not modify this directly, use set() and flush(true)
+ this.data = null;
+ this._dataChanged(_.cloneDeep(arguments.length > 1? data||null : MockFirebase.DEFAULT_DATA));
+
+ // stores the last auto id generated by push() for tests
+ this._lastAutoId = null;
+
+ // turn all our public methods into spies so they can be monitored for calls and return values
+ // see jasmine spies: https://github.com/pivotal/jasmine/wiki/Spies
+ // the Firebase constructor can be spied on using spyOn(window, 'Firebase') from within the test unit
+ for(var key in this) {
+ if( !key.match(/^_/) && typeof(this[key]) === 'function' ) {
+ spyFactory(this, key);
+ }
+ }
+}
+
+MockFirebase.prototype = {
+ /*****************************************************
+ * Test Unit tools (not part of Firebase API)
+ *****************************************************/
+
+ /**
+ * Invoke all the operations that have been queued thus far. If a numeric delay is specified, this
+ * occurs asynchronously. Otherwise, it is a synchronous event.
+ *
+ * This allows Firebase to be used in synchronous tests without waiting for async callbacks. It also
+ * provides a rudimentary mechanism for simulating locally cached data (events are triggered
+ * synchronously when you do on('value') or on('child_added') against locally cached data)
+ *
+ * If you call this multiple times with different delay values, you could invoke the events out
+ * of order; make sure that is your intention.
+ *
+ * This also affects all child and parent paths that were created using .child from the original
+ * MockFirebase instance; all events queued before a flush, regardless of the node level in hierarchy,
+ * are processed together. To make child and parent paths fire on a different timeline or out of order,
+ * check out splitFlushQueue() below.
+ *
+ *
+ * var fbRef = new MockFirebase();
+ * var childRef = fbRef.child('a');
+ * fbRef.update({a: 'foo'});
+ * childRef.set('bar');
+ * fbRef.flush(); // a === 'bar'
+ *
+ * fbRef.update({a: 'foo'});
+ * fbRef.flush(0); // async flush
+ *
+ * childRef.set('bar');
+ * childRef.flush(); // sync flush (could also do fbRef.flush()--same thing)
+ * // after the async flush completes, a === 'foo'!
+ * // the child_changed and value events also happen in reversed order
+ *
+ *
+ * @param {boolean|int} [delay]
+ * @returns {MockFirebase}
+ */
+ flush: function(delay) {
+ this.flushQueue.flush(delay);
+ return this;
+ },
+
+ /**
+ * Automatically trigger a flush event after each operation. If a numeric delay is specified, this is an
+ * asynchronous event. If value is set to true, it is synchronous (flush is triggered immediately). Setting
+ * this to false disables autoFlush
+ *
+ * @param {int|boolean} [delay]
+ * @returns {MockFirebase}
+ */
+ autoFlush: function(delay){
+ if(_.isUndefined(delay)) { delay = true; }
+ if( this.flushDelay !== delay ) {
+ this.flushDelay = delay;
+ _.each(this.children, function(c) {
+ c.autoFlush(delay);
+ });
+ if( this.parentRef ) { this.parentRef.autoFlush(delay); }
+ if (delay !== false) this.flush(delay);
+ }
+ return this;
+ },
+
+ /**
+ * If we can't use fakeEvent() and we need to test events out of order, we can give a child its own flush queue
+ * so that calling flush() does not also trigger parent and siblings in the queue.
+ */
+ splitFlushQueue: function() {
+ this.flushQueue = new FlushQueue();
+ },
+
+ /**
+ * Restore the flush queue after using splitFlushQueue() so that child/sibling/parent queues are flushed in order.
+ */
+ joinFlushQueue: function() {
+ if( this.parent ) {
+ this.flushQueue = this.parent.flushQueue;
+ }
+ },
+
+ /**
+ * Simulate a failure by specifying that the next invocation of methodName should
+ * fail with the provided error.
+ *
+ * @param {String} methodName currently only supports `set`, `update`, `push` (with data) and `transaction`
+ * @param {String|Error} error
+ */
+ failNext: function(methodName, error) {
+ this.errs[methodName] = error;
+ },
+
+ /**
+ * Simulate a security error by cancelling any opened listeners on the given path
+ * and returning the error provided. If event/callback/context are provided, then
+ * only listeners exactly matching this signature (same rules as off()) will be cancelled.
+ *
+ * This also invokes off() on the events--they won't be notified of future changes.
+ *
+ * @param {String|Error} error
+ * @param {String} [event]
+ * @param {Function} [callback]
+ * @param {Object} [context]
+ */
+ forceCancel: function(error, event, callback, context) {
+ var self = this, events = self._events;
+ _.each(event? [event] : _.keys(events), function(eventType) {
+ var list = _.filter(events[eventType], function(parts) {
+ return !event || !callback || (callback === parts[0] && context === parts[1]);
+ });
+ _.each(list, function(parts) {
+ parts[2].call(parts[1], error);
+ self.off(event, callback, context);
+ });
+ });
+ },
+
+ /**
+ * Returns a copy of the current data
+ * @returns {*}
+ */
+ getData: function() {
+ return _.cloneDeep(this.data);
+ },
+
+ /**
+ * Returns keys from the data in this path
+ * @returns {Array}
+ */
+ getKeys: function() {
+ return this.sortedDataKeys.slice();
+ },
+
+ /**
+ * Returns the last automatically generated ID
+ * @returns {string|string|*}
+ */
+ getLastAutoId: function() {
+ return this._lastAutoId;
+ },
+
+ /**
+ * Generates a fake event that does not affect or derive from the actual data in this
+ * mock. Great for quick event handling tests that won't rely on longer-term consistency
+ * or for creating out-of-order networking conditions that are hard to produce
+ * using set/remove/setPriority
+ *
+ * @param {string} event
+ * @param {string} key
+ * @param data
+ * @param {string} [prevChild]
+ * @param [pri]
+ * @returns {MockFirebase}
+ */
+ fakeEvent: function(event, key, data, prevChild, pri) {
+ if (DEBUG) console.log('fakeEvent', event, this.toString(), key);
+ if( arguments.length < 5 ) { pri = null; }
+ if( arguments.length < 4 ) { prevChild = null; }
+ if( arguments.length < 3 ) { data = null; }
+ var self = this;
+ var ref = event==='value'? self : self.child(key);
+ var snap = makeSnap(ref, data, pri);
+ self._defer(function() {
+ _.each(self._events[event], function (parts) {
+ var fn = parts[0], context = parts[1];
+ if (_.contains(['child_added', 'child_moved'], event)) {
+ fn.call(context, snap, prevChild);
+ }
+ else {
+ fn.call(context, snap);
+ }
+ });
+ });
+ return this;
+ },
+
+ /*****************************************************
+ * Firebase API methods
+ *****************************************************/
+
+ toString: function() {
+ return this.currentPath;
+ },
+
+ child: function(childPath) {
+ if( !childPath ) { throw new Error('bad child path '+this.toString()); }
+ var parts = _.isArray(childPath)? childPath : childPath.split('/');
+ var childKey = parts.shift();
+ var child = this.children[childKey];
+ if( !child ) {
+ child = new MockFirebase(mergePaths(this.currentPath, childKey), this._childData(childKey), this, childKey);
+ this.children[child.name()] = child;
+ }
+ if( parts.length ) {
+ child = child.child(parts);
+ }
+ return child;
+ },
+
+ set: function(data, callback) {
+ var self = this;
+ var err = this._nextErr('set');
+ data = _.cloneDeep(data);
+ if (DEBUG) console.log('set called',this.toString(), data);
+ this._defer(function() {
+ if (DEBUG) console.log('set completed',self.toString(), data);
+ if( err === null ) {
+ self._dataChanged(data);
+ }
+ if (callback) callback(err);
+ });
+ },
+
+ update: function(changes, callback) {
+ if( !_.isObject(changes) ) {
+ throw new Error('First argument must be an object when calling $update');
+ }
+ var self = this;
+ var err = this._nextErr('update');
+ var base = this.getData();
+ var data = _.assign(_.isObject(base)? base : {}, changes);
+ if (DEBUG) console.log('update called', this.toString(), data);
+ this._defer(function() {
+ if (DEBUG) console.log('update flushed', self.toString(), data);
+ if( err === null ) {
+ self._dataChanged(data);
+ }
+ if (callback) callback(err);
+ });
+ },
+
+ setPriority: function(newPriority, callback) {
+ var self = this;
+ var err = this._nextErr('setPriority');
+ if (DEBUG) console.log('setPriority called', self.toString(), newPriority);
+ self._defer(function() {
+ if (DEBUG) console.log('setPriority flushed', self.toString(), newPriority);
+ self._priChanged(newPriority);
+ if (callback) callback(err);
+ });
+ },
+
+ setWithPriority: function(data, pri, callback) {
+ this.setPriority(pri);
+ this.set(data, callback);
+ },
+
+ name: function() {
+ return this.myName;
+ },
+
+ ref: function() {
+ return this;
+ },
+
+ parent: function() {
+ return this.parentRef;
+ },
+
+ root: function() {
+ var next = this;
+ while(next.parentRef) {
+ next = next.parentRef;
+ }
+ return next;
+ },
+
+ push: function(data, callback) {
+ var child = this.child(this._newAutoId());
+ var err = this._nextErr('push');
+ if( err ) { child.failNext('set', err); }
+ if( arguments.length && data !== null ) {
+ // currently, callback only invoked if child exists
+ child.set(data, callback);
+ }
+ return child;
+ },
+
+ once: function(event, callback, cancel, context) {
+ var self = this;
+ if( arguments.length === 3 && !_.isFunction(cancel) ) {
+ context = cancel;
+ cancel = function() {};
+ }
+ else if( arguments.length < 3 ) {
+ cancel = function() {};
+ context = null;
+ }
+ var err = this._nextErr('once');
+ if( err ) {
+ this._defer(function() {
+ cancel.call(context, err);
+ });
+ }
+ else {
+ var fn = function (snap) {
+ self.off(event, fn, context);
+ callback.call(context, snap);
+ };
+
+ this.on(event, fn, context);
+ }
+ },
+
+ remove: function(callback) {
+ var self = this;
+ var err = this._nextErr('remove');
+ if (DEBUG) console.log('remove called', this.toString());
+ this._defer(function() {
+ if (DEBUG) console.log('remove completed',self.toString());
+ if( err === null ) {
+ self._dataChanged(null);
+ }
+ if (callback) callback(err);
+ });
+ return this;
+ },
+
+ on: function(event, callback, cancel, context) {
+ if( arguments.length === 3 && !_.isFunction(cancel) ) {
+ context = cancel;
+ cancel = function() {};
+ }
+ else if( arguments.length < 3 ) {
+ cancel = function() {};
+ }
+
+ var err = this._nextErr('on');
+ if( err ) {
+ this._defer(function() {
+ cancel.call(context, err);
+ });
+ }
+ else {
+ var eventArr = [callback, context, cancel];
+ this._events[event].push(eventArr);
+ var self = this;
+ if( event === 'value' ) {
+ self._defer(function() {
+ // make sure off() wasn't called in the interim
+ if( self._events[event].indexOf(eventArr) > -1) {
+ callback.call(context, makeSnap(self, self.getData(), self.priority));
+ }
+ });
+ }
+ else if( event === 'child_added' ) {
+ self._defer(function() {
+ if( self._events[event].indexOf(eventArr) > -1) {
+ var prev = null;
+ _.each(self.sortedDataKeys, function (k) {
+ var child = self.child(k);
+ callback.call(context, makeSnap(child, child.getData(), child.priority), prev);
+ prev = k;
+ });
+ }
+ });
+ }
+ }
+ },
+
+ off: function(event, callback, context) {
+ if( !event ) {
+ for (var key in this._events)
+ if( this._events.hasOwnProperty(key) )
+ this.off(key);
+ }
+ else if( callback ) {
+ var list = this._events[event];
+ var newList = this._events[event] = [];
+ _.each(list, function(parts) {
+ if( parts[0] !== callback || parts[1] !== context ) {
+ newList.push(parts);
+ }
+ });
+ }
+ else {
+ this._events[event] = [];
+ }
+ },
+
+ transaction: function(valueFn, finishedFn, applyLocally) {
+ var self = this;
+ var valueSpy = spyFactory(valueFn, 'trxn:valueFn');
+ var finishedSpy = spyFactory(finishedFn, 'trxn:finishedFn');
+ this._defer(function() {
+ var err = self._nextErr('transaction');
+ // unlike most defer methods, self will use the value as it exists at the time
+ // the transaction is actually invoked, which is the eventual consistent value
+ // it would have in reality
+ var res = valueSpy(self.getData());
+ var newData = _.isUndefined(res) || err? self.getData() : res;
+ self._dataChanged(newData);
+ finishedSpy(err, err === null && !_.isUndefined(res), makeSnap(self, newData, self.priority));
+ });
+ return [valueSpy, finishedSpy, applyLocally];
+ },
+
+ /**
+ * If token is valid and parses, returns the contents of token as exected. If not, the error is returned.
+ * Does not change behavior in any way (since we don't really auth anywhere)
+ *
+ * @param {String} token
+ * @param {Function} [callback]
+ */
+ auth: function(token, callback) {
+ //todo invoke callback with the parsed token contents
+ if (callback) this._defer(callback);
+ },
+
+ /**
+ * Just a stub at this point.
+ * @param {int} limit
+ */
+ limit: function(limit) {
+ return new MockQuery(this).limit(limit);
+ },
+
+ startAt: function(priority, key) {
+ return new MockQuery(this).startAt(priority, key);
+ },
+
+ endAt: function(priority, key) {
+ return new MockQuery(this).endAt(priority, key);
+ },
+
+ /*****************************************************
+ * Private/internal methods
+ *****************************************************/
+
+ _childChanged: function(ref) {
+ var events = [];
+ var childKey = ref.name();
+ var data = ref.getData();
+ if (DEBUG) console.log('_childChanged', this.toString() + ' -> ' + childKey, data);
+ if( data === null ) {
+ this._removeChild(childKey, events);
+ }
+ else {
+ this._updateOrAdd(childKey, data, events);
+ }
+ this._triggerAll(events);
+ },
+
+ _dataChanged: function(unparsedData) {
+ var self = this;
+ var pri = getMeta(unparsedData, 'priority', self.priority);
+ var data = cleanData(unparsedData);
+ if( pri !== self.priority ) {
+ self._priChanged(pri);
+ }
+ if( !_.isEqual(data, self.data) ) {
+ if (DEBUG) console.log('_dataChanged', self.toString(), data);
+ var oldKeys = _.keys(self.data).sort();
+ var newKeys = _.keys(data).sort();
+ var keysToRemove = _.difference(oldKeys, newKeys);
+ var keysToChange = _.difference(newKeys, keysToRemove);
+ var events = [];
+
+ _.each(keysToRemove, function(key) {
+ self._removeChild(key, events);
+ });
+
+ if(!_.isObject(data)) {
+ events.push(false);
+ self.data = data;
+ }
+ else {
+ _.each(keysToChange, function(key) {
+ self._updateOrAdd(key, unparsedData[key], events);
+ });
+ }
+
+ // update order of my child keys
+ self._resort();
+
+ // trigger parent notifications after all children have
+ // been processed
+ self._triggerAll(events);
+ }
+ },
+
+ _priChanged: function(newPriority) {
+ if (DEBUG) console.log('_priChanged', this.toString(), newPriority);
+ this.priority = newPriority;
+ if( this.parentRef ) {
+ this.parentRef._resort(this.name());
+ }
+ },
+
+ _getPri: function(key) {
+ return _.has(this.children, key)? this.children[key].priority : null;
+ },
+
+ _resort: function(childKeyMoved) {
+ var self = this;
+ self.sortedDataKeys.sort(self.childComparator.bind(self));
+ // resort the data object to match our keys so value events return ordered content
+ var oldDat = _.assign({}, self.data);
+ _.each(oldDat, function(v,k) { delete self.data[k]; });
+ _.each(self.sortedDataKeys, function(k) {
+ self.data[k] = oldDat[k];
+ });
+ if( !_.isUndefined(childKeyMoved) && _.has(self.data, childKeyMoved) ) {
+ self._trigger('child_moved', self.data[childKeyMoved], self._getPri(childKeyMoved), childKeyMoved);
+ }
+ },
+
+ _addKey: function(newKey) {
+ if(_.indexOf(this.sortedDataKeys, newKey) === -1) {
+ this.sortedDataKeys.push(newKey);
+ this._resort();
+ }
+ },
+
+ _dropKey: function(key) {
+ var i = _.indexOf(this.sortedDataKeys, key);
+ if( i > -1 ) {
+ this.sortedDataKeys.splice(i, 1);
+ }
+ },
+
+ _defer: function() {
+ //todo should probably be taking some sort of snapshot of my data here and passing
+ //todo that into `fn` for reference
+ this.flushQueue.add(Array.prototype.slice.call(arguments, 0));
+ if( this.flushDelay !== false ) { this.flush(this.flushDelay); }
+ },
+
+ _trigger: function(event, data, pri, key) {
+ if (DEBUG) console.log('_trigger', event, this.toString(), key);
+ var self = this, ref = event==='value'? self : self.child(key);
+ var snap = makeSnap(ref, data, pri);
+ _.each(self._events[event], function(parts) {
+ var fn = parts[0], context = parts[1];
+ if(_.contains(['child_added', 'child_moved'], event)) {
+ fn.call(context, snap, self._getPrevChild(key));
+ }
+ else {
+ fn.call(context, snap);
+ }
+ });
+ },
+
+ _triggerAll: function(events) {
+ var self = this;
+ if( !events.length ) { return; }
+ _.each(events, function(event) {
+ if (event !== false) self._trigger.apply(self, event);
+ });
+ self._trigger('value', self.data, self.priority);
+ if( self.parentRef ) {
+ self.parentRef._childChanged(self);
+ }
+ },
+
+ _updateOrAdd: function(key, data, events) {
+ var exists = _.isObject(this.data) && this.data.hasOwnProperty(key);
+ if( !exists ) {
+ return this._addChild(key, data, events);
+ }
+ else {
+ return this._updateChild(key, data, events);
+ }
+ },
+
+ _addChild: function(key, data, events) {
+ if(this._hasChild(key)) {
+ throw new Error('Tried to add existing object', key);
+ }
+ if( !_.isObject(this.data) ) {
+ this.data = {};
+ }
+ this._addKey(key);
+ this.data[key] = cleanData(data);
+ var c = this.child(key);
+ c._dataChanged(data);
+ if (events) events.push(['child_added', c.getData(), c.priority, key]);
+ },
+
+ _removeChild: function(key, events) {
+ if(this._hasChild(key)) {
+ this._dropKey(key);
+ var data = this.data[key];
+ delete this.data[key];
+ if(_.isEmpty(this.data)) {
+ this.data = null;
+ }
+ if(_.has(this.children, key)) {
+ this.children[key]._dataChanged(null);
+ }
+ if (events) events.push(['child_removed', data, null, key]);
+ }
+ },
+
+ _updateChild: function(key, data, events) {
+ var cdata = cleanData(data);
+ if(_.isObject(this.data) && _.has(this.data,key) && !_.isEqual(this.data[key], cdata)) {
+ this.data[key] = cdata;
+ var c = this.child(key);
+ c._dataChanged(data);
+ if (events) events.push(['child_changed', c.getData(), c.priority, key]);
+ }
+ },
+
+ _newAutoId: function() {
+ this._lastAutoId = 'mock-'+Date.now()+'-'+Math.floor(Math.random()*10000);
+ return this._lastAutoId;
+ },
+
+ _nextErr: function(type) {
+ var err = this.errs[type];
+ delete this.errs[type];
+ return err||null;
+ },
+
+ _hasChild: function(key) {
+ return _.isObject(this.data) && _.has(this.data, key);
+ },
+
+ _childData: function(key) {
+ return this._hasChild(key)? this.data[key] : null;
+ },
+
+ _getPrevChild: function(key) {
+// this._resort();
+ var keys = this.sortedDataKeys;
+ var i = _.indexOf(keys, key);
+ if( i === -1 ) {
+ keys = keys.slice();
+ keys.push(key);
+ keys.sort(this.childComparator.bind(this));
+ i = _.indexOf(keys, key);
+ }
+ return i === 0? null : keys[i-1];
+ },
+
+ childComparator: function(a, b) {
+ var aPri = this._getPri(a);
+ var bPri = this._getPri(b);
+ var x = priorityComparator(aPri, bPri);
+ if( x === 0 ) {
+ if( a !== b ) {
+ x = a < b? -1 : 1;
+ }
+ }
+ return x;
+ }
+};
+
+
+/*******************************************************************************
+ * MOCK QUERY
+ ******************************************************************************/
+function MockQuery(ref) {
+ this._ref = ref;
+ this._subs = [];
+ // startPri, endPri, startKey, endKey, and limit
+ this._q = {};
+}
+
+MockQuery.prototype = {
+ /*******************
+ * UTILITY FUNCTIONS
+ *******************/
+ flush: function() {
+ this.ref().flush.apply(this.ref(), arguments);
+ return this;
+ },
+
+ autoFlush: function() {
+ this.ref().autoFlush.apply(this.ref(), arguments);
+ return this;
+ },
+
+ slice: function() {
+ return new Slice(this);
+ },
+
+ getData: function() {
+ return this.slice().data;
+ },
+
+ fakeEvent: function(event, snap) {
+ _.each(this._subs, function(parts) {
+ if( parts[0] === 'event' ) {
+ parts[1].call(parts[2], snap);
+ }
+ });
+ },
+
+ /*******************
+ * API FUNCTIONS
+ *******************/
+ on: function(event, callback, cancelCallback, context) {
+ var self = this, isFirst = true, lastSlice = this.slice(), map;
+ var fn = function(snap, prevChild) {
+ var slice = new Slice(self, event==='value'? snap : makeRefSnap(snap.ref().parent()));
+ switch(event) {
+ case 'value':
+ if( isFirst || !lastSlice.equals(slice) ) {
+ callback.call(context, slice.snap());
+ }
+ break;
+ case 'child_moved':
+ var x = slice.pos(snap.name());
+ var y = slice.insertPos(snap.name());
+ if( x > -1 && y > -1 ) {
+ callback.call(context, snap, prevChild);
+ }
+ else if( x > -1 || y > -1 ) {
+ map = lastSlice.changeMap(slice);
+ }
+ break;
+ case 'child_added':
+ if( slice.has(snap.name()) && lastSlice.has(snap.name()) ) {
+ // is a child_added for existing event so allow it
+ callback.call(context, snap, prevChild);
+ }
+ map = lastSlice.changeMap(slice);
+ break;
+ case 'child_removed':
+ map = lastSlice.changeMap(slice);
+ break;
+ case 'child_changed':
+ callback.call(context, snap);
+ break;
+ default:
+ throw new Error('Invalid event: '+event);
+ }
+
+ if( map ) {
+ var newSnap = slice.snap();
+ var oldSnap = lastSlice.snap();
+ _.each(map.added, function(addKey) {
+ self.fakeEvent('child_added', newSnap.child(addKey));
+ });
+ _.each(map.removed, function(remKey) {
+ self.fakeEvent('child_removed', oldSnap.child(remKey));
+ });
+ }
+
+ isFirst = false;
+ lastSlice = slice;
+ };
+ var cancelFn = function(err) {
+ cancelCallback.call(context, err);
+ };
+ self._subs.push([event, callback, context, fn]);
+ this.ref().on(event, fn, cancelFn);
+ },
+
+ off: function(event, callback, context) {
+ var ref = this.ref();
+ _.each(this._subs, function(parts) {
+ if( parts[0] === event && parts[1] === callback && parts[2] === context ) {
+ ref.off(event, parts[3]);
+ }
+ });
+ },
+
+ once: function(event, callback, context) {
+ var self = this;
+ // once is tricky because we want the first match within our range
+ // so we use the on() method above which already does the needed legwork
+ function fn() {
+ self.off(event, fn);
+ // the snap is already sliced in on() so we can just pass it on here
+ callback.apply(context, arguments);
+ }
+ self.on(event, fn);
+ },
+
+ limit: function(intVal) {
+ if( typeof intVal !== 'number' ) {
+ throw new Error('Query.limit: First argument must be a positive integer.');
+ }
+ var q = new MockQuery(this.ref());
+ _.extend(q._q, this._q, {limit: intVal});
+ return q;
+ },
+
+ startAt: function(priority, key) {
+ assertQuery('Query.startAt', priority, key);
+ var q = new MockQuery(this.ref());
+ _.extend(q._q, this._q, {startKey: key, startPri: priority});
+ return q;
+ },
+
+ endAt: function(priority, key) {
+ assertQuery('Query.endAt', priority, key);
+ var q = new MockQuery(this.ref());
+ _.extend(q._q, this._q, {endKey: key, endPri: priority});
+ return q;
+ },
+
+ ref: function() {
+ return this._ref;
+ }
+};
+
+
+/*******************************************************************************
+ * SIMPLE LOGIN
+ ******************************************************************************/
+function MockFirebaseSimpleLogin(ref, callback, userData) {
+ // allows test units to monitor the callback function to make sure
+ // it is invoked (even if one is not declared)
+ this.callback = function() { callback.apply(null, Array.prototype.slice.call(arguments, 0)); };
+ this.attempts = [];
+ this.failMethod = MockFirebaseSimpleLogin.DEFAULT_FAIL_WHEN;
+ this.ref = ref; // we don't use ref for anything
+ this.autoFlushTime = MockFirebaseSimpleLogin.DEFAULT_AUTO_FLUSH;
+ this.userData = _.cloneDeep(MockFirebaseSimpleLogin.DEFAULT_USER_DATA);
+ if (userData) _.assign(this.userData, userData);
+
+ // turn all our public methods into spies so they can be monitored for calls and return values
+ // see jasmine spies: https://github.com/pivotal/jasmine/wiki/Spies
+ // the constructor can be spied on using spyOn(window, 'FirebaseSimpleLogin') from within the test unit
+ for(var key in this) {
+ if( !key.match(/^_/) && typeof(this[key]) === 'function' ) {
+ spyFactory(this, key);
+ }
+ }
+}
+
+MockFirebaseSimpleLogin.prototype = {
+
+ /*****************************************************
+ * Test Unit Methods
+ *****************************************************/
+
+ /**
+ * When this method is called, any outstanding login()
+ * attempts will be immediately resolved. If this method
+ * is called with an integer value, then the login attempt
+ * will resolve asynchronously after that many milliseconds.
+ *
+ * @param {int|boolean} [milliseconds]
+ * @returns {MockFirebaseSimpleLogin}
+ */
+ flush: function(milliseconds) {
+ var self = this;
+ if(_.isNumber(milliseconds) ) {
+ setTimeout(self.flush.bind(self), milliseconds);
+ }
+ else {
+ var attempts = self.attempts;
+ self.attempts = [];
+ _.each(attempts, function(x) {
+ x[0].apply(self, x.slice(1));
+ });
+ }
+ return self;
+ },
+
+ /**
+ * Automatically queue the flush() event
+ * each time login() is called. If this method
+ * is called with `true`, then the callback
+ * is invoked synchronously.
+ *
+ * If this method is called with an integer,
+ * the callback is triggered asynchronously
+ * after that many milliseconds.
+ *
+ * If this method is called with false, then
+ * autoFlush() is disabled.
+ *
+ * @param {int|boolean} [milliseconds]
+ * @returns {MockFirebaseSimpleLogin}
+ */
+ autoFlush: function(milliseconds) {
+ this.autoFlushTime = milliseconds;
+ if( this.autoFlushTime !== false ) {
+ this.flush(this.autoFlushTime);
+ }
+ return this;
+ },
+
+ /**
+ * `testMethod` is passed the {string}provider, {object}options, {object}user
+ * for each call to login(). If it returns anything other than
+ * null, then that is passed as the error message to the
+ * callback and the login call fails.
+ *
+ *
+ * // this is a simplified example of the default implementation (MockFirebaseSimpleLogin.DEFAULT_FAIL_WHEN)
+ * auth.failWhen(function(provider, options, user) {
+ * if( user.email !== options.email ) {
+ * return MockFirebaseSimpleLogin.createError('INVALID_USER');
+ * }
+ * else if( user.password !== options.password ) {
+ * return MockFirebaseSimpleLogin.createError('INVALID_PASSWORD');
+ * }
+ * else {
+ * return null;
+ * }
+ * });
+ *
+ *
+ * Multiple calls to this method replace the old failWhen criteria.
+ *
+ * @param testMethod
+ * @returns {MockFirebaseSimpleLogin}
+ */
+ failWhen: function(testMethod) {
+ this.failMethod = testMethod;
+ return this;
+ },
+
+ /**
+ * Retrieves a user account from the mock user data on this object
+ *
+ * @param provider
+ * @param options
+ */
+ getUser: function(provider, options) {
+ var data = this.userData[provider];
+ if( provider === 'password' ) {
+ data = (data||{})[options.email];
+ }
+ return data||null;
+ },
+
+ /*****************************************************
+ * Public API
+ *****************************************************/
+ login: function(provider, options) {
+ var err = this.failMethod(provider, options||{}, this.getUser(provider, options));
+ this._notify(err, err===null? this.userData[provider]: null);
+ },
+
+ logout: function() {
+ this._notify(null, null);
+ },
+
+ createUser: function(email, password, callback) {
+ if (!callback) callback = _.noop;
+ this._defer(function() {
+ var user = null, err = null;
+ if( this.userData.password.hasOwnProperty(email) ) {
+ err = createError('EMAIL_TAKEN', 'The specified email address is already in use.');
+ }
+ else {
+ user = createEmailUser(email, password);
+ this.userData.password[email] = user;
+ }
+ callback(err, user);
+ });
+ },
+
+ changePassword: function(email, oldPassword, newPassword, callback) {
+ if (!callback) callback = _.noop;
+ this._defer(function() {
+ var user = this.getUser('password', {email: email});
+ var err = this.failMethod('password', {email: email, password: oldPassword}, user);
+ if( err ) {
+ callback(err, false);
+ }
+ else {
+ user.password = newPassword;
+ callback(null, true);
+ }
+ });
+ },
+
+ sendPasswordResetEmail: function(email, callback) {
+ if (!callback) callback = _.noop;
+ this._defer(function() {
+ var user = this.getUser('password', {email: email});
+ if( !user ) {
+ callback(createError('INVALID_USER'), false);
+ }
+ else {
+ callback(null, true);
+ }
+ });
+ },
+
+ removeUser: function(email, password, callback) {
+ if (!callback) callback = _.noop;
+ this._defer(function() {
+ var user = this.getUser('password', {email: email});
+ if( !user ) {
+ callback(createError('INVALID_USER'), false);
+ }
+ else if( user.password !== password ) {
+ callback(createError('INVALID_PASSWORD'), false);
+ }
+ else {
+ delete this.userData.password[email];
+ callback(null, true);
+ }
+ });
+ },
+
+ /*****************************************************
+ * Private/internal methods
+ *****************************************************/
+ _notify: function(error, user) {
+ this._defer(this.callback, error, user);
+ },
+
+ _defer: function() {
+ var args = _.toArray(arguments);
+ this.attempts.push(args);
+ if( this.autoFlushTime !== false ) {
+ this.flush(this.autoFlushTime);
+ }
+ }
+};
+
+/***
+ * DATA SLICE
+ * A utility to handle limits, startAts, and endAts
+ */
+function Slice(queue, snap) {
+ var data = snap? snap.val() : queue.ref().getData();
+ this.ref = snap? snap.ref() : queue.ref();
+ this.priority = snap? snap.getPriority() : this.ref.priority;
+ this.pris = {};
+ this.data = {};
+ this.map = {};
+ this.outerMap = {};
+ this.keys = [];
+ this.props = this._makeProps(queue._q, this.ref, this.ref.getKeys().length);
+ this._build(this.ref, data);
+}
+
+Slice.prototype = {
+ prev: function(key) {
+ var pos = this.pos(key);
+ if( pos === 0 ) { return null; }
+ else {
+ if( pos < 0 ) { pos = this.keys.length; }
+ return this.keys[pos-1];
+ }
+ },
+
+ equals: function(slice) {
+ return _.isEqual(this.keys, slice.keys) && _.isEqual(this.data, slice.data);
+ },
+
+ pos: function(key) {
+ return this.has(key)? this.map[key] : -1;
+ },
+
+ insertPos: function(prevChild) {
+ var outerPos = this.outerMap[prevChild];
+ if( outerPos >= this.min && outerPos < this.max ) {
+ return outerPos+1;
+ }
+ return -1;
+ },
+
+ has: function(key) {
+ return this.map.hasOwnProperty(key);
+ },
+
+ snap: function(key) {
+ var ref = this.ref;
+ var data = this.data;
+ var pri = this.priority;
+ if( key ) {
+ data = this.get(key);
+ ref = ref.child(key);
+ pri = this.pri(key);
+ }
+ return makeSnap(ref, data, pri);
+ },
+
+ get: function(key) {
+ return this.has(key)? this.data[key] : null;
+ },
+
+ pri: function(key) {
+ return this.has(key)? this.pris[key] : null;
+ },
+
+ changeMap: function(slice) {
+ var self = this;
+ var changes = { in: [], out: [] };
+ _.each(self.data, function(v,k) {
+ if( !slice.has(k) ) {
+ changes.out.push(k);
+ }
+ });
+ _.each(slice.data, function(v,k) {
+ if( !self.has(k) ) {
+ changes.in.push(k);
+ }
+ });
+ return changes;
+ },
+
+ _inRange: function(props, key, pri, pos) {
+ if( pos === -1 ) { return false; }
+ if( !_.isUndefined(props.startPri) && priorityComparator(pri, props.startPri) < 0 ) {
+ return false;
+ }
+ if( !_.isUndefined(props.startKey) && priorityComparator(key, props.startKey) < 0 ) {
+ return false;
+ }
+ if( !_.isUndefined(props.endPri) && priorityComparator(pri, props.endPri) > 0 ) {
+ return false;
+ }
+ if( !_.isUndefined(props.endKey) && priorityComparator(key, props.endKey) > 0 ) {
+ return false;
+ }
+ if( props.max > -1 && pos > props.max ) {
+ return false;
+ }
+ return pos >= props.min;
+ },
+
+ _findPos: function(pri, key, ref, isStartBoundary) {
+ var keys = ref.getKeys(), firstMatch = -1, lastMatch = -1;
+ var len = keys.length, i, x, k;
+ if(_.isUndefined(pri) && _.isUndefined(key)) {
+ return -1;
+ }
+ for(i = 0; i < len; i++) {
+ k = keys[i];
+ x = priAndKeyComparator(pri, key, ref.child(k).priority, k);
+ if( x === 0 ) {
+ // if the key is undefined, we may have several matching comparisons
+ // so we will record both the first and last successful match
+ if (firstMatch === -1) {
+ firstMatch = i;
+ }
+ lastMatch = i;
+ }
+ else if( x < 0 ) {
+ // we found the breakpoint where our keys exceed the match params
+ if( i === 0 ) {
+ // if this is 0 then our match point is before the data starts, we
+ // will use len here because -1 already has a special meaning (no limit)
+ // and len ensures we won't get any data (no matches)
+ i = len;
+ }
+ break;
+ }
+ }
+
+ if( firstMatch !== -1 ) {
+ // we found a match, life is simple
+ return isStartBoundary? firstMatch : lastMatch;
+ }
+ else if( i < len ) {
+ // if we're looking for the start boundary then it's the first record after
+ // the breakpoint. If we're looking for the end boundary, it's the last record before it
+ return isStartBoundary? i : i -1;
+ }
+ else {
+ // we didn't find one, so use len (i.e. after the data, no results)
+ return len;
+ }
+ },
+
+ _makeProps: function(queueProps, ref, numRecords) {
+ var out = {};
+ _.each(queueProps, function(v,k) {
+ if(!_.isUndefined(v)) {
+ out[k] = v;
+ }
+ });
+ out.min = this._findPos(out.startPri, out.startKey, ref, true);
+ out.max = this._findPos(out.endPri, out.endKey, ref);
+ if( !_.isUndefined(queueProps.limit) ) {
+ if( out.min > -1 ) {
+ out.max = out.min + queueProps.limit;
+ }
+ else if( out.max > -1 ) {
+ out.min = out.max - queueProps.limit;
+ }
+ else if( queueProps.limit < numRecords ) {
+ out.max = numRecords-1;
+ out.min = Math.max(0, numRecords - queueProps.limit);
+ }
+ }
+ return out;
+ },
+
+ _build: function(ref, rawData) {
+ var i = 0, map = this.map, keys = this.keys, outer = this.outerMap;
+ var props = this.props, slicedData = this.data;
+ _.each(rawData, function(v,k) {
+ outer[k] = i < props.min? props.min - i : i - Math.max(props.min,0);
+ if( this._inRange(props, k, ref.child(k).priority, i++) ) {
+ map[k] = keys.length;
+ keys.push(k);
+ slicedData[k] = v;
+ }
+ }, this);
+ }
+};
+
+/***
+ * FLUSH QUEUE
+ * A utility to make sure events are flushed in the order
+ * they are invoked.
+ ***/
+function FlushQueue() {
+ this.queuedEvents = [];
+}
+
+FlushQueue.prototype.add = function(args) {
+ this.queuedEvents.push(args);
+};
+
+FlushQueue.prototype.flush = function(delay) {
+ if( !this.queuedEvents.length ) { return; }
+
+ // make a copy of event list and reset, this allows
+ // multiple calls to flush to queue various events out
+ // of order, and ensures that events that are added
+ // while flushing go into the next flush and not this one
+ var list = this.queuedEvents;
+
+ // events could get added as we invoke
+ // the list, so make a copy and reset first
+ this.queuedEvents = [];
+
+ function process() {
+ // invoke each event
+ list.forEach(function(parts) {
+ parts[0].apply(null, parts.slice(1));
+ });
+ }
+
+ if( _.isNumber(delay) ) {
+ setTimeout(process, delay);
+ }
+ else {
+ process();
+ }
+};
+
+
+function priAndKeyComparator(testPri, testKey, valPri, valKey) {
+ var x = 0;
+ if( !_.isUndefined(testPri) ) {
+ x = priorityComparator(testPri, valPri);
+ }
+ if( x === 0 && !_.isUndefined(testKey) && testKey !== valKey ) {
+ x = testKey < valKey? -1 : 1;
+ }
+ return x;
+}
+
+function priorityComparator(a,b) {
+ if (a !== b) {
+ if( a === null || b === null ) {
+ return a === null? -1 : 1;
+ }
+ if (typeof a !== typeof b) {
+ return typeof a === 'number' ? -1 : 1;
+ } else {
+ return a > b ? 1 : -1;
+ }
+ }
+ return 0;
+}
+
+var spyFactory = (function() {
+ var spyFunction;
+ if( typeof(global.jasmine) !== 'undefined' ) {
+ spyFunction = function(obj, method) {
+ var fn, spy;
+ if( typeof(obj) === 'object' ) {
+ spy = global.spyOn(obj, method);
+ if( typeof(spy.andCallThrough) === 'function' ) {
+ // karma < 0.12.x
+ fn = spy.andCallThrough();
+ }
+ else {
+ fn = spy.and.callThrough();
+ }
+ }
+ else {
+ spy = global.jasmine.createSpy(method);
+ if( typeof(arguments[0]) === 'function' ) {
+ if( typeof(spy.andCallFake) === 'function' ) {
+ // karma < 0.12.x
+ fn = spy.andCallFake(obj);
+ }
+ else {
+ fn = spy.and.callFake(obj);
+ }
+ }
+ else {
+ fn = spy;
+ }
+ }
+ return fn;
+ };
+ }
+ else {
+ spyFunction = function(obj, method) {
+ var sinon = (typeof window !== "undefined" ? window.sinon : typeof global !== "undefined" ? global.sinon : null);
+ if ( typeof (obj) === 'object') {
+ return sinon.spy(obj, method);
+ }
+ else {
+ return sinon.spy(obj);
+ }
+ };
+ }
+ return spyFunction;
+})();
+
+var USER_COUNT = 100;
+function createEmailUser(email, password) {
+ var id = USER_COUNT++;
+ return {
+ uid: 'password:'+id,
+ id: id,
+ email: email,
+ password: password,
+ provider: 'password',
+ md5_hash: md5(email),
+ firebaseAuthToken: 'FIREBASE_AUTH_TOKEN' //todo
+ };
+}
+
+function createDefaultUser(provider) {
+ var id = USER_COUNT++;
+
+ var out = {
+ uid: provider+':'+id,
+ id: id,
+ password: id,
+ provider: provider,
+ firebaseAuthToken: 'FIREBASE_AUTH_TOKEN' //todo
+ };
+ switch(provider) {
+ case 'password':
+ out.email = 'email@firebase.com';
+ out.md5_hash = md5(out.email);
+ break;
+ case 'twitter':
+ out.accessToken = 'ACCESS_TOKEN'; //todo
+ out.accessTokenSecret = 'ACCESS_TOKEN_SECRET'; //todo
+ out.displayName = 'DISPLAY_NAME';
+ out.thirdPartyUserData = {}; //todo
+ out.username = 'USERNAME';
+ break;
+ case 'google':
+ out.accessToken = 'ACCESS_TOKEN'; //todo
+ out.displayName = 'DISPLAY_NAME';
+ out.email = 'email@firebase.com';
+ out.thirdPartyUserData = {}; //todo
+ break;
+ case 'github':
+ out.accessToken = 'ACCESS_TOKEN'; //todo
+ out.displayName = 'DISPLAY_NAME';
+ out.thirdPartyUserData = {}; //todo
+ out.username = 'USERNAME';
+ break;
+ case 'facebook':
+ out.accessToken = 'ACCESS_TOKEN'; //todo
+ out.displayName = 'DISPLAY_NAME';
+ out.thirdPartyUserData = {}; //todo
+ break;
+ case 'anonymous':
+ break;
+ default:
+ throw new Error('Invalid auth provider', provider);
+ }
+
+ return out;
+}
+
+function ref(path, autoSyncDelay) {
+ var reference = new MockFirebase();
+ reference.flushDelay = _.isUndefined(autoSyncDelay)? true : autoSyncDelay;
+ if( path ) { reference = reference.child(path); }
+ return reference;
+}
+
+function mergePaths(base, add) {
+ return base.replace(/\/$/, '')+'/'+add.replace(/^\//, '');
+}
+
+function makeRefSnap(ref) {
+ return makeSnap(ref, ref.getData(), ref.priority);
+}
+
+function makeSnap(ref, data, pri) {
+ data = _.cloneDeep(data);
+ if(_.isObject(data) && _.isEmpty(data)) { data = null; }
+ return {
+ val: function() { return data; },
+ ref: function() { return ref; },
+ name: function() { return ref.name(); },
+ getPriority: function() { return pri; },
+ forEach: function(cb, scope) {
+ var self = this;
+ _.each(data, function(v, k) {
+ var res = cb.call(scope, self.child(k));
+ return res !== true;
+ });
+ },
+ child: function(key) {
+ return makeSnap(ref.child(key), _.isObject(data) && _.has(data, key)? data[key] : null, ref.child(key).priority);
+ }
+ };
+}
+
+function extractName(path) {
+ return ((path || '').match(/\/([^.$\[\]#\/]+)$/)||[null, null])[1];
+}
+
+function createError(code, message) {
+ return { code: code||'UNKNOWN_ERROR', message: 'FirebaseSimpleLogin: '+(message||code||'unspecific error') };
+}
+
+function getMeta(data, key, defaultVal) {
+ var val = defaultVal;
+ var metaKey = '.' + key;
+ if( _.isObject(data) && _.has(data, metaKey) ) {
+ val = data[metaKey];
+ delete data[metaKey];
+ }
+ return val;
+}
+
+function cleanData(data) {
+ var newData = _.clone(data);
+ if(_.isObject(newData)) {
+ if(_.has(newData, '.value')) {
+ newData = _.clone(newData['.value']);
+ }
+ if(_.has(newData, '.priority')) {
+ delete newData['.priority'];
+ }
+// _.each(newData, function(v,k) {
+// newData[k] = cleanData(v);
+// });
+ if(_.isEmpty(newData)) { newData = null; }
+ }
+ return newData;
+}
+
+function assertKey(method, key, argNum) {
+ if (!argNum) argNum = 'first';
+ if( typeof(key) !== 'string' || key.match(/[.#$\/\[\]]/) ) {
+ throw new Error(method + ' failed: '+argNum+' was an invalid key "'+(key+'')+'. Firebase keys must be non-empty strings and can\'t contain ".", "#", "$", "/", "[", or "]"');
+ }
+}
+
+function assertQuery(method, pri, key) {
+ if( pri !== null && typeof(pri) !== 'string' && typeof(pri) !== 'number' ) {
+ throw new Error(method + ' failed: first argument must be a valid firebase priority (a string, number, or null).');
+ }
+ if(!_.isUndefined(key)) {
+ assertKey(method, key, 'second');
+ }
+}
+
+/*** PUBLIC METHODS AND FIXTURES ***/
+
+MockFirebaseSimpleLogin.DEFAULT_FAIL_WHEN = function(provider, options, user) {
+ var res = null;
+ if( ['password', 'anonymous', 'twitter', 'facebook', 'google', 'github'].indexOf(provider) === -1 ) {
+ console.error('MockFirebaseSimpleLogin:login() failed: unrecognized authentication provider '+provider);
+// res = createError();
+ }
+ else if( !user ) {
+ res = createError('INVALID_USER', 'The specified user does not exist');
+ }
+ else if( provider === 'password' && user.password !== options.password ) {
+ res = createError('INVALID_PASSWORD', 'The specified password is incorrect');
+ }
+ return res;
+};
+
+MockFirebaseSimpleLogin.DEFAULT_USER_DATA = {};
+_.each(['password', 'anonymous', 'facebook', 'twitter', 'google', 'github'], function(provider) {
+ var user = createDefaultUser(provider);
+ if( provider !== 'password' ) {
+ MockFirebaseSimpleLogin.DEFAULT_USER_DATA[provider] = user;
+ }
+ else {
+ var set = MockFirebaseSimpleLogin.DEFAULT_USER_DATA[provider] = {};
+ set[user.email] = user;
+ }
+});
+
+
+MockFirebase.md5 = md5;
+MockFirebaseSimpleLogin.DEFAULT_AUTO_FLUSH = false;
+
+MockFirebase._ = _; // expose for tests
+MockFirebase.Query = MockQuery; // expose for tests
+
+MockFirebase.override = function () {
+ /* global window */
+ if (typeof window !== 'undefined') {
+ MockFirebase._origFirebase = window.Firebase;
+ MockFirebase._origFirebaseSimpleLogin = window.FirebaseSimpleLogin;
+ window.Firebase = MockFirebase;
+ window.FirebaseSimpleLogin = MockFirebaseSimpleLogin;
+ }
+ else {
+ console.warn('MockFirebase.override is only useful in a browser environment.');
+ }
+};
+
+MockFirebase.ref = ref;
+MockFirebase.DEFAULT_DATA = {
+ 'data': {
+ 'a': {
+ aString: 'alpha',
+ aNumber: 1,
+ aBoolean: false
+ },
+ 'b': {
+ aString: 'bravo',
+ aNumber: 2,
+ aBoolean: true
+ },
+ 'c': {
+ aString: 'charlie',
+ aNumber: 3,
+ aBoolean: true
+ },
+ 'd': {
+ aString: 'delta',
+ aNumber: 4,
+ aBoolean: true
+ },
+ 'e': {
+ aString: 'echo',
+ aNumber: 5
+ }
+ },
+ 'index': {
+ 'b': true,
+ 'c': 1,
+ 'e': false,
+ 'z': true // must not exist in `data`
+ },
+ 'ordered': {
+ 'null_a': {
+ aNumber: 0,
+ aLetter: 'a'
+ },
+ 'null_b': {
+ aNumber: 0,
+ aLetter: 'b'
+ },
+ 'null_c': {
+ aNumber: 0,
+ aLetter: 'c'
+ },
+ 'num_1_a': {
+ '.priority': 1,
+ aNumber: 1
+ },
+ 'num_1_b': {
+ '.priority': 1,
+ aNumber: 1
+ },
+ 'num_2': {
+ '.priority': 2,
+ aNumber: 2
+ },
+ 'num_3': {
+ '.priority': 3,
+ aNumber: 3
+ },
+ 'char_a_1': {
+ '.priority': 'a',
+ aNumber: 1,
+ aLetter: 'a'
+ },
+ 'char_a_2': {
+ '.priority': 'a',
+ aNumber: 2,
+ aLetter: 'a'
+ },
+ 'char_b': {
+ '.priority': 'b',
+ aLetter: 'b'
+ },
+ 'char_c': {
+ '.priority': 'c',
+ aLetter: 'c'
+ }
+ }
+};
+
+exports.MockFirebase = MockFirebase;
+exports.MockFirebaseSimpleLogin = MockFirebaseSimpleLogin;
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"MD5":1,"lodash":7}]},{},[8])
+(8)
+});;(function (window) {
+ 'use strict';
+ if (typeof window === 'object') {
+ window.MockFirebase = window.mockfirebase.MockFirebase;
+ window.MockFirebaseSimpleLogin = window.mockfirebase.MockFirebaseSimpleLogin;
+ }
+})(window);
+
+/*!
+ * AngularFire is the officially supported AngularJS binding for Firebase. Firebase
+ * is a full backend so you don't need servers to build your Angular app. AngularFire
+ * provides you with the $firebase service which allows you to easily keep your $scope
+ * variables in sync with your Firebase backend.
+ *
+ * AngularFire 0.0.0
+ * https://github.com/firebase/angularfire/
+ * Date: 08/28/2014
+ * License: MIT
+ */
+!function(a){"use strict";angular.module("firebase",[]).value("Firebase",a.Firebase).value("firebaseBatchDelay",50)}(window),function(){"use strict";angular.module("firebase").factory("$FirebaseArray",["$log","$firebaseUtils",function(a,b){function c(a,c,d){var e=this;return this._observers=[],this.$list=[],this._inst=a,this._promise=d,this._destroyFn=c,b.getPublicMethods(e,function(a,b){e.$list[b]=a.bind(e)}),this.$list}return c.prototype={$add:function(a){return this._assertNotDestroyed("$add"),this.$inst().$push(b.toJSON(a))},$save:function(a){this._assertNotDestroyed("$save");var c=this,d=c._resolveItem(a),e=c.$keyAt(d);return null!==e?c.$inst().$set(e,b.toJSON(d)).then(function(a){return c._notify("child_changed",e),a}):b.reject("Invalid record; could determine its key: "+a)},$remove:function(a){this._assertNotDestroyed("$remove");var c=this.$keyAt(a);return null!==c?this.$inst().$remove(c):b.reject("Invalid record; could not find key: "+a)},$keyAt:function(a){var b=this._resolveItem(a);return this._getKey(b)},$indexFor:function(a){var b=this;return this.$list.findIndex(function(c){return b._getKey(c)===a})},$loaded:function(a,b){var c=this._promise;return arguments.length&&(c=c.then.call(c,a,b)),c},$inst:function(){return this._inst},$watch:function(a,b){var c=this._observers;return c.push([a,b]),function(){var d=c.findIndex(function(c){return c[0]===a&&c[1]===b});d>-1&&c.splice(d,1)}},$destroy:function(b){this._isDestroyed||(this._isDestroyed=!0,this.$list.length=0,a.debug("destroy called for FirebaseArray: "+this.$inst().$ref().toString()),this._destroyFn(b))},$getRecord:function(a){var b=this.$indexFor(a);return b>-1?this.$list[b]:null},$$added:function(a,c){var d=this.$indexFor(a.name());if(-1===d){var e=a.val();angular.isObject(e)||(e={$value:e}),e.$id=a.name(),e.$priority=a.getPriority(),b.applyDefaults(e,this.$$defaults),this._process("child_added",e,c)}},$$removed:function(a){var b=this.$getRecord(a.name());angular.isObject(b)&&this._process("child_removed",b)},$$updated:function(a){var c=this.$getRecord(a.name());if(angular.isObject(c)){var d=b.updateRec(c,a);b.applyDefaults(c,this.$$defaults),d&&this._process("child_changed",c)}},$$moved:function(a,b){var c=this.$getRecord(a.name());angular.isObject(c)&&(c.$priority=a.getPriority(),this._process("child_moved",c,b))},$$error:function(b){a.error(b),this.$destroy(b)},_getKey:function(a){return angular.isObject(a)?a.$id:null},_process:function(a,b,c){var d,e=this._getKey(b),f=!1;switch(a){case"child_added":d=this.$indexFor(e);break;case"child_moved":d=this.$indexFor(e),this._spliceOut(e);break;case"child_removed":f=null!==this._spliceOut(e);break;case"child_changed":f=!0}return angular.isDefined(d)&&(f=this._addAfter(b,c)!==d),f&&this._notify(a,e,c),f},_notify:function(a,b,c){var d={event:a,key:b};angular.isDefined(c)&&(d.prevChild=c),angular.forEach(this._observers,function(a){a[0].call(a[1],d)})},_addAfter:function(a,b){var c;return null===b?c=0:(c=this.$indexFor(b)+1,0===c&&(c=this.$list.length)),this.$list.splice(c,0,a),c},_spliceOut:function(a){var b=this.$indexFor(a);return b>-1?this.$list.splice(b,1)[0]:null},_resolveItem:function(a){var b=this.$list;if(angular.isNumber(a)&&a>=0&&b.length>=a)return b[a];if(angular.isObject(a))for(var c=b.length;c--;)if(b[c]===a)return a;return null},_assertNotDestroyed:function(a){if(this._isDestroyed)throw new Error("Cannot call "+a+" method on a destroyed $FirebaseArray object")}},c.$extendFactory=function(a,d){return 1===arguments.length&&angular.isObject(a)&&(d=a,a=function(){return c.apply(this,arguments)}),b.inherit(a,c,d)},c}])}(),function(){"use strict";angular.module("firebase").factory("$FirebaseObject",["$parse","$firebaseUtils","$log","$interval",function(a,b,c,d){function e(a,c,d){this.$$conf={promise:d,inst:a,binding:new f(this),destroyFn:c,listeners:[]},Object.defineProperty(this,"$$conf",{value:this.$$conf}),this.$id=a.$ref().ref().name(),this.$priority=null,b.applyDefaults(this,this.$$defaults)}function f(a){this.subs=[],this.scope=null,this.name=null,this.rec=a}return e.prototype={$save:function(){var a=this;return a.$inst().$set(b.toJSON(a)).then(function(b){return a.$$notify(),b})},$loaded:function(a,b){var c=this.$$conf.promise;return arguments.length&&(c=c.then.call(c,a,b)),c},$inst:function(){return this.$$conf.inst},$bindTo:function(a,b){var c=this;return c.$loaded().then(function(){return c.$$conf.binding.bindTo(a,b)})},$watch:function(a,b){var c=this.$$conf.listeners;return c.push([a,b]),function(){var d=c.findIndex(function(c){return c[0]===a&&c[1]===b});d>-1&&c.splice(d,1)}},$destroy:function(a){var c=this;c.$isDestroyed||(c.$isDestroyed=!0,c.$$conf.binding.destroy(),b.each(c,function(a,b){delete c[b]}),c.$$conf.destroyFn(a))},$$updated:function(a){var c=b.updateRec(this,a);b.applyDefaults(this,this.$$defaults),c&&this.$$notify()},$$error:function(a){c.error(a),this.$destroy(a)},$$scopeUpdated:function(a){return this.$inst().$set(b.toJSON(a))},$$notify:function(){var a=this,b=this.$$conf.listeners.slice();angular.forEach(b,function(b){b[0].call(b[1],{event:"value",key:a.$id})})},forEach:function(a,c){return b.each(this,a,c)}},e.$extendFactory=function(a,c){return 1===arguments.length&&angular.isObject(a)&&(c=a,a=function(){e.apply(this,arguments)}),b.inherit(a,e,c)},f.prototype={assertNotBound:function(a){if(this.scope){var d="Cannot bind to "+a+" because this instance is already bound to "+this.name+"; one binding per instance (call unbind method or create another $firebase instance)";return c.error(d),b.reject(d)}},bindTo:function(c,e){function f(f){function g(a){var c=h(),d=b.scopeData(a);return angular.equals(c,d)&&c.$priority===a.$priority&&c.$value===a.$value}function h(){return b.scopeData(l(c))}function i(a){l.assign(c,b.scopeData(a))}function j(){var a=l(c);(a.$value!==m.$value||a.$priority!==m.$priority)&&n()}var k=!1,l=a(e),m=f.rec;f.scope=c,f.varName=e;var n=function(){var a=b.debounce(function(){m.$$scopeUpdated(h())["finally"](function(){k=!1})},50,500);g(m)||(k=!0,a())},o=function(){k||g(m)||i(m)};return function(){var a="_firebaseCounterForVar"+e;c[a]=0;var b=d(function(){c[a]++},51,0,!1);f.subs.push(c.$watch(a,j)),f.subs.push(function(){d.cancel(b),delete c[a]})}(),i(m),f.subs.push(c.$on("$destroy",f.unbind.bind(f))),f.subs.push(c.$watch(e,n,!0)),f.subs.push(m.$watch(o)),f.unbind.bind(f)}return this.assertNotBound(e)||f(this)},unbind:function(){this.scope&&(angular.forEach(this.subs,function(a){a()}),this.subs=[],this.scope=null,this.name=null)},destroy:function(){this.unbind(),this.rec=null}},e}])}(),function(){"use strict";angular.module("firebase").factory("$firebase",["$firebaseUtils","$firebaseConfig",function(a,b){function c(a,d){return this instanceof c?(this._config=b(d),this._ref=a,this._arraySync=null,this._objectSync=null,void this._assertValidConfig(a,this._config)):new c(a,d)}function d(b,c){function d(a){q.isDestroyed=!0;var c=b.$ref();c.off("child_added",k),c.off("child_moved",m),c.off("child_changed",l),c.off("child_removed",n),i=null,p(a||"destroyed")}function e(){var a=b.$ref();a.on("child_added",k,o),a.on("child_moved",m,o),a.on("child_changed",l,o),a.on("child_removed",n,o),a.once("value",function(){p(null)},p)}function f(a){h&&(a?h.reject(a):h.resolve(i),h=null)}function g(a){if(!angular.isArray(a)){var b=Object.prototype.toString.call(a);throw new Error('arrayFactory must return a valid array that passes angular.isArray and Array.isArray, but received "'+b+'"')}}var h=a.defer(),i=new c(b,d,h.promise),j=a.batch(),k=j(i.$$added,i),l=j(i.$$updated,i),m=j(i.$$moved,i),n=j(i.$$removed,i),o=j(i.$$error,i),p=j(f),q=this;q.isDestroyed=!1,q.getArray=function(){return i},g(i),e()}function e(b,c){function d(a){n.isDestroyed=!0,i.off("value",k),h=null,m(a||"destroyed")}function e(){i.on("value",k,l),i.once("value",function(){m(null)},m)}function f(a){g&&(a?g.reject(a):g.resolve(h),g=null)}var g=a.defer(),h=new c(b,d,g.promise),i=b.$ref(),j=a.batch(),k=j(h.$$updated,h),l=j(h.$$error,h),m=j(f),n=this;n.isDestroyed=!1,n.getObject=function(){return h},e()}return c.prototype={$ref:function(){return this._ref},$push:function(b){var c=a.defer(),d=this._ref.ref().push(),e=this._handle(c,d);return arguments.length>0?d.set(b,e):e(),c.promise},$set:function(b,c){var d=this._ref,e=a.defer();if(arguments.length>1?d=d.ref().child(b):c=b,angular.isFunction(d.set)||!angular.isObject(c))d.ref().set(c,this._handle(e,d));else{var f=angular.extend({},c);d.once("value",function(a){a.forEach(function(a){f.hasOwnProperty(a.name())||(f[a.name()]=null)}),d.ref().update(f,this._handle(e,d))},this)}return e.promise},$remove:function(b){var c,d=this._ref,e=this,f=a.defer();if(arguments.length>0&&(d=d.ref().child(b)),angular.isFunction(d.remove))d.remove(e._handle(f,d)),c=f.promise;else{var g=[];d.once("value",function(b){b.forEach(function(b){var c=a.defer();g.push(c),b.ref().remove(e._handle(c,b.ref()))},e)}),c=a.allPromises(g).then(function(){return d})}return c},$update:function(b,c){var d=this._ref.ref(),e=a.defer();return arguments.length>1?d=d.child(b):c=b,d.update(c,this._handle(e,d)),e.promise},$transaction:function(b,c,d){var e=this._ref.ref();angular.isFunction(b)?(d=c,c=b):e=e.child(b),d=!!d;var f=a.defer();return e.transaction(c,function(a,b,c){a?f.reject(a):f.resolve(b?c:null)},d),f.promise},$asObject:function(){return(!this._objectSync||this._objectSync.isDestroyed)&&(this._objectSync=new e(this,this._config.objectFactory)),this._objectSync.getObject()},$asArray:function(){return(!this._arraySync||this._arraySync.isDestroyed)&&(this._arraySync=new d(this,this._config.arrayFactory)),this._arraySync.getArray()},_handle:function(a){var b=Array.prototype.slice.call(arguments,1);return function(c){c?a.reject(c):a.resolve.apply(a,b)}},_assertValidConfig:function(b,c){if(a.assertValidRef(b,"Must pass a valid Firebase reference to $firebase (not a string or URL)"),!angular.isFunction(c.arrayFactory))throw new Error("config.arrayFactory must be a valid function");if(!angular.isFunction(c.objectFactory))throw new Error("config.objectFactory must be a valid function")}},c}])}(),function(){"use strict";var a;angular.module("firebase").factory("$firebaseSimpleLogin",["$q","$timeout","$rootScope",function(b,c,d){return function(e){var f=new a(b,c,d,e);return f.construct()}}]),a=function(a,b,c,d){if(this._q=a,this._timeout=b,this._rootScope=c,this._loginDeferred=null,this._getCurrentUserDeferred=[],this._currentUserData=void 0,"string"==typeof d)throw new Error("Please provide a Firebase reference instead of a URL, eg: new Firebase(url)");this._fRef=d},a.prototype={construct:function(){var a={user:null,$login:this.login.bind(this),$logout:this.logout.bind(this),$createUser:this.createUser.bind(this),$changePassword:this.changePassword.bind(this),$removeUser:this.removeUser.bind(this),$getCurrentUser:this.getCurrentUser.bind(this),$sendPasswordResetEmail:this.sendPasswordResetEmail.bind(this)};if(this._object=a,!window.FirebaseSimpleLogin){var b=new Error("FirebaseSimpleLogin is undefined. Did you forget to include firebase-simple-login.js?");throw this._rootScope.$broadcast("$firebaseSimpleLogin:error",b),b}var c=new FirebaseSimpleLogin(this._fRef,this._onLoginEvent.bind(this));return this._authClient=c,this._object},login:function(a,b){var c=this._q.defer(),d=this;return this.getCurrentUser().then(function(){d._loginDeferred=c,d._authClient.login(a,b)}),c.promise},logout:function(){this._authClient.logout(),delete this._currentUserData},createUser:function(a,b){var c=this,d=this._q.defer();return c._authClient.createUser(a,b,function(a,b){a?(c._rootScope.$broadcast("$firebaseSimpleLogin:error",a),d.reject(a)):d.resolve(b)}),d.promise},changePassword:function(a,b,c){var d=this,e=this._q.defer();return d._authClient.changePassword(a,b,c,function(a){a?(d._rootScope.$broadcast("$firebaseSimpleLogin:error",a),e.reject(a)):e.resolve()}),e.promise},getCurrentUser:function(){var a=this,b=this._q.defer();return void 0!==a._currentUserData?b.resolve(a._currentUserData):a._getCurrentUserDeferred.push(b),b.promise},removeUser:function(a,b){var c=this,d=this._q.defer();return c._authClient.removeUser(a,b,function(a){a?(c._rootScope.$broadcast("$firebaseSimpleLogin:error",a),d.reject(a)):d.resolve()}),d.promise},sendPasswordResetEmail:function(a){var b=this,c=this._q.defer();return b._authClient.sendPasswordResetEmail(a,function(a){a?(b._rootScope.$broadcast("$firebaseSimpleLogin:error",a),c.reject(a)):c.resolve()}),c.promise},_onLoginEvent:function(a,b){if(this._currentUserData!==b||null!==a){var c=this;a?(c._loginDeferred&&(c._loginDeferred.reject(a),c._loginDeferred=null),c._rootScope.$broadcast("$firebaseSimpleLogin:error",a)):(this._currentUserData=b,c._timeout(function(){for(c._object.user=b,b?c._rootScope.$broadcast("$firebaseSimpleLogin:login",b):c._rootScope.$broadcast("$firebaseSimpleLogin:logout"),c._loginDeferred&&(c._loginDeferred.resolve(b),c._loginDeferred=null);c._getCurrentUserDeferred.length>0;){var a=c._getCurrentUserDeferred.pop();a.resolve(b)}}))}}}}(),Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){if(void 0===this||null===this)throw new TypeError("'this' is null or not defined");var c=this.length>>>0;for(b=+b||0,1/0===Math.abs(b)&&(b=0),0>b&&(b+=c,0>b&&(b=0));c>b;b++)if(this[b]===a)return b;return-1}),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),Array.prototype.findIndex||Object.defineProperty(Array.prototype,"findIndex",{enumerable:!1,configurable:!0,writable:!0,value:function(a){if(null==this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof a)throw new TypeError("predicate must be a function");for(var b,c=Object(this),d=c.length>>>0,e=arguments[1],f=0;d>f;f++)if(f in c&&(b=c[f],a.call(e,b,f,c)))return f;return-1}}),"function"!=typeof Object.create&&!function(){var a=function(){};Object.create=function(b){if(arguments.length>1)throw new Error("Second argument not supported");if(null===b)throw new Error("Cannot set a null [[Prototype]]");if("object"!=typeof b)throw new TypeError("Argument must be an object");return a.prototype=b,new a}}(),Object.keys||(Object.keys=function(){"use strict";var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&("function"!=typeof e||null===e))throw new TypeError("Object.keys called on non-object");var f,g,h=[];for(f in e)a.call(e,f)&&h.push(f);if(b)for(g=0;d>g;g++)a.call(e,c[g])&&h.push(c[g]);return h}}()),"function"!=typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"==typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype}),function(){"use strict";function a(b){if(!angular.isObject(b))return b;var c=angular.isArray(b)?[]:{};return angular.forEach(b,function(b,d){("string"!=typeof d||"$"!==d.charAt(0))&&(c[d]=a(b))}),c}angular.module("firebase").factory("$firebaseConfig",["$FirebaseArray","$FirebaseObject","$injector",function(a,b,c){return function(d){var e=angular.extend({},d);return"string"==typeof e.objectFactory&&(e.objectFactory=c.get(e.objectFactory)),"string"==typeof e.arrayFactory&&(e.arrayFactory=c.get(e.arrayFactory)),angular.extend({arrayFactory:a,objectFactory:b},e)}}]).factory("$firebaseUtils",["$q","$timeout","firebaseBatchDelay",function(b,c,d){var e={batch:function(a,b){function c(a,b){if("function"!=typeof a)throw new Error("Must provide a function to be batched. Got "+a);return function(){var c=Array.prototype.slice.call(arguments,0);j.push([a,b,c]),f()}}function f(){i&&(i(),i=null),h&&Date.now()-h>b?e.compile(g):(h||(h=Date.now()),i=e.wait(g,a))}function g(){i=null,h=null;var a=j.slice(0);j=[],angular.forEach(a,function(a){a[0].apply(a[1],a[2])})}a=d,b||(b=10*a||100);var h,i,j=[];return c},debounce:function(a,b,c,d){function f(){j&&(j(),j=null),i&&Date.now()-i>d?e.compile(g):(i||(i=Date.now()),j=e.wait(g,c))}function g(){j=null,i=null,a.apply(b,k)}function h(){k=Array.prototype.slice.call(arguments,0),f()}var i,j,k;if("number"==typeof b&&(d=c,c=b,b=null),"number"!=typeof c)throw new Error("Must provide a valid integer for wait. Try 0 for a default");if("function"!=typeof a)throw new Error("Must provide a valid function to debounce");return d||(d=10*c||100),h.running=function(){return i>0},h},assertValidRef:function(a,b){if(!angular.isObject(a)||"function"!=typeof a.ref||"function"!=typeof a.ref().transaction)throw new Error(b||"Invalid Firebase reference")},inherit:function(a,b,c){var d=a.prototype;return a.prototype=Object.create(b.prototype),a.prototype.constructor=a,angular.forEach(Object.keys(d),function(b){a.prototype[b]=d[b]}),angular.isObject(c)&&angular.extend(a.prototype,c),a},getPrototypeMethods:function(a,b,c){for(var d={},e=Object.getPrototypeOf({}),f=angular.isFunction(a)&&angular.isObject(a.prototype)?a.prototype:Object.getPrototypeOf(a);f&&f!==e;){for(var g in f)f.hasOwnProperty(g)&&!d.hasOwnProperty(g)&&(d[g]=!0,b.call(c,f[g],g,f));f=Object.getPrototypeOf(f)}},getPublicMethods:function(a,b,c){e.getPrototypeMethods(a,function(a,d){"function"==typeof a&&"_"!==d.charAt(0)&&b.call(c,a,d)})},defer:function(){return b.defer()},reject:function(a){var b=e.defer();return b.reject(a),b.promise},resolve:function(){var a=e.defer();return a.resolve.apply(a,arguments),a.promise},wait:function(a,b){var d=c(a,b||0);return function(){d&&(c.cancel(d),d=null)}},compile:function(a){return c(a||function(){})},deepCopy:function(a){if(!angular.isObject(a))return a;var b=angular.isArray(a)?a.slice():angular.extend({},a);for(var c in b)b.hasOwnProperty(c)&&angular.isObject(b[c])&&(b[c]=e.deepCopy(b[c]));return b},trimKeys:function(a,b){e.each(a,function(c,d){b.hasOwnProperty(d)||delete a[d]})},extendData:function(a,b){return e.each(b,function(b,c){a[c]=e.deepCopy(b)}),a},scopeData:function(a){var b={$id:a.$id,$priority:a.$priority};return a.hasOwnProperty("$value")&&(b.$value=a.$value),e.extendData(b,a)},updateRec:function(a,b){var c=b.val(),d=angular.extend({},a);return angular.isObject(c)?delete a.$value:(a.$value=c,c={}),e.trimKeys(a,c),angular.extend(a,c),a.$priority=b.getPriority(),!angular.equals(d,a)||d.$value!==a.$value||d.$priority!==a.$priority},applyDefaults:function(a,b){return angular.isObject(b)&&angular.forEach(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)}),a},dataKeys:function(a){var b=[];return e.each(a,function(a,c){b.push(c)}),b},each:function(a,b,c){if(angular.isObject(a)){for(var d in a)if(a.hasOwnProperty(d)){var e=d.charAt(0);"_"!==e&&"$"!==e&&"."!==e&&b.call(c,a[d],d,a)}}else if(angular.isArray(a))for(var f=0,g=a.length;g>f;f++)b.call(c,a[f],f,a);return a},toJSON:function(b){var c;return angular.isObject(b)||(b={$value:b}),angular.isFunction(b.toJSON)?c=b.toJSON():(c={},e.each(b,function(b,d){c[d]=a(b)})),angular.isDefined(b.$value)&&0===Object.keys(c).length&&null!==b.$value&&(c[".value"]=b.$value),angular.isDefined(b.$priority)&&Object.keys(c).length>0&&null!==b.$priority&&(c[".priority"]=b.$priority),angular.forEach(c,function(a,b){if(b.match(/[.$\[\]#\/]/)&&".value"!==b&&".priority"!==b)throw new Error("Invalid key "+b+" (cannot contain .$[]#)");if(angular.isUndefined(a))throw new Error("Key "+b+" was undefined. Cannot pass undefined in JSON. Use null instead.")}),c},batchDelay:d,allPromises:b.all.bind(b)};return e}])}();
+/**
+ * @license
+ * lodash 3.1.0 (Custom Build)
+ * Build: `lodash modern -o ./lodash.js`
+ * Copyright 2012-2015 The Dojo Foundation
+ * Based on Underscore.js 1.7.0
+ * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre-ES5 environments. */
+ var undefined;
+
+ /** Used as the semantic version number. */
+ var VERSION = '3.1.0';
+
+ /** Used to compose bitmasks for wrapper metadata. */
+ var BIND_FLAG = 1,
+ BIND_KEY_FLAG = 2,
+ CURRY_BOUND_FLAG = 4,
+ CURRY_FLAG = 8,
+ CURRY_RIGHT_FLAG = 16,
+ PARTIAL_FLAG = 32,
+ PARTIAL_RIGHT_FLAG = 64,
+ REARG_FLAG = 128,
+ ARY_FLAG = 256;
+
+ /** Used as default options for `_.trunc`. */
+ var DEFAULT_TRUNC_LENGTH = 30,
+ DEFAULT_TRUNC_OMISSION = '...';
+
+ /** Used to detect when a function becomes hot. */
+ var HOT_COUNT = 150,
+ HOT_SPAN = 16;
+
+ /** Used to indicate the type of lazy iteratees. */
+ var LAZY_FILTER_FLAG = 0,
+ LAZY_MAP_FLAG = 1,
+ LAZY_WHILE_FLAG = 2;
+
+ /** Used as the `TypeError` message for "Functions" methods. */
+ var FUNC_ERROR_TEXT = 'Expected a function';
+
+ /** Used as the internal argument placeholder. */
+ var PLACEHOLDER = '__lodash_placeholder__';
+
+ /** `Object#toString` result references. */
+ var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ objectTag = '[object Object]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ weakMapTag = '[object WeakMap]';
+
+ var arrayBufferTag = '[object ArrayBuffer]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+ /** Used to match empty string literals in compiled template source. */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+ /** Used to match HTML entities and HTML characters. */
+ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g,
+ reUnescapedHtml = /[&<>"'`]/g,
+ reHasEscapedHtml = RegExp(reEscapedHtml.source),
+ reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
+
+ /** Used to match template delimiters. */
+ var reEscape = /<%-([\s\S]+?)%>/g,
+ reEvaluate = /<%([\s\S]+?)%>/g,
+ reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /**
+ * Used to match ES template delimiters.
+ * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components)
+ * for more details.
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+ /** Used to match `RegExp` flags from their coerced string values. */
+ var reFlags = /\w*$/;
+
+ /** Used to detect named functions. */
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+ /** Used to detect hexadecimal string values. */
+ var reHexPrefix = /^0[xX]/;
+
+ /** Used to detect host constructors (Safari > 5). */
+ var reHostCtor = /^\[object .+?Constructor\]$/;
+
+ /** Used to match latin-1 supplementary letters (excluding mathematical operators). */
+ var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
+
+ /** Used to ensure capturing order of template delimiters. */
+ var reNoMatch = /($^)/;
+
+ /**
+ * Used to match `RegExp` special characters.
+ * See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
+ * for more details.
+ */
+ var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
+ reHasRegExpChars = RegExp(reRegExpChars.source);
+
+ /** Used to detect functions containing a `this` reference. */
+ var reThis = /\bthis\b/;
+
+ /** Used to match unescaped characters in compiled string literals. */
+ var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
+
+ /** Used to match words to create compound words. */
+ var reWords = (function() {
+ var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]',
+ lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+';
+
+ return RegExp(upper + '{2,}(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g');
+ }());
+
+ /** Used to detect and test for whitespace. */
+ var whitespace = (
+ // Basic whitespace characters.
+ ' \t\x0b\f\xa0\ufeff' +
+
+ // Line terminators.
+ '\n\r\u2028\u2029' +
+
+ // Unicode category "Zs" space separators.
+ '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+ );
+
+ /** Used to assign default `context` object properties. */
+ var contextProps = [
+ 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array',
+ 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number',
+ 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document',
+ 'isFinite', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array',
+ 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
+ 'window', 'WinRTError'
+ ];
+
+ /** Used to make template sourceURLs easier to identify. */
+ var templateCounter = -1;
+
+ /** Used to identify `toStringTag` values of typed arrays. */
+ var typedArrayTags = {};
+ typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+ typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+ typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+ typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+ typedArrayTags[uint32Tag] = true;
+ typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+ typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+ typedArrayTags[dateTag] = typedArrayTags[errorTag] =
+ typedArrayTags[funcTag] = typedArrayTags[mapTag] =
+ typedArrayTags[numberTag] = typedArrayTags[objectTag] =
+ typedArrayTags[regexpTag] = typedArrayTags[setTag] =
+ typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
+
+ /** Used to identify `toStringTag` values supported by `_.clone`. */
+ var cloneableTags = {};
+ cloneableTags[argsTag] = cloneableTags[arrayTag] =
+ cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
+ cloneableTags[dateTag] = cloneableTags[float32Tag] =
+ cloneableTags[float64Tag] = cloneableTags[int8Tag] =
+ cloneableTags[int16Tag] = cloneableTags[int32Tag] =
+ cloneableTags[numberTag] = cloneableTags[objectTag] =
+ cloneableTags[regexpTag] = cloneableTags[stringTag] =
+ cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+ cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+ cloneableTags[errorTag] = cloneableTags[funcTag] =
+ cloneableTags[mapTag] = cloneableTags[setTag] =
+ cloneableTags[weakMapTag] = false;
+
+ /** Used as an internal `_.debounce` options object by `_.throttle`. */
+ var debounceOptions = {
+ 'leading': false,
+ 'maxWait': 0,
+ 'trailing': false
+ };
+
+ /** Used to map latin-1 supplementary letters to basic latin letters. */
+ var deburredLetters = {
+ '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
+ '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
+ '\xc7': 'C', '\xe7': 'c',
+ '\xd0': 'D', '\xf0': 'd',
+ '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
+ '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
+ '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
+ '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i',
+ '\xd1': 'N', '\xf1': 'n',
+ '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
+ '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
+ '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
+ '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
+ '\xdd': 'Y', '\xfd': 'y', '\xff': 'y',
+ '\xc6': 'Ae', '\xe6': 'ae',
+ '\xde': 'Th', '\xfe': 'th',
+ '\xdf': 'ss'
+ };
+
+ /** Used to map characters to HTML entities. */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '`': '`'
+ };
+
+ /** Used to map HTML entities to characters. */
+ var htmlUnescapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ ''': "'",
+ '`': '`'
+ };
+
+ /** Used to determine if values are of the language type `Object`. */
+ var objectTypes = {
+ 'function': true,
+ 'object': true
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals. */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /**
+ * Used as a reference to the global object.
+ *
+ * The `this` value is used if it is the global object to avoid Greasemonkey's
+ * restricted `window` object, otherwise the `window` object is used.
+ */
+ var root = (objectTypes[typeof window] && window !== (this && this.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;
+ }
+
+ /** Detect the popular CommonJS extension `module.exports`. */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `compareAscending` which compares values and
+ * sorts them in ascending order without guaranteeing a stable sort.
+ *
+ * @private
+ * @param {*} value The value to compare to `other`.
+ * @param {*} other The value to compare to `value`.
+ * @returns {number} Returns the sort order indicator for `value`.
+ */
+ function baseCompareAscending(value, other) {
+ if (value !== other) {
+ var valIsReflexive = value === value,
+ othIsReflexive = other === other;
+
+ if (value > other || !valIsReflexive || (typeof value == 'undefined' && othIsReflexive)) {
+ return 1;
+ }
+ if (value < other || !othIsReflexive || (typeof other == 'undefined' && valIsReflexive)) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ if (value !== value) {
+ return indexOfNaN(array, fromIndex);
+ }
+ var index = (fromIndex || 0) - 1,
+ length = array.length;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * The base implementation of `_.sortBy` and `_.sortByAll` which uses `comparer`
+ * to define the sort order of `array` and replaces criteria objects with their
+ * corresponding values.
+ *
+ * @private
+ * @param {Array} array The array to sort.
+ * @param {Function} comparer The function to define sort order.
+ * @returns {Array} Returns `array`.
+ */
+ function baseSortBy(array, comparer) {
+ var length = array.length;
+
+ array.sort(comparer);
+ while (length--) {
+ array[length] = array[length].value;
+ }
+ return array;
+ }
+
+ /**
+ * Converts `value` to a string if it is not one. An empty string is returned
+ * for `null` or `undefined` values.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ */
+ function baseToString(value) {
+ if (typeof value == 'string') {
+ return value;
+ }
+ return value == null ? '' : (value + '');
+ }
+
+ /**
+ * Used by `_.max` and `_.min` as the default callback for string values.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the code unit of the first character of the string.
+ */
+ function charAtCallback(string) {
+ return string.charCodeAt(0);
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimLeft` to get the index of the first character
+ * of `string` that is not found in `chars`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @param {string} chars The characters to find.
+ * @returns {number} Returns the index of the first character not found in `chars`.
+ */
+ function charsLeftIndex(string, chars) {
+ var index = -1,
+ length = string.length;
+
+ while (++index < length && chars.indexOf(string.charAt(index)) > -1) {}
+ return index;
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimRight` to get the index of the last character
+ * of `string` that is not found in `chars`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @param {string} chars The characters to find.
+ * @returns {number} Returns the index of the last character not found in `chars`.
+ */
+ function charsRightIndex(string, chars) {
+ var index = string.length;
+
+ while (index-- && chars.indexOf(string.charAt(index)) > -1) {}
+ return index;
+ }
+
+ /**
+ * Used by `_.sortBy` to compare transformed elements of a collection and stable
+ * sort them in ascending order.
+ *
+ * @private
+ * @param {Object} object The object to compare to `other`.
+ * @param {Object} other The object to compare to `object`.
+ * @returns {number} Returns the sort order indicator for `object`.
+ */
+ function compareAscending(object, other) {
+ return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index);
+ }
+
+ /**
+ * Used by `_.sortByAll` to compare multiple properties of each element
+ * in a collection and stable sort them in ascending order.
+ *
+ * @private
+ * @param {Object} object The object to compare to `other`.
+ * @param {Object} other The object to compare to `object`.
+ * @returns {number} Returns the sort order indicator for `object`.
+ */
+ function compareMultipleAscending(object, other) {
+ var index = -1,
+ objCriteria = object.criteria,
+ othCriteria = other.criteria,
+ length = objCriteria.length;
+
+ while (++index < length) {
+ var result = baseCompareAscending(objCriteria[index], othCriteria[index]);
+ if (result) {
+ return result;
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to provide the same value for
+ // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+ // for more details.
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See https://code.google.com/p/v8/issues/detail?id=90 for more details.
+ return object.index - other.index;
+ }
+
+ /**
+ * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.
+ *
+ * @private
+ * @param {string} letter The matched letter to deburr.
+ * @returns {string} Returns the deburred letter.
+ */
+ function deburrLetter(letter) {
+ return deburredLetters[letter];
+ }
+
+ /**
+ * Used by `_.escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(chr) {
+ return htmlEscapes[chr];
+ }
+
+ /**
+ * Used by `_.template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(chr) {
+ return '\\' + stringEscapes[chr];
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `NaN` is found in `array`.
+ * If `fromRight` is provided elements of `array` are iterated from right to left.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {number} [fromIndex] The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched `NaN`, else `-1`.
+ */
+ function indexOfNaN(array, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromRight ? (fromIndex || length) : ((fromIndex || 0) - 1);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ var other = array[index];
+ if (other !== other) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Checks if `value` is object-like.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ */
+ function isObjectLike(value) {
+ return (value && typeof value == 'object') || false;
+ }
+
+ /**
+ * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a
+ * character code is whitespace.
+ *
+ * @private
+ * @param {number} charCode The character code to inspect.
+ * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`.
+ */
+ function isSpace(charCode) {
+ return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 ||
+ (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279)));
+ }
+
+ /**
+ * Replaces all `placeholder` elements in `array` with an internal placeholder
+ * and returns an array of their indexes.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {*} placeholder The placeholder to replace.
+ * @returns {Array} Returns the new array of placeholder indexes.
+ */
+ function replaceHolders(array, placeholder) {
+ var index = -1,
+ length = array.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ if (array[index] === placeholder) {
+ array[index] = PLACEHOLDER;
+ result[++resIndex] = index;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * An implementation of `_.uniq` optimized for sorted arrays without support
+ * for callback shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The function invoked per iteration.
+ * @returns {Array} Returns the new duplicate-value-free array.
+ */
+ function sortedUniq(array, iteratee) {
+ var seen,
+ index = -1,
+ length = array.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value, index, array) : value;
+
+ if (!index || seen !== computed) {
+ seen = computed;
+ result[++resIndex] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace
+ * character of `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the index of the first non-whitespace character.
+ */
+ function trimmedLeftIndex(string) {
+ var index = -1,
+ length = string.length;
+
+ while (++index < length && isSpace(string.charCodeAt(index))) {}
+ return index;
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace
+ * character of `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the index of the last non-whitespace character.
+ */
+ function trimmedRightIndex(string) {
+ var index = string.length;
+
+ while (index-- && isSpace(string.charCodeAt(index))) {}
+ return index;
+ }
+
+ /**
+ * Used by `_.unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} chr The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ function unescapeHtmlChar(chr) {
+ return htmlUnescapes[chr];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Create a new pristine `lodash` function using the given `context` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utility
+ * @param {Object} [context=root] The context object.
+ * @returns {Function} Returns a new `lodash` function.
+ * @example
+ *
+ * _.mixin({ 'add': function(a, b) { return a + b; } });
+ *
+ * var lodash = _.runInContext();
+ * lodash.mixin({ 'sub': function(a, b) { return a - b; } });
+ *
+ * _.isFunction(_.add);
+ * // => true
+ * _.isFunction(_.sub);
+ * // => false
+ *
+ * lodash.isFunction(lodash.add);
+ * // => false
+ * lodash.isFunction(lodash.sub);
+ * // => true
+ *
+ * // using `context` to mock `Date#getTime` use in `_.now`
+ * var mock = _.runInContext({
+ * 'Date': function() {
+ * return { 'getTime': getTimeMock };
+ * }
+ * });
+ *
+ * // or creating a suped-up `defer` in Node.js
+ * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
+ */
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See https://es5.github.io/#x11.1.5 for more details.
+ context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+ /** Native constructor references. */
+ var Array = context.Array,
+ Date = context.Date,
+ Error = context.Error,
+ Function = context.Function,
+ Math = context.Math,
+ Number = context.Number,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String,
+ TypeError = context.TypeError;
+
+ /** Used for native method references. */
+ var arrayProto = Array.prototype,
+ objectProto = Object.prototype;
+
+ /** Used to detect DOM support. */
+ var document = (document = context.window) && document.document;
+
+ /** Used to resolve the decompiled source of functions. */
+ var fnToString = Function.prototype.toString;
+
+ /** Used to the length of n-tuples for `_.unzip`. */
+ var getLength = baseProperty('length');
+
+ /** Used to check objects for own properties. */
+ var hasOwnProperty = objectProto.hasOwnProperty;
+
+ /** Used to generate unique IDs. */
+ var idCounter = 0;
+
+ /**
+ * Used to resolve the `toStringTag` of values.
+ * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * for more details.
+ */
+ var objToString = objectProto.toString;
+
+ /** Used to restore the original `_` reference in `_.noConflict`. */
+ var oldDash = context._;
+
+ /** Used to detect if a method is native. */
+ var reNative = RegExp('^' +
+ escapeRegExp(objToString)
+ .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+ );
+
+ /** Native method references. */
+ var ArrayBuffer = isNative(ArrayBuffer = context.ArrayBuffer) && ArrayBuffer,
+ bufferSlice = isNative(bufferSlice = ArrayBuffer && new ArrayBuffer(0).slice) && bufferSlice,
+ ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ push = arrayProto.push,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ Set = isNative(Set = context.Set) && Set,
+ setTimeout = context.setTimeout,
+ splice = arrayProto.splice,
+ Uint8Array = isNative(Uint8Array = context.Uint8Array) && Uint8Array,
+ unshift = arrayProto.unshift,
+ WeakMap = isNative(WeakMap = context.WeakMap) && WeakMap;
+
+ /** Used to clone array buffers. */
+ var Float64Array = (function() {
+ // Safari 5 errors when using an array buffer to initialize a typed array
+ // where the array buffer's `byteLength` is not a multiple of the typed
+ // array's `BYTES_PER_ELEMENT`.
+ try {
+ var func = isNative(func = context.Float64Array) && func,
+ result = new func(new ArrayBuffer(10), 0, 1) && func;
+ } catch(e) {}
+ return result;
+ }());
+
+ /* Native method references for those with the same name as other `lodash` methods. */
+ var nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsFinite = context.isFinite,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeNow = isNative(nativeNow = Date.now) && nativeNow,
+ nativeNumIsFinite = isNative(nativeNumIsFinite = Number.isFinite) && nativeNumIsFinite,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Used as references for `-Infinity` and `Infinity`. */
+ var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY,
+ POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
+
+ /** Used as references for the maximum length and index of an array. */
+ var MAX_ARRAY_LENGTH = Math.pow(2, 32) - 1,
+ MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
+ HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
+
+ /** Used as the size, in bytes, of each `Float64Array` element. */
+ var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
+
+ /**
+ * Used as the maximum length of an array-like value.
+ * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
+ * for more details.
+ */
+ var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
+
+ /** Used to store function metadata. */
+ var metaMap = WeakMap && new WeakMap;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps `value` to enable intuitive chaining.
+ * Methods that operate on and return arrays, collections, and functions can
+ * be chained together. Methods that return a boolean or single value will
+ * automatically end the chain returning the unwrapped value. Explicit chaining
+ * may be enabled using `_.chain`. The execution of chained methods is lazy,
+ * that is, execution is deferred until `_#value` is implicitly or explicitly
+ * called.
+ *
+ * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
+ * fusion is an optimization that merges iteratees to avoid creating intermediate
+ * arrays and reduce the number of iteratee executions.
+ *
+ * Chaining is supported in custom builds as long as the `_#value` method is
+ * directly or indirectly included in the build.
+ *
+ * In addition to lodash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * The wrapper functions that support shortcut fusion are:
+ * `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, `first`,
+ * `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, `slice`,
+ * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `where`
+ *
+ * The chainable wrapper functions are:
+ * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
+ * `callback`, `chain`, `chunk`, `compact`, `concat`, `constant`, `countBy`,
+ * `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`,
+ * `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, `flatten`,
+ * `flattenDeep`, `flow`, `flowRight`, `forEach`, `forEachRight`, `forIn`,
+ * `forInRight`, `forOwn`, `forOwnRight`, `functions`, `groupBy`, `indexBy`,
+ * `initial`, `intersection`, `invert`, `invoke`, `keys`, `keysIn`, `map`,
+ * `mapValues`, `matches`, `memoize`, `merge`, `mixin`, `negate`, `noop`,
+ * `omit`, `once`, `pairs`, `partial`, `partialRight`, `partition`, `pick`,
+ * `pluck`, `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`,
+ * `rearg`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `sortByAll`, `splice`, `take`, `takeRight`, `takeRightWhile`,
+ * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`,
+ * `transform`, `union`, `uniq`, `unshift`, `unzip`, `values`, `valuesIn`,
+ * `where`, `without`, `wrap`, `xor`, `zip`, and `zipObject`
+ *
+ * The wrapper functions that are **not** chainable by default are:
+ * `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
+ * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
+ * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `has`,
+ * `identity`, `includes`, `indexOf`, `isArguments`, `isArray`, `isBoolean`,
+ * `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, `isFinite`,
+ * `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
+ * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`,
+ * `noConflict`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`,
+ * `random`, `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`,
+ * `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`,
+ * `startCase`, `startsWith`, `template`, `trim`, `trimLeft`, `trimRight`,
+ * `trunc`, `unescape`, `uniqueId`, `value`, and `words`
+ *
+ * The wrapper function `sample` will return a wrapped value when `n` is provided,
+ * otherwise an unwrapped value is returned.
+ *
+ * @name _
+ * @constructor
+ * @category Chain
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, n) { return sum + n; });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(n) { return n * n; });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ if (isObjectLike(value) && !isArray(value)) {
+ if (value instanceof LodashWrapper) {
+ return value;
+ }
+ if (hasOwnProperty.call(value, '__wrapped__')) {
+ return new LodashWrapper(value.__wrapped__, value.__chain__, arrayCopy(value.__actions__));
+ }
+ }
+ return new LodashWrapper(value);
+ }
+
+ /**
+ * The base constructor for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
+ * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
+ */
+ function LodashWrapper(value, chainAll, actions) {
+ this.__actions__ = actions || [];
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+
+ /**
+ * An object environment feature flags.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = lodash.support = {};
+
+ (function(x) {
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but Firefox OS certified apps, older Opera mobile browsers, and
+ * the PlayStation 3; forced `false` for Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * Detect if the DOM is supported.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.dom = document.createDocumentFragment().nodeType === 11;
+ } catch(e) {
+ support.dom = false;
+ }
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable.
+ *
+ * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object
+ * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat
+ * `arguments` object indexes as non-enumerable and fail `hasOwnProperty`
+ * checks for indexes that exceed their function's formal parameters with
+ * associated values of `0`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.nonEnumArgs = !propertyIsEnumerable.call(arguments, 1);
+ } catch(e) {
+ support.nonEnumArgs = true;
+ }
+ }(0, 0));
+
+ /**
+ * By default, the template delimiters used by lodash are like those in
+ * embedded Ruby (ERB). Change the following template settings to use
+ * alternative delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': reEscape,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': reEvaluate,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': '',
+
+ /**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type Object
+ */
+ 'imports': {
+
+ /**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type Function
+ */
+ '_': lodash
+ }
+ };
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ */
+ function LazyWrapper(value) {
+ this.actions = null;
+ this.dir = 1;
+ this.dropCount = 0;
+ this.filtered = false;
+ this.iteratees = null;
+ this.takeCount = POSITIVE_INFINITY;
+ this.views = null;
+ this.wrapped = value;
+ }
+
+ /**
+ * Creates a clone of the lazy wrapper object.
+ *
+ * @private
+ * @name clone
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the cloned `LazyWrapper` object.
+ */
+ function lazyClone() {
+ var actions = this.actions,
+ iteratees = this.iteratees,
+ views = this.views,
+ result = new LazyWrapper(this.wrapped);
+
+ result.actions = actions ? arrayCopy(actions) : null;
+ result.dir = this.dir;
+ result.dropCount = this.dropCount;
+ result.filtered = this.filtered;
+ result.iteratees = iteratees ? arrayCopy(iteratees) : null;
+ result.takeCount = this.takeCount;
+ result.views = views ? arrayCopy(views) : null;
+ return result;
+ }
+
+ /**
+ * Reverses the direction of lazy iteration.
+ *
+ * @private
+ * @name reverse
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the new reversed `LazyWrapper` object.
+ */
+ function lazyReverse() {
+ if (this.filtered) {
+ var result = new LazyWrapper(this);
+ result.dir = -1;
+ result.filtered = true;
+ } else {
+ result = this.clone();
+ result.dir *= -1;
+ }
+ return result;
+ }
+
+ /**
+ * Extracts the unwrapped value from its lazy wrapper.
+ *
+ * @private
+ * @name value
+ * @memberOf LazyWrapper
+ * @returns {*} Returns the unwrapped value.
+ */
+ function lazyValue() {
+ var array = this.wrapped.value();
+ if (!isArray(array)) {
+ return baseWrapperValue(array, this.actions);
+ }
+ var dir = this.dir,
+ isRight = dir < 0,
+ view = getView(0, array.length, this.views),
+ start = view.start,
+ end = view.end,
+ length = end - start,
+ dropCount = this.dropCount,
+ takeCount = nativeMin(length, this.takeCount - dropCount),
+ index = isRight ? end : start - 1,
+ iteratees = this.iteratees,
+ iterLength = iteratees ? iteratees.length : 0,
+ resIndex = 0,
+ result = [];
+
+ outer:
+ while (length-- && resIndex < takeCount) {
+ index += dir;
+
+ var iterIndex = -1,
+ value = array[index];
+
+ while (++iterIndex < iterLength) {
+ var data = iteratees[iterIndex],
+ iteratee = data.iteratee,
+ computed = iteratee(value, index, array),
+ type = data.type;
+
+ if (type == LAZY_MAP_FLAG) {
+ value = computed;
+ } else if (!computed) {
+ if (type == LAZY_FILTER_FLAG) {
+ continue outer;
+ } else {
+ break outer;
+ }
+ }
+ }
+ if (dropCount) {
+ dropCount--;
+ } else {
+ result[resIndex++] = value;
+ }
+ }
+ return result;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a cache object to store key/value pairs.
+ *
+ * @private
+ * @static
+ * @name Cache
+ * @memberOf _.memoize
+ */
+ function MapCache() {
+ this.__data__ = {};
+ }
+
+ /**
+ * Removes `key` and its value from the cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf _.memoize.Cache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`.
+ */
+ function mapDelete(key) {
+ return this.has(key) && delete this.__data__[key];
+ }
+
+ /**
+ * Gets the cached value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf _.memoize.Cache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the cached value.
+ */
+ function mapGet(key) {
+ return key == '__proto__' ? undefined : this.__data__[key];
+ }
+
+ /**
+ * Checks if a cached value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf _.memoize.Cache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function mapHas(key) {
+ return key != '__proto__' && hasOwnProperty.call(this.__data__, key);
+ }
+
+ /**
+ * Adds `value` to `key` of the cache.
+ *
+ * @private
+ * @name set
+ * @memberOf _.memoize.Cache
+ * @param {string} key The key of the value to cache.
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache object.
+ */
+ function mapSet(key, value) {
+ if (key != '__proto__') {
+ this.__data__[key] = value;
+ }
+ return this;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ *
+ * Creates a cache object to store unique values.
+ *
+ * @private
+ * @param {Array} [values] The values to cache.
+ */
+ function SetCache(values) {
+ var length = values ? values.length : 0;
+
+ this.data = { 'hash': nativeCreate(null), 'set': new Set };
+ while (length--) {
+ this.push(values[length]);
+ }
+ }
+
+ /**
+ * Checks if `value` is in `cache` mimicking the return signature of
+ * `_.indexOf` by returning `0` if the value is found, else `-1`.
+ *
+ * @private
+ * @param {Object} cache The cache to search.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `0` if `value` is found, else `-1`.
+ */
+ function cacheIndexOf(cache, value) {
+ var data = cache.data,
+ result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
+
+ return result ? 0 : -1;
+ }
+
+ /**
+ * Adds `value` to the cache.
+ *
+ * @private
+ * @name push
+ * @memberOf SetCache
+ * @param {*} value The value to cache.
+ */
+ function cachePush(value) {
+ var data = this.data;
+ if (typeof value == 'string' || isObject(value)) {
+ data.set.add(value);
+ } else {
+ data.hash[value] = true;
+ }
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Copies the values of `source` to `array`.
+ *
+ * @private
+ * @param {Array} source The array to copy values from.
+ * @param {Array} [array=[]] The array to copy values to.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayCopy(source, array) {
+ var index = -1,
+ length = source.length;
+
+ array || (array = Array(length));
+ while (++index < length) {
+ array[index] = source[index];
+ }
+ return array;
+ }
+
+ /**
+ * A specialized version of `_.forEach` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEach(array, iteratee) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ if (iteratee(array[index], index, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * A specialized version of `_.forEachRight` for arrays without support for
+ * callback shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEachRight(array, iteratee) {
+ var length = array.length;
+
+ while (length--) {
+ if (iteratee(array[length], length, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * A specialized version of `_.every` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ */
+ function arrayEvery(array, predicate) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ if (!predicate(array[index], index, array)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A specialized version of `_.filter` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+ function arrayFilter(array, predicate) {
+ var index = -1,
+ length = array.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (predicate(value, index, array)) {
+ result[++resIndex] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.map` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function arrayMap(array, iteratee) {
+ var index = -1,
+ length = array.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iteratee(array[index], index, array);
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.max` for arrays without support for iteratees.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the maximum value.
+ */
+ function arrayMax(array) {
+ var index = -1,
+ length = array.length,
+ result = NEGATIVE_INFINITY;
+
+ while (++index < length) {
+ var value = array[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.min` for arrays without support for iteratees.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the minimum value.
+ */
+ function arrayMin(array) {
+ var index = -1,
+ length = array.length,
+ result = POSITIVE_INFINITY;
+
+ while (++index < length) {
+ var value = array[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.reduce` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initFromArray] Specify using the first element of `array`
+ * as the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+ function arrayReduce(array, iteratee, accumulator, initFromArray) {
+ var index = -1,
+ length = array.length;
+
+ if (initFromArray && length) {
+ accumulator = array[++index];
+ }
+ while (++index < length) {
+ accumulator = iteratee(accumulator, array[index], index, array);
+ }
+ return accumulator;
+ }
+
+ /**
+ * A specialized version of `_.reduceRight` for arrays without support for
+ * callback shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initFromArray] Specify using the last element of `array`
+ * as the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+ function arrayReduceRight(array, iteratee, accumulator, initFromArray) {
+ var length = array.length;
+ if (initFromArray && length) {
+ accumulator = array[--length];
+ }
+ while (length--) {
+ accumulator = iteratee(accumulator, array[length], length, array);
+ }
+ return accumulator;
+ }
+
+ /**
+ * A specialized version of `_.some` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+ function arraySome(array, predicate) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ if (predicate(array[index], index, array)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Used by `_.defaults` to customize its `_.assign` use.
+ *
+ * @private
+ * @param {*} objectValue The destination object property value.
+ * @param {*} sourceValue The source object property value.
+ * @returns {*} Returns the value to assign to the destination object.
+ */
+ function assignDefaults(objectValue, sourceValue) {
+ return typeof objectValue == 'undefined' ? sourceValue : objectValue;
+ }
+
+ /**
+ * Used by `_.template` to customize its `_.assign` use.
+ *
+ * **Note:** This method is like `assignDefaults` except that it ignores
+ * inherited property values when checking if a property is `undefined`.
+ *
+ * @private
+ * @param {*} objectValue The destination object property value.
+ * @param {*} sourceValue The source object property value.
+ * @param {string} key The key associated with the object and source values.
+ * @param {Object} object The destination object.
+ * @returns {*} Returns the value to assign to the destination object.
+ */
+ function assignOwnDefaults(objectValue, sourceValue, key, object) {
+ return (typeof objectValue == 'undefined' || !hasOwnProperty.call(object, key))
+ ? sourceValue
+ : objectValue;
+ }
+
+ /**
+ * The base implementation of `_.assign` without support for argument juggling,
+ * multiple sources, and `this` binding `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [customizer] The function to customize assigning values.
+ * @returns {Object} Returns the destination object.
+ */
+ function baseAssign(object, source, customizer) {
+ var props = keys(source);
+ if (!customizer) {
+ return baseCopy(source, object, props);
+ }
+ var index = -1,
+ length = props.length
+
+ while (++index < length) {
+ var key = props[index],
+ value = object[key],
+ result = customizer(value, source[key], key, object, source);
+
+ if ((result === result ? result !== value : value === value) ||
+ (typeof value == 'undefined' && !(key in object))) {
+ object[key] = result;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * The base implementation of `_.at` without support for strings and individual
+ * key arguments.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {number[]|string[]} [props] The property names or indexes of elements to pick.
+ * @returns {Array} Returns the new array of picked elements.
+ */
+ function baseAt(collection, props) {
+ var index = -1,
+ length = collection.length,
+ isArr = isLength(length),
+ propsLength = props.length,
+ result = Array(propsLength);
+
+ while(++index < propsLength) {
+ var key = props[index];
+ if (isArr) {
+ key = parseFloat(key);
+ result[index] = isIndex(key, length) ? collection[key] : undefined;
+ } else {
+ result[index] = collection[key];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Copies the properties of `source` to `object`.
+ *
+ * @private
+ * @param {Object} source The object to copy properties from.
+ * @param {Object} [object={}] The object to copy properties to.
+ * @param {Array} props The property names to copy.
+ * @returns {Object} Returns `object`.
+ */
+ function baseCopy(source, object, props) {
+ if (!props) {
+ props = object;
+ object = {};
+ }
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ object[key] = source[key];
+ }
+ return object;
+ }
+
+ /**
+ * The base implementation of `_.bindAll` without support for individual
+ * method name arguments.
+ *
+ * @private
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {string[]} methodNames The object method names to bind.
+ * @returns {Object} Returns `object`.
+ */
+ function baseBindAll(object, methodNames) {
+ var index = -1,
+ length = methodNames.length;
+
+ while (++index < length) {
+ var key = methodNames[index];
+ object[key] = createWrapper(object[key], BIND_FLAG, object);
+ }
+ return object;
+ }
+
+ /**
+ * The base implementation of `_.callback` which supports specifying the
+ * number of arguments to provide to `func`.
+ *
+ * @private
+ * @param {*} [func=_.identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [argCount] The number of arguments to provide to `func`.
+ * @returns {Function} Returns the callback.
+ */
+ function baseCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (type == 'function') {
+ return (typeof thisArg != 'undefined' && isBindable(func))
+ ? bindCallback(func, thisArg, argCount)
+ : func;
+ }
+ if (func == null) {
+ return identity;
+ }
+ // Handle "_.property" and "_.matches" style callback shorthands.
+ return type == 'object'
+ ? baseMatches(func)
+ : baseProperty(func + '');
+ }
+
+ /**
+ * The base implementation of `_.clone` without support for argument juggling
+ * and `this` binding `customizer` functions.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @param {Function} [customizer] The function to customize cloning values.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The object `value` belongs to.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
+ var result;
+ if (customizer) {
+ result = object ? customizer(value, key, object) : customizer(value);
+ }
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ if (!isObject(value)) {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isArr) {
+ result = initCloneArray(value);
+ if (!isDeep) {
+ return arrayCopy(value, result);
+ }
+ } else {
+ var tag = objToString.call(value),
+ isFunc = tag == funcTag;
+
+ if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+ result = initCloneObject(isFunc ? {} : value);
+ if (!isDeep) {
+ return baseCopy(value, result, keys(value));
+ }
+ } else {
+ return cloneableTags[tag]
+ ? initCloneByTag(value, tag, isDeep)
+ : (object ? value : {});
+ }
+ }
+ // Check for circular references and return corresponding clone.
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ // Add the source value to the stack of traversed objects and associate it with its clone.
+ stackA.push(value);
+ stackB.push(result);
+
+ // Recursively populate clone (susceptible to call stack limits).
+ (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
+ result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ var baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+
+ /**
+ * The base implementation of `_.delay` and `_.defer` which accepts an index
+ * of where to slice the arguments to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {Object} args The `arguments` object to slice and provide to `func`.
+ * @returns {number} Returns the timer id.
+ */
+ function baseDelay(func, wait, args, fromIndex) {
+ if (!isFunction(func)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return setTimeout(function() { func.apply(undefined, baseSlice(args, fromIndex)); }, wait);
+ }
+
+ /**
+ * The base implementation of `_.difference` which accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Array} values The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var length = array ? array.length : 0,
+ result = [];
+
+ if (!length) {
+ return result;
+ }
+ var index = -1,
+ indexOf = getIndexOf(),
+ isCommon = indexOf == baseIndexOf,
+ cache = isCommon && values.length >= 200 && createCache(values),
+ valuesLength = values.length;
+
+ if (cache) {
+ indexOf = cacheIndexOf;
+ isCommon = false;
+ values = cache;
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index];
+
+ if (isCommon && value === value) {
+ var valuesIndex = valuesLength;
+ while (valuesIndex--) {
+ if (values[valuesIndex] === value) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ else if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.forEach` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object|string} Returns `collection`.
+ */
+ function baseEach(collection, iteratee) {
+ var length = collection ? collection.length : 0;
+ if (!isLength(length)) {
+ return baseForOwn(collection, iteratee);
+ }
+ var index = -1,
+ iterable = toObject(collection);
+
+ while (++index < length) {
+ if (iteratee(iterable[index], index, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ }
+
+ /**
+ * The base implementation of `_.forEachRight` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object|string} Returns `collection`.
+ */
+ function baseEachRight(collection, iteratee) {
+ var length = collection ? collection.length : 0;
+ if (!isLength(length)) {
+ return baseForOwnRight(collection, iteratee);
+ }
+ var iterable = toObject(collection);
+ while (length--) {
+ if (iteratee(iterable[length], length, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ }
+
+ /**
+ * The base implementation of `_.every` without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`
+ */
+ function baseEvery(collection, predicate) {
+ var result = true;
+ baseEach(collection, function(value, index, collection) {
+ result = !!predicate(value, index, collection);
+ return result;
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.filter` without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+ function baseFilter(collection, predicate) {
+ var result = [];
+ baseEach(collection, function(value, index, collection) {
+ if (predicate(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
+ * without support for callback shorthands and `this` binding, which iterates
+ * over `collection` using the provided `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @param {boolean} [retKey] Specify returning the key of the found element
+ * instead of the element itself.
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+ function baseFind(collection, predicate, eachFunc, retKey) {
+ var result;
+ eachFunc(collection, function(value, key, collection) {
+ if (predicate(value, key, collection)) {
+ result = retKey ? key : value;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` with added support for restricting
+ * flattening and specifying the start index.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isDeep] Specify a deep flatten.
+ * @param {boolean} [isStrict] Restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns the new flattened array.
+ */
+ function baseFlatten(array, isDeep, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (isObjectLike(value) && isLength(value.length) && (isArray(value) || isArguments(value))) {
+ if (isDeep) {
+ // Recursively flatten arrays (susceptible to call stack limits).
+ value = baseFlatten(value, isDeep, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[++resIndex] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result[++resIndex] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `baseForIn` and `baseForOwn` which iterates
+ * over `object` properties returned by `keysFunc` invoking `iteratee` for
+ * each property. Iterator functions may exit iteration early by explicitly
+ * returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+ function baseFor(object, iteratee, keysFunc) {
+ var index = -1,
+ iterable = toObject(object),
+ props = keysFunc(object),
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * This function is like `baseFor` except that it iterates over properties
+ * in the opposite order.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForRight(object, iteratee, keysFunc) {
+ var iterable = toObject(object),
+ props = keysFunc(object),
+ length = props.length;
+
+ while (length--) {
+ var key = props[length];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * The base implementation of `_.forIn` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForIn(object, iteratee) {
+ return baseFor(object, iteratee, keysIn);
+ }
+
+ /**
+ * The base implementation of `_.forOwn` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwn(object, iteratee) {
+ return baseFor(object, iteratee, keys);
+ }
+
+ /**
+ * The base implementation of `_.forOwnRight` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwnRight(object, iteratee) {
+ return baseForRight(object, iteratee, keys);
+ }
+
+ /**
+ * The base implementation of `_.functions` which creates an array of
+ * `object` function property names filtered from those provided.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Array} props The property names to filter.
+ * @returns {Array} Returns the new array of filtered property names.
+ */
+ function baseFunctions(object, props) {
+ var index = -1,
+ length = props.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ var key = props[index];
+ if (isFunction(object[key])) {
+ result[++resIndex] = key;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.invoke` which requires additional arguments
+ * to be provided as an array of arguments rather than individually.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {Array} [args] The arguments to invoke the method with.
+ * @returns {Array} Returns the array of results.
+ */
+ function baseInvoke(collection, methodName, args) {
+ var index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = isLength(length) ? Array(length) : [];
+
+ baseEach(collection, function(value) {
+ var func = isFunc ? methodName : (value != null && value[methodName]);
+ result[++index] = func ? func.apply(value, args) : undefined;
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual` without support for `this` binding
+ * `customizer` functions.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {boolean} [isWhere] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(value, other, customizer, isWhere, stackA, stackB) {
+ // Exit early for identical values.
+ if (value === other) {
+ // Treat `+0` vs. `-0` as not equal.
+ return value !== 0 || (1 / value == 1 / other);
+ }
+ var valType = typeof value,
+ othType = typeof other;
+
+ // Exit early for unlike primitive values.
+ if ((valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object') ||
+ value == null || other == null) {
+ // Return `false` unless both values are `NaN`.
+ return value !== value && other !== other;
+ }
+ return baseIsEqualDeep(value, other, baseIsEqual, customizer, isWhere, stackA, stackB);
+ }
+
+ /**
+ * A specialized version of `baseIsEqual` for arrays and objects which performs
+ * deep comparisons and tracks traversed objects enabling objects with circular
+ * references to be compared.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing objects.
+ * @param {boolean} [isWhere] Specify performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `value` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function baseIsEqualDeep(object, other, equalFunc, customizer, isWhere, stackA, stackB) {
+ var objIsArr = isArray(object),
+ othIsArr = isArray(other),
+ objTag = arrayTag,
+ othTag = arrayTag;
+
+ if (!objIsArr) {
+ objTag = objToString.call(object);
+ if (objTag == argsTag) {
+ objTag = objectTag;
+ } else if (objTag != objectTag) {
+ objIsArr = isTypedArray(object);
+ }
+ }
+ if (!othIsArr) {
+ othTag = objToString.call(other);
+ if (othTag == argsTag) {
+ othTag = objectTag;
+ } else if (othTag != objectTag) {
+ othIsArr = isTypedArray(other);
+ }
+ }
+ var objIsObj = objTag == objectTag,
+ othIsObj = othTag == objectTag,
+ isSameTag = objTag == othTag;
+
+ if (isSameTag && !(objIsArr || objIsObj)) {
+ return equalByTag(object, other, objTag);
+ }
+ var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+ othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+ if (valWrapped || othWrapped) {
+ return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isWhere, stackA, stackB);
+ }
+ if (!isSameTag) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ // For more information on detecting circular references see https://es5.github.io/#JO.
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == object) {
+ return stackB[length] == other;
+ }
+ }
+ // Add `object` and `other` to the stack of traversed objects.
+ stackA.push(object);
+ stackB.push(other);
+
+ var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isWhere, stackA, stackB);
+
+ stackA.pop();
+ stackB.pop();
+
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isMatch` without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Object} source The object to inspect.
+ * @param {Array} props The source property names to match.
+ * @param {Array} values The source values to match.
+ * @param {Array} strictCompareFlags Strict comparison flags for source values.
+ * @param {Function} [customizer] The function to customize comparing objects.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ */
+ function baseIsMatch(object, props, values, strictCompareFlags, customizer) {
+ var length = props.length;
+ if (object == null) {
+ return !length;
+ }
+ var index = -1,
+ noCustomizer = !customizer;
+
+ while (++index < length) {
+ if ((noCustomizer && strictCompareFlags[index])
+ ? values[index] !== object[props[index]]
+ : !hasOwnProperty.call(object, props[index])
+ ) {
+ return false;
+ }
+ }
+ index = -1;
+ while (++index < length) {
+ var key = props[index];
+ if (noCustomizer && strictCompareFlags[index]) {
+ var result = hasOwnProperty.call(object, key);
+ } else {
+ var objValue = object[key],
+ srcValue = values[index];
+
+ result = customizer ? customizer(objValue, srcValue, key) : undefined;
+ if (typeof result == 'undefined') {
+ result = baseIsEqual(srcValue, objValue, customizer, true);
+ }
+ }
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * The base implementation of `_.map` without support for callback shorthands
+ * or `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function baseMap(collection, iteratee) {
+ var result = [];
+ baseEach(collection, function(value, key, collection) {
+ result.push(iteratee(value, key, collection));
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.matches` which supports specifying whether
+ * `source` should be cloned.
+ *
+ * @private
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new function.
+ */
+ function baseMatches(source) {
+ var props = keys(source),
+ length = props.length;
+
+ if (length == 1) {
+ var key = props[0],
+ value = source[key];
+
+ if (isStrictComparable(value)) {
+ return function(object) {
+ return object != null && value === object[key] && hasOwnProperty.call(object, key);
+ };
+ }
+ }
+ var values = Array(length),
+ strictCompareFlags = Array(length);
+
+ while (length--) {
+ value = source[props[length]];
+ values[length] = value;
+ strictCompareFlags[length] = isStrictComparable(value);
+ }
+ return function(object) {
+ return baseIsMatch(object, props, values, strictCompareFlags);
+ };
+ }
+
+ /**
+ * The base implementation of `_.merge` without support for argument juggling,
+ * multiple sources, and `this` binding `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [customizer] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ * @returns {Object} Returns the destination object.
+ */
+ function baseMerge(object, source, customizer, stackA, stackB) {
+ var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source));
+
+ (isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) {
+ if (isObjectLike(srcValue)) {
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+ return baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
+ }
+ var value = object[key],
+ result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
+ isCommon = typeof result == 'undefined';
+
+ if (isCommon) {
+ result = srcValue;
+ }
+ if ((isSrcArr || typeof result != 'undefined') &&
+ (isCommon || (result === result ? result !== value : value === value))) {
+ object[key] = result;
+ }
+ });
+ return object;
+ }
+
+ /**
+ * A specialized version of `baseMerge` for arrays and objects which performs
+ * deep merges and tracks traversed objects enabling objects with circular
+ * references to be merged.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {string} key The key of the value to merge.
+ * @param {Function} mergeFunc The function to merge values.
+ * @param {Function} [customizer] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
+ var length = stackA.length,
+ srcValue = source[key];
+
+ while (length--) {
+ if (stackA[length] == srcValue) {
+ object[key] = stackB[length];
+ return;
+ }
+ }
+ var value = object[key],
+ result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
+ isCommon = typeof result == 'undefined';
+
+ if (isCommon) {
+ result = srcValue;
+ if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) {
+ result = isArray(value)
+ ? value
+ : (value ? arrayCopy(value) : []);
+ }
+ else if (isPlainObject(srcValue) || isArguments(srcValue)) {
+ result = isArguments(value)
+ ? toPlainObject(value)
+ : (isPlainObject(value) ? value : {});
+ }
+ else {
+ isCommon = false;
+ }
+ }
+ // Add the source value to the stack of traversed objects and associate
+ // it with its merged value.
+ stackA.push(srcValue);
+ stackB.push(result);
+
+ if (isCommon) {
+ // Recursively merge objects and arrays (susceptible to call stack limits).
+ object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
+ } else if (result === result ? result !== value : value === value) {
+ object[key] = result;
+ }
+ }
+
+ /**
+ * The base implementation of `_.property` which does not coerce `key` to a string.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @returns {Function} Returns the new function.
+ */
+ function baseProperty(key) {
+ return function(object) {
+ return object == null ? undefined : object[key];
+ };
+ }
+
+ /**
+ * The base implementation of `_.pullAt` without support for individual
+ * index arguments.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {number[]} indexes The indexes of elements to remove.
+ * @returns {Array} Returns the new array of removed elements.
+ */
+ function basePullAt(array, indexes) {
+ var length = indexes.length,
+ result = baseAt(array, indexes);
+
+ indexes.sort(baseCompareAscending);
+ while (length--) {
+ var index = parseFloat(indexes[length]);
+ if (index != previous && isIndex(index)) {
+ var previous = index;
+ splice.call(array, index, 1);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.random` without support for argument juggling
+ * and returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns the random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.reduce` and `_.reduceRight` without support
+ * for callback shorthands or `this` binding, which iterates over `collection`
+ * using the provided `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} accumulator The initial value.
+ * @param {boolean} initFromCollection Specify using the first or last element
+ * of `collection` as the initial value.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @returns {*} Returns the accumulated value.
+ */
+ function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
+ eachFunc(collection, function(value, index, collection) {
+ accumulator = initFromCollection
+ ? (initFromCollection = false, value)
+ : iteratee(accumulator, value, index, collection)
+ });
+ return accumulator;
+ }
+
+ /**
+ * The base implementation of `setData` without support for hot loop detection.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+ var baseSetData = !metaMap ? identity : function(func, data) {
+ metaMap.set(func, data);
+ return func;
+ };
+
+ /**
+ * The base implementation of `_.slice` without an iteratee call guard.
+ *
+ * @private
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
+ */
+ function baseSlice(array, start, end) {
+ var index = -1,
+ length = array.length;
+
+ start = start == null ? 0 : (+start || 0);
+ if (start < 0) {
+ start = -start > length ? 0 : (length + start);
+ }
+ end = (typeof end == 'undefined' || end > length) ? length : (+end || 0);
+ if (end < 0) {
+ end += length;
+ }
+ length = start > end ? 0 : (end - start) >>> 0;
+ start >>>= 0;
+
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = array[index + start];
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.some` without support for callback shorthands
+ * or `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+ function baseSome(collection, predicate) {
+ var result;
+
+ baseEach(collection, function(value, index, collection) {
+ result = predicate(value, index, collection);
+ return !result;
+ });
+ return !!result;
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * and `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The function invoked per iteration.
+ * @returns {Array} Returns the new duplicate-value-free array.
+ */
+ function baseUniq(array, iteratee) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array.length,
+ isCommon = indexOf == baseIndexOf,
+ isLarge = isCommon && length >= 200,
+ seen = isLarge && createCache(),
+ result = [];
+
+ if (seen) {
+ indexOf = cacheIndexOf;
+ isCommon = false;
+ } else {
+ isLarge = false;
+ seen = iteratee ? [] : result;
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value, index, array) : value;
+
+ if (isCommon && value === value) {
+ var seenIndex = seen.length;
+ while (seenIndex--) {
+ if (seen[seenIndex] === computed) {
+ continue outer;
+ }
+ }
+ if (iteratee) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ else if (indexOf(seen, computed) < 0) {
+ if (iteratee || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.values` and `_.valuesIn` which creates an
+ * array of `object` property values corresponding to the property names
+ * returned by `keysFunc`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} props The property names to get values for.
+ * @returns {Object} Returns the array of property values.
+ */
+ function baseValues(object, props) {
+ var index = -1,
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `wrapperValue` which returns the result of
+ * performing a sequence of actions on the unwrapped `value`, where each
+ * successive action is supplied the return value of the previous.
+ *
+ * @private
+ * @param {*} value The unwrapped value.
+ * @param {Array} actions Actions to peform to resolve the unwrapped value.
+ * @returns {*} Returns the resolved unwrapped value.
+ */
+ function baseWrapperValue(value, actions) {
+ var result = value;
+ if (result instanceof LazyWrapper) {
+ result = result.value();
+ }
+ var index = -1,
+ length = actions.length;
+
+ while (++index < length) {
+ var args = [result],
+ action = actions[index];
+
+ push.apply(args, action.args);
+ result = action.func.apply(action.thisArg, args);
+ }
+ return result;
+ }
+
+ /**
+ * Performs a binary search of `array` to determine the index at which `value`
+ * should be inserted into `array` in order to maintain its sort order.
+ *
+ * @private
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {boolean} [retHighest] Specify returning the highest, instead
+ * of the lowest, index at which a value should be inserted into `array`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ */
+ function binaryIndex(array, value, retHighest) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
+ while (low < high) {
+ var mid = (low + high) >>> 1,
+ computed = array[mid];
+
+ if (retHighest ? (computed <= value) : (computed < value)) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return high;
+ }
+ return binaryIndexBy(array, value, identity, retHighest);
+ }
+
+ /**
+ * This function is like `binaryIndex` except that it invokes `iteratee` for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * iteratee is invoked with one argument; (value).
+ *
+ * @private
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {boolean} [retHighest] Specify returning the highest, instead
+ * of the lowest, index at which a value should be inserted into `array`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ */
+ function binaryIndexBy(array, value, iteratee, retHighest) {
+ value = iteratee(value);
+
+ var low = 0,
+ high = array ? array.length : 0,
+ valIsNaN = value !== value,
+ valIsUndef = typeof value == 'undefined';
+
+ while (low < high) {
+ var mid = floor((low + high) / 2),
+ computed = iteratee(array[mid]),
+ isReflexive = computed === computed;
+
+ if (valIsNaN) {
+ var setLow = isReflexive || retHighest;
+ } else if (valIsUndef) {
+ setLow = isReflexive && (retHighest || typeof computed != 'undefined');
+ } else {
+ setLow = retHighest ? (computed <= value) : (computed < value);
+ }
+ if (setLow) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return nativeMin(high, MAX_ARRAY_INDEX);
+ }
+
+ /**
+ * A specialized version of `baseCallback` which only supports `this` binding
+ * and specifying the number of arguments to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {number} [argCount] The number of arguments to provide to `func`.
+ * @returns {Function} Returns the callback.
+ */
+ function bindCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ if (typeof thisArg == 'undefined') {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ case 5: return function(value, other, key, object, source) {
+ return func.call(thisArg, value, other, key, object, source);
+ };
+ }
+ return function() {
+ return func.apply(thisArg, arguments);
+ };
+ }
+
+ /**
+ * Creates a clone of the given array buffer.
+ *
+ * @private
+ * @param {ArrayBuffer} buffer The array buffer to clone.
+ * @returns {ArrayBuffer} Returns the cloned array buffer.
+ */
+ function bufferClone(buffer) {
+ return bufferSlice.call(buffer, 0);
+ }
+ if (!bufferSlice) {
+ // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
+ bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
+ var byteLength = buffer.byteLength,
+ floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
+ offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
+ result = new ArrayBuffer(byteLength);
+
+ if (floatLength) {
+ var view = new Float64Array(result, 0, floatLength);
+ view.set(new Float64Array(buffer, 0, floatLength));
+ }
+ if (byteLength != offset) {
+ view = new Uint8Array(result, offset);
+ view.set(new Uint8Array(buffer, offset));
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates an array that is the composition of partially applied arguments,
+ * placeholders, and provided arguments into a single array of arguments.
+ *
+ * @private
+ * @param {Array|Object} args The provided arguments.
+ * @param {Array} partials The arguments to prepend to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+ function composeArgs(args, partials, holders) {
+ var holdersLength = holders.length,
+ argsIndex = -1,
+ argsLength = nativeMax(args.length - holdersLength, 0),
+ leftIndex = -1,
+ leftLength = partials.length,
+ result = Array(argsLength + leftLength);
+
+ while (++leftIndex < leftLength) {
+ result[leftIndex] = partials[leftIndex];
+ }
+ while (++argsIndex < holdersLength) {
+ result[holders[argsIndex]] = args[argsIndex];
+ }
+ while (argsLength--) {
+ result[leftIndex++] = args[argsIndex++];
+ }
+ return result;
+ }
+
+ /**
+ * This function is like `composeArgs` except that the arguments composition
+ * is tailored for `_.partialRight`.
+ *
+ * @private
+ * @param {Array|Object} args The provided arguments.
+ * @param {Array} partials The arguments to append to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+ function composeArgsRight(args, partials, holders) {
+ var holdersIndex = -1,
+ holdersLength = holders.length,
+ argsIndex = -1,
+ argsLength = nativeMax(args.length - holdersLength, 0),
+ rightIndex = -1,
+ rightLength = partials.length,
+ result = Array(argsLength + rightLength);
+
+ while (++argsIndex < argsLength) {
+ result[argsIndex] = args[argsIndex];
+ }
+ var pad = argsIndex;
+ while (++rightIndex < rightLength) {
+ result[pad + rightIndex] = partials[rightIndex];
+ }
+ while (++holdersIndex < holdersLength) {
+ result[pad + holders[holdersIndex]] = args[argsIndex++];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an accumulator
+ * object composed from the results of running each element in the collection
+ * through an iteratee. The `setter` sets the keys and values of the accumulator
+ * object. If `initializer` is provided initializes the accumulator object.
+ *
+ * @private
+ * @param {Function} setter The function to set keys and values of the accumulator object.
+ * @param {Function} [initializer] The function to initialize the accumulator object.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter, initializer) {
+ return function(collection, iteratee, thisArg) {
+ var result = initializer ? initializer() : {};
+ iteratee = getCallback(iteratee, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, iteratee(value, index, collection), collection);
+ }
+ } else {
+ baseEach(collection, function(value, key, collection) {
+ setter(result, value, iteratee(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that assigns properties of source object(s) to a given
+ * destination object.
+ *
+ * @private
+ * @param {Function} assigner The function to assign values.
+ * @returns {Function} Returns the new assigner function.
+ */
+ function createAssigner(assigner) {
+ return function() {
+ var length = arguments.length,
+ object = arguments[0];
+
+ if (length < 2 || object == null) {
+ return object;
+ }
+ if (length > 3 && isIterateeCall(arguments[1], arguments[2], arguments[3])) {
+ length = 2;
+ }
+ // Juggle arguments.
+ if (length > 3 && typeof arguments[length - 2] == 'function') {
+ var customizer = bindCallback(arguments[--length - 1], arguments[length--], 5);
+ } else if (length > 2 && typeof arguments[length - 1] == 'function') {
+ customizer = arguments[--length];
+ }
+ var index = 0;
+ while (++index < length) {
+ var source = arguments[index];
+ if (source) {
+ assigner(object, source, customizer);
+ }
+ }
+ return object;
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` and invokes it with the `this`
+ * binding of `thisArg`.
+ *
+ * @private
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @returns {Function} Returns the new bound function.
+ */
+ function createBindWrapper(func, thisArg) {
+ var Ctor = createCtorWrapper(func);
+
+ function wrapper() {
+ return (this instanceof wrapper ? Ctor : func).apply(thisArg, arguments);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a `Set` cache object to optimize linear searches of large arrays.
+ *
+ * @private
+ * @param {Array} [values] The values to cache.
+ * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
+ */
+ var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
+ return new SetCache(values);
+ };
+
+ /**
+ * Creates a function that produces compound words out of the words in a
+ * given string.
+ *
+ * @private
+ * @param {Function} callback The function to combine each word.
+ * @returns {Function} Returns the new compounder function.
+ */
+ function createCompounder(callback) {
+ return function(string) {
+ var index = -1,
+ array = words(deburr(string)),
+ length = array.length,
+ result = '';
+
+ while (++index < length) {
+ result = callback(result, array[index], index);
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that produces an instance of `Ctor` regardless of
+ * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+ *
+ * @private
+ * @param {Function} Ctor The constructor to wrap.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createCtorWrapper(Ctor) {
+ return function() {
+ var thisBinding = baseCreate(Ctor.prototype),
+ result = Ctor.apply(thisBinding, arguments);
+
+ // Mimic the constructor's `return` behavior.
+ // See https://es5.github.io/#x13.2.2 for more details.
+ return isObject(result) ? result : thisBinding;
+ };
+ }
+
+ /**
+ * Creates a function that gets the extremum value of a collection.
+ *
+ * @private
+ * @param {Function} arrayFunc The function to get the extremum value from an array.
+ * @param {boolean} [isMin] Specify returning the minimum, instead of the maximum,
+ * extremum value.
+ * @returns {Function} Returns the new extremum function.
+ */
+ function createExtremum(arrayFunc, isMin) {
+ return function(collection, iteratee, thisArg) {
+ if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
+ iteratee = null;
+ }
+ var func = getCallback(),
+ noIteratee = iteratee == null;
+
+ if (!(func === baseCallback && noIteratee)) {
+ noIteratee = false;
+ iteratee = func(iteratee, thisArg, 3);
+ }
+ if (noIteratee) {
+ var isArr = isArray(collection);
+ if (!isArr && isString(collection)) {
+ iteratee = charAtCallback;
+ } else {
+ return arrayFunc(isArr ? collection : toIterable(collection));
+ }
+ }
+ return extremumBy(collection, iteratee, isMin);
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` and invokes it with optional `this`
+ * binding of, partial application, and currying.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
+ * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+ var isAry = bitmask & ARY_FLAG,
+ isBind = bitmask & BIND_FLAG,
+ isBindKey = bitmask & BIND_KEY_FLAG,
+ isCurry = bitmask & CURRY_FLAG,
+ isCurryBound = bitmask & CURRY_BOUND_FLAG,
+ isCurryRight = bitmask & CURRY_RIGHT_FLAG;
+
+ var Ctor = !isBindKey && createCtorWrapper(func),
+ key = func;
+
+ function wrapper() {
+ // Avoid `arguments` object use disqualifying optimizations by
+ // converting it to an array before providing it to other functions.
+ var length = arguments.length,
+ index = length,
+ args = Array(length);
+
+ while (index--) {
+ args[index] = arguments[index];
+ }
+ if (partials) {
+ args = composeArgs(args, partials, holders);
+ }
+ if (partialsRight) {
+ args = composeArgsRight(args, partialsRight, holdersRight);
+ }
+ if (isCurry || isCurryRight) {
+ var placeholder = wrapper.placeholder,
+ argsHolders = replaceHolders(args, placeholder);
+
+ length -= argsHolders.length;
+ if (length < arity) {
+ var newArgPos = argPos ? arrayCopy(argPos) : null,
+ newArity = nativeMax(arity - length, 0),
+ newsHolders = isCurry ? argsHolders : null,
+ newHoldersRight = isCurry ? null : argsHolders,
+ newPartials = isCurry ? args : null,
+ newPartialsRight = isCurry ? null : args;
+
+ bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
+ bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
+
+ if (!isCurryBound) {
+ bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+ }
+ var result = createHybridWrapper(func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity);
+ result.placeholder = placeholder;
+ return result;
+ }
+ }
+ var thisBinding = isBind ? thisArg : this;
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (argPos) {
+ args = reorder(args, argPos);
+ }
+ if (isAry && ary < args.length) {
+ args.length = ary;
+ }
+ return (this instanceof wrapper ? (Ctor || createCtorWrapper(func)) : func).apply(thisBinding, args);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates the pad required for `string` based on the given padding length.
+ * The `chars` string may be truncated if the number of padding characters
+ * exceeds the padding length.
+ *
+ * @private
+ * @param {string} string The string to create padding for.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the pad for `string`.
+ */
+ function createPad(string, length, chars) {
+ var strLength = string.length;
+ length = +length;
+
+ if (strLength >= length || !nativeIsFinite(length)) {
+ return '';
+ }
+ var padLength = length - strLength;
+ chars = chars == null ? ' ' : (chars + '');
+ return repeat(chars, ceil(padLength / chars.length)).slice(0, padLength);
+ }
+
+ /**
+ * Creates a function that wraps `func` and invokes it with the optional `this`
+ * binding of `thisArg` and the `partials` prepended to those provided to
+ * the wrapper.
+ *
+ * @private
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} partials The arguments to prepend to those provided to the new function.
+ * @returns {Function} Returns the new bound function.
+ */
+ function createPartialWrapper(func, bitmask, thisArg, partials) {
+ var isBind = bitmask & BIND_FLAG,
+ Ctor = createCtorWrapper(func);
+
+ function wrapper() {
+ // Avoid `arguments` object use disqualifying optimizations by
+ // converting it to an array before providing it `func`.
+ var argsIndex = -1,
+ argsLength = arguments.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ args = Array(argsLength + leftLength);
+
+ while (++leftIndex < leftLength) {
+ args[leftIndex] = partials[leftIndex];
+ }
+ while (argsLength--) {
+ args[leftIndex++] = arguments[++argsIndex];
+ }
+ return (this instanceof wrapper ? Ctor : func).apply(isBind ? thisArg : this, args);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a function that either curries or invokes `func` with optional
+ * `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of flags.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry` or `_.curryRight` of a bound function
+ * 8 - `_.curry`
+ * 16 - `_.curryRight`
+ * 32 - `_.partial`
+ * 64 - `_.partialRight`
+ * 128 - `_.rearg`
+ * 256 - `_.ary`
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to be partially applied.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+ var isBindKey = bitmask & BIND_KEY_FLAG;
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var length = partials ? partials.length : 0;
+ if (!length) {
+ bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
+ partials = holders = null;
+ }
+ length -= (holders ? holders.length : 0);
+ if (bitmask & PARTIAL_RIGHT_FLAG) {
+ var partialsRight = partials,
+ holdersRight = holders;
+
+ partials = holders = null;
+ }
+ var data = !isBindKey && getData(func),
+ newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
+
+ if (data && data !== true) {
+ mergeData(newData, data);
+ bitmask = newData[1];
+ arity = newData[9];
+ }
+ newData[9] = arity == null
+ ? (isBindKey ? 0 : func.length)
+ : (nativeMax(arity - length, 0) || 0);
+
+ if (bitmask == BIND_FLAG) {
+ var result = createBindWrapper(newData[0], newData[2]);
+ } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
+ result = createPartialWrapper.apply(null, newData);
+ } else {
+ result = createHybridWrapper.apply(null, newData);
+ }
+ var setter = data ? baseSetData : setData;
+ return setter(result, newData);
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for arrays with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Array} array The array to compare.
+ * @param {Array} other The other array to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing arrays.
+ * @param {boolean} [isWhere] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+ */
+ function equalArrays(array, other, equalFunc, customizer, isWhere, stackA, stackB) {
+ var index = -1,
+ arrLength = array.length,
+ othLength = other.length,
+ result = true;
+
+ if (arrLength != othLength && !(isWhere && othLength > arrLength)) {
+ return false;
+ }
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (result && ++index < arrLength) {
+ var arrValue = array[index],
+ othValue = other[index];
+
+ result = undefined;
+ if (customizer) {
+ result = isWhere
+ ? customizer(othValue, arrValue, index)
+ : customizer(arrValue, othValue, index);
+ }
+ if (typeof result == 'undefined') {
+ // Recursively compare arrays (susceptible to call stack limits).
+ if (isWhere) {
+ var othIndex = othLength;
+ while (othIndex--) {
+ othValue = other[othIndex];
+ result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB);
+ if (result) {
+ break;
+ }
+ }
+ } else {
+ result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB);
+ }
+ }
+ }
+ return !!result;
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for comparing objects of
+ * the same `toStringTag`.
+ *
+ * **Note:** This function only supports comparing values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ * @private
+ * @param {Object} value The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {string} tag The `toStringTag` of the objects to compare.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function equalByTag(object, other, tag) {
+ switch (tag) {
+ case boolTag:
+ case dateTag:
+ // Coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
+ return +object == +other;
+
+ case errorTag:
+ return object.name == other.name && object.message == other.message;
+
+ case numberTag:
+ // Treat `NaN` vs. `NaN` as equal.
+ return (object != +object)
+ ? other != +other
+ // But, treat `-0` vs. `+0` as not equal.
+ : (object == 0 ? ((1 / object) == (1 / other)) : object == +other);
+
+ case regexpTag:
+ case stringTag:
+ // Coerce regexes to strings and treat strings primitives and string
+ // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
+ return object == (other + '');
+ }
+ return false;
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for objects with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {boolean} [isWhere] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function equalObjects(object, other, equalFunc, customizer, isWhere, stackA, stackB) {
+ var objProps = keys(object),
+ objLength = objProps.length,
+ othProps = keys(other),
+ othLength = othProps.length;
+
+ if (objLength != othLength && !isWhere) {
+ return false;
+ }
+ var hasCtor,
+ index = -1;
+
+ while (++index < objLength) {
+ var key = objProps[index],
+ result = hasOwnProperty.call(other, key);
+
+ if (result) {
+ var objValue = object[key],
+ othValue = other[key];
+
+ result = undefined;
+ if (customizer) {
+ result = isWhere
+ ? customizer(othValue, objValue, key)
+ : customizer(objValue, othValue, key);
+ }
+ if (typeof result == 'undefined') {
+ // Recursively compare objects (susceptible to call stack limits).
+ result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isWhere, stackA, stackB);
+ }
+ }
+ if (!result) {
+ return false;
+ }
+ hasCtor || (hasCtor = key == 'constructor');
+ }
+ if (!hasCtor) {
+ var objCtor = object.constructor,
+ othCtor = other.constructor;
+
+ // Non `Object` object instances with different constructors are not equal.
+ if (objCtor != othCtor && ('constructor' in object && 'constructor' in other) &&
+ !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets the extremum value of `collection` invoking `iteratee` for each value
+ * in `collection` to generate the criterion by which the value is ranked.
+ * The `iteratee` is invoked with three arguments; (value, index, collection).
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {boolean} [isMin] Specify returning the minimum, instead of the
+ * maximum, extremum value.
+ * @returns {*} Returns the extremum value.
+ */
+ function extremumBy(collection, iteratee, isMin) {
+ var exValue = isMin ? POSITIVE_INFINITY : NEGATIVE_INFINITY,
+ computed = exValue,
+ result = computed;
+
+ baseEach(collection, function(value, index, collection) {
+ var current = iteratee(value, index, collection);
+ if ((isMin ? current < computed : current > computed) || (current === exValue && current === result)) {
+ computed = current;
+ result = value;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Gets the appropriate "callback" function. If the `_.callback` method is
+ * customized this function returns the custom method, otherwise it returns
+ * the `baseCallback` function. If arguments are provided the chosen function
+ * is invoked with them and its result is returned.
+ *
+ * @private
+ * @returns {Function} Returns the chosen function or its result.
+ */
+ function getCallback(func, thisArg, argCount) {
+ var result = lodash.callback || callback;
+ result = result === callback ? baseCallback : result;
+ return argCount ? result(func, thisArg, argCount) : result;
+ }
+
+ /**
+ * Gets metadata for `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {*} Returns the metadata for `func`.
+ */
+ var getData = !metaMap ? noop : function(func) {
+ return metaMap.get(func);
+ };
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized this function returns the custom method, otherwise it returns
+ * the `baseIndexOf` function. If arguments are provided the chosen function
+ * is invoked with them and its result is returned.
+ *
+ * @private
+ * @returns {Function|number} Returns the chosen function or its result.
+ */
+ function getIndexOf(collection, target, fromIndex) {
+ var result = lodash.indexOf || indexOf;
+ result = result === indexOf ? baseIndexOf : result;
+ return collection ? result(collection, target, fromIndex) : result;
+ }
+
+ /**
+ * Gets the view, applying any `transforms` to the `start` and `end` positions.
+ *
+ * @private
+ * @param {number} start The start of the view.
+ * @param {number} end The end of the view.
+ * @param {Array} [transforms] The transformations to apply to the view.
+ * @returns {Object} Returns an object containing the `start` and `end`
+ * positions of the view.
+ */
+ function getView(start, end, transforms) {
+ var index = -1,
+ length = transforms ? transforms.length : 0;
+
+ while (++index < length) {
+ var data = transforms[index],
+ size = data.size;
+
+ switch (data.type) {
+ case 'drop': start += size; break;
+ case 'dropRight': end -= size; break;
+ case 'take': end = nativeMin(end, start + size); break;
+ case 'takeRight': start = nativeMax(start, end - size); break;
+ }
+ }
+ return { 'start': start, 'end': end };
+ }
+
+ /**
+ * Initializes an array clone.
+ *
+ * @private
+ * @param {Array} array The array to clone.
+ * @returns {Array} Returns the initialized clone.
+ */
+ function initCloneArray(array) {
+ var length = array.length,
+ result = new array.constructor(length);
+
+ // Add array properties assigned by `RegExp#exec`.
+ if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+ result.index = array.index;
+ result.input = array.input;
+ }
+ return result;
+ }
+
+ /**
+ * Initializes an object clone.
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+ function initCloneObject(object) {
+ var Ctor = object.constructor;
+ if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
+ Ctor = Object;
+ }
+ return new Ctor;
+ }
+
+ /**
+ * Initializes an object clone based on its `toStringTag`.
+ *
+ * **Note:** This function only supports cloning values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @param {string} tag The `toStringTag` of the object to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+ function initCloneByTag(object, tag, isDeep) {
+ var Ctor = object.constructor;
+ switch (tag) {
+ case arrayBufferTag:
+ return bufferClone(object);
+
+ case boolTag:
+ case dateTag:
+ return new Ctor(+object);
+
+ case float32Tag: case float64Tag:
+ case int8Tag: case int16Tag: case int32Tag:
+ case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+ var buffer = object.buffer;
+ return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
+
+ case numberTag:
+ case stringTag:
+ return new Ctor(object);
+
+ case regexpTag:
+ var result = new Ctor(object.source, reFlags.exec(object));
+ result.lastIndex = object.lastIndex;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `func` is eligible for `this` binding.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` is eligible, else `false`.
+ */
+ function isBindable(func) {
+ var support = lodash.support,
+ result = !(support.funcNames ? func.name : support.funcDecomp);
+
+ if (!result) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ result = !reFuncName.test(source);
+ }
+ if (!result) {
+ // Check if `func` references the `this` keyword and store the result.
+ result = reThis.test(source) || isNative(func);
+ baseSetData(func, result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a valid array-like index.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ */
+ function isIndex(value, length) {
+ value = +value;
+ length = length == null ? MAX_SAFE_INTEGER : length;
+ return value > -1 && value % 1 == 0 && value < length;
+ }
+
+ /**
+ * Checks if the provided arguments are from an iteratee call.
+ *
+ * @private
+ * @param {*} value The potential iteratee value argument.
+ * @param {*} index The potential iteratee index or key argument.
+ * @param {*} object The potential iteratee object argument.
+ * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
+ */
+ function isIterateeCall(value, index, object) {
+ if (!isObject(object)) {
+ return false;
+ }
+ var type = typeof index;
+ if (type == 'number') {
+ var length = object.length,
+ prereq = isLength(length) && isIndex(index, length);
+ } else {
+ prereq = type == 'string' && index in object;
+ }
+ return prereq && object[index] === value;
+ }
+
+ /**
+ * Checks if `value` is a valid array-like length.
+ *
+ * **Note:** This function is based on ES `ToLength`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
+ * for more details.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ */
+ function isLength(value) {
+ return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+ }
+
+ /**
+ * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` if suitable for strict
+ * equality comparisons, else `false`.
+ */
+ function isStrictComparable(value) {
+ return value === value && (value === 0 ? ((1 / value) > 0) : !isObject(value));
+ }
+
+ /**
+ * Merges the function metadata of `source` into `data`.
+ *
+ * Merging metadata reduces the number of wrappers required to invoke a function.
+ * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+ * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
+ * augment function arguments, making the order in which they are executed important,
+ * preventing the merging of metadata. However, we make an exception for a safe
+ * common case where curried functions have `_.ary` and or `_.rearg` applied.
+ *
+ * @private
+ * @param {Array} data The destination metadata.
+ * @param {Array} source The source metadata.
+ * @returns {Array} Returns `data`.
+ */
+ function mergeData(data, source) {
+ var bitmask = data[1],
+ srcBitmask = source[1],
+ newBitmask = bitmask | srcBitmask;
+
+ var arityFlags = ARY_FLAG | REARG_FLAG,
+ bindFlags = BIND_FLAG | BIND_KEY_FLAG,
+ comboFlags = arityFlags | bindFlags | CURRY_BOUND_FLAG | CURRY_RIGHT_FLAG;
+
+ var isAry = bitmask & ARY_FLAG && !(srcBitmask & ARY_FLAG),
+ isRearg = bitmask & REARG_FLAG && !(srcBitmask & REARG_FLAG),
+ argPos = (isRearg ? data : source)[7],
+ ary = (isAry ? data : source)[8];
+
+ var isCommon = !(bitmask >= REARG_FLAG && srcBitmask > bindFlags) &&
+ !(bitmask > bindFlags && srcBitmask >= REARG_FLAG);
+
+ var isCombo = (newBitmask >= arityFlags && newBitmask <= comboFlags) &&
+ (bitmask < REARG_FLAG || ((isRearg || isAry) && argPos.length <= ary));
+
+ // Exit early if metadata can't be merged.
+ if (!(isCommon || isCombo)) {
+ return data;
+ }
+ // Use source `thisArg` if available.
+ if (srcBitmask & BIND_FLAG) {
+ data[2] = source[2];
+ // Set when currying a bound function.
+ newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
+ }
+ // Compose partial arguments.
+ var value = source[3];
+ if (value) {
+ var partials = data[3];
+ data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
+ data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
+ }
+ // Compose partial right arguments.
+ value = source[5];
+ if (value) {
+ partials = data[5];
+ data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
+ data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
+ }
+ // Use source `argPos` if available.
+ value = source[7];
+ if (value) {
+ data[7] = arrayCopy(value);
+ }
+ // Use source `ary` if it's smaller.
+ if (srcBitmask & ARY_FLAG) {
+ data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+ }
+ // Use source `arity` if one is not provided.
+ if (data[9] == null) {
+ data[9] = source[9];
+ }
+ // Use source `func` and merge bitmasks.
+ data[0] = source[0];
+ data[1] = newBitmask;
+
+ return data;
+ }
+
+ /**
+ * A specialized version of `_.pick` that picks `object` properties specified
+ * by the `props` array.
+ *
+ * @private
+ * @param {Object} object The source object.
+ * @param {string[]} props The property names to pick.
+ * @returns {Object} Returns the new object.
+ */
+ function pickByArray(object, props) {
+ object = toObject(object);
+
+ var index = -1,
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.pick` that picks `object` properties `predicate`
+ * returns truthy for.
+ *
+ * @private
+ * @param {Object} object The source object.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Object} Returns the new object.
+ */
+ function pickByCallback(object, predicate) {
+ var result = {};
+ baseForIn(object, function(value, key, object) {
+ if (predicate(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Reorder `array` according to the specified indexes where the element at
+ * the first index is assigned as the first element, the element at
+ * the second index is assigned as the second element, and so on.
+ *
+ * @private
+ * @param {Array} array The array to reorder.
+ * @param {Array} indexes The arranged array indexes.
+ * @returns {Array} Returns `array`.
+ */
+ function reorder(array, indexes) {
+ var arrLength = array.length,
+ length = nativeMin(indexes.length, arrLength),
+ oldArray = arrayCopy(array);
+
+ while (length--) {
+ var index = indexes[length];
+ array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+ }
+ return array;
+ }
+
+ /**
+ * Sets metadata for `func`.
+ *
+ * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+ * period of time, it will trip its breaker and transition to an identity function
+ * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
+ * for more details.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+ var setData = (function() {
+ var count = 0,
+ lastCalled = 0;
+
+ return function(key, value) {
+ var stamp = now(),
+ remaining = HOT_SPAN - (stamp - lastCalled);
+
+ lastCalled = stamp;
+ if (remaining > 0) {
+ if (++count >= HOT_COUNT) {
+ return key;
+ }
+ } else {
+ count = 0;
+ }
+ return baseSetData(key, value);
+ };
+ }());
+
+ /**
+ * A fallback implementation of `_.isPlainObject` which checks if `value`
+ * is an object created by the `Object` constructor or has a `[[Prototype]]`
+ * of `null`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+ function shimIsPlainObject(value) {
+ var Ctor,
+ support = lodash.support;
+
+ // Exit early for non `Object` objects.
+ if (!(isObjectLike(value) && objToString.call(value) == objectTag) ||
+ (!hasOwnProperty.call(value, 'constructor') &&
+ (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
+ return false;
+ }
+ // IE < 9 iterates inherited properties before own properties. If the first
+ // iterated property is an object's own property then there are no inherited
+ // enumerable properties.
+ var result;
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ baseForIn(value, function(subValue, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+ }
+
+ /**
+ * A fallback implementation of `Object.keys` which creates an array of the
+ * own enumerable property names of `object`.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ */
+ function shimKeys(object) {
+ var props = keysIn(object),
+ propsLength = props.length,
+ length = propsLength && object.length,
+ support = lodash.support;
+
+ var allowIndexes = length && isLength(length) &&
+ (isArray(object) || (support.nonEnumArgs && isArguments(object)));
+
+ var index = -1,
+ result = [];
+
+ while (++index < propsLength) {
+ var key = props[index];
+ if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Converts `value` to an array-like object if it is not one.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {Array|Object} Returns the array-like object.
+ */
+ function toIterable(value) {
+ if (value == null) {
+ return [];
+ }
+ if (!isLength(value.length)) {
+ return values(value);
+ }
+ return isObject(value) ? value : Object(value);
+ }
+
+ /**
+ * Converts `value` to an object if it is not one.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {Object} Returns the object.
+ */
+ function toObject(value) {
+ return isObject(value) ? value : Object(value);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements split into groups the length of `size`.
+ * If `collection` can't be split evenly, the final chunk will be the remaining
+ * elements.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to process.
+ * @param {numer} [size=1] The length of each chunk.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the new array containing chunks.
+ * @example
+ *
+ * _.chunk(['a', 'b', 'c', 'd'], 2);
+ * // => [['a', 'b'], ['c', 'd']]
+ *
+ * _.chunk(['a', 'b', 'c', 'd'], 3);
+ * // => [['a', 'b', 'c'], ['d']]
+ */
+ function chunk(array, size, guard) {
+ if (guard ? isIterateeCall(array, size, guard) : size == null) {
+ size = 1;
+ } else {
+ size = nativeMax(+size || 1, 1);
+ }
+ var index = 0,
+ length = array ? array.length : 0,
+ resIndex = -1,
+ result = Array(ceil(length / size));
+
+ while (index < length) {
+ result[++resIndex] = baseSlice(array, index, (index += size));
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result[++resIndex] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using
+ * `SameValueZero` for equality comparisons.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3], [5, 2, 10]);
+ * // => [1, 3]
+ */
+ function difference() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var value = arguments[index];
+ if (isArray(value) || isArguments(value)) {
+ break;
+ }
+ }
+ return baseDifference(value, baseFlatten(arguments, false, true, ++index));
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements dropped from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.drop([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.drop([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.drop([1, 2, 3], 5);
+ * // => []
+ *
+ * _.drop([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */
+ function drop(array, n, guard) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ if (guard ? isIterateeCall(array, n, guard) : n == null) {
+ n = 1;
+ }
+ return baseSlice(array, n < 0 ? 0 : n);
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements dropped from the end.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRight([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.dropRight([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.dropRight([1, 2, 3], 5);
+ * // => []
+ *
+ * _.dropRight([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */
+ function dropRight(array, n, guard) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ if (guard ? isIterateeCall(array, n, guard) : n == null) {
+ n = 1;
+ }
+ n = length - (+n || 0);
+ return baseSlice(array, 0, n < 0 ? 0 : n);
+ }
+
+ /**
+ * Creates a slice of `array` excluding elements dropped from the end.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * bound to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per element.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRightWhile([1, 2, 3], function(n) { return n > 1; });
+ * // => [1]
+ *
+ * var users = [
+ * { 'user': 'barney', 'status': 'busy', 'active': false },
+ * { 'user': 'fred', 'status': 'busy', 'active': true },
+ * { 'user': 'pebbles', 'status': 'away', 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.dropRightWhile(users, 'active'), 'user');
+ * // => ['barney']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.dropRightWhile(users, { 'status': 'away' }), 'user');
+ * // => ['barney', 'fred']
+ */
+ function dropRightWhile(array, predicate, thisArg) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ predicate = getCallback(predicate, thisArg, 3);
+ while (length-- && predicate(array[length], length, array)) {}
+ return baseSlice(array, 0, length + 1);
+ }
+
+ /**
+ * Creates a slice of `array` excluding elements dropped from the beginning.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * bound to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per element.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropWhile([1, 2, 3], function(n) { return n < 3; });
+ * // => [3]
+ *
+ * var users = [
+ * { 'user': 'barney', 'status': 'busy', 'active': true },
+ * { 'user': 'fred', 'status': 'busy', 'active': false },
+ * { 'user': 'pebbles', 'status': 'away', 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.dropWhile(users, 'active'), 'user');
+ * // => ['fred', 'pebbles']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.dropWhile(users, { 'status': 'busy' }), 'user');
+ * // => ['pebbles']
+ */
+ function dropWhile(array, predicate, thisArg) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ var index = -1;
+ predicate = getCallback(predicate, thisArg, 3);
+ while (++index < length && predicate(array[index], index, array)) {}
+ return baseSlice(array, index);
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element `predicate` returns truthy for, instead of the element itself.
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
+ *
+ * _.findIndex(users, function(chr) { return chr.age < 40; });
+ * // => 0
+ *
+ * // using the "_.matches" callback shorthand
+ * _.findIndex(users, { 'age': 1 });
+ * // => 2
+ *
+ * // using the "_.property" callback shorthand
+ * _.findIndex(users, 'active');
+ * // => 1
+ */
+ function findIndex(array, predicate, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ predicate = getCallback(predicate, thisArg, 3);
+ while (++index < length) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of `collection` from right to left.
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
+ *
+ * _.findLastIndex(users, function(chr) { return chr.age < 40; });
+ * // => 2
+ *
+ * // using the "_.matches" callback shorthand
+ * _.findLastIndex(users, { 'age': 40 });
+ * // => 1
+ *
+ * // using the "_.property" callback shorthand
+ * _.findLastIndex(users, 'active');
+ * // => 0
+ */
+ function findLastIndex(array, predicate, thisArg) {
+ var length = array ? array.length : 0;
+ predicate = getCallback(predicate, thisArg, 3);
+ while (length--) {
+ if (predicate(array[length], length, array)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the first element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the first element of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([]);
+ * // => undefined
+ */
+ function first(array) {
+ return array ? array[0] : undefined;
+ }
+
+ /**
+ * Flattens a nested array. If `isDeep` is `true` the array is recursively
+ * flattened, otherwise it is only flattened a single level.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isDeep] Specify a deep flatten.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * // using `isDeep`
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, 4];
+ */
+ function flatten(array, isDeep, guard) {
+ var length = array ? array.length : 0;
+ if (guard && isIterateeCall(array, isDeep, guard)) {
+ isDeep = false;
+ }
+ return length ? baseFlatten(array, isDeep) : [];
+ }
+
+ /**
+ * Recursively flattens a nested array.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to recursively flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flattenDeep([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ */
+ function flattenDeep(array) {
+ var length = array ? array.length : 0;
+ return length ? baseFlatten(array, true) : [];
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using `SameValueZero` for equality comparisons. If `fromIndex` is negative,
+ * it is used as the offset from the end of `array`. If `array` is sorted
+ * providing `true` for `fromIndex` performs a faster binary search.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * // using `fromIndex`
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * // performing a binary search
+ * _.indexOf([4, 4, 5, 5, 6, 6], 5, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return -1;
+ }
+ if (typeof fromIndex == 'number') {
+ fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
+ } else if (fromIndex) {
+ var index = binaryIndex(array, value),
+ other = array[index];
+
+ return (value === value ? value === other : other !== other) ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ */
+ function initial(array) {
+ return dropRight(array, 1);
+ }
+
+ /**
+ * Creates an array of unique values in all provided arrays using `SameValueZero`
+ * for equality comparisons.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length,
+ caches = [],
+ indexOf = getIndexOf(),
+ isCommon = indexOf == baseIndexOf;
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ caches.push(isCommon && value.length >= 120 && createCache(argsIndex && value));
+ }
+ }
+ argsLength = args.length;
+ var array = args[0],
+ index = -1,
+ length = array ? array.length : 0,
+ result = [],
+ seen = caches[0];
+
+ outer:
+ while (++index < length) {
+ value = array[index];
+ if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value)) < 0) {
+ argsIndex = argsLength;
+ while (--argsIndex) {
+ var cache = caches[argsIndex];
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+ continue outer;
+ }
+ }
+ if (seen) {
+ seen.push(value);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the last element of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ */
+ function last(array) {
+ var length = array ? array.length : 0;
+ return length ? array[length - 1] : undefined;
+ }
+
+ /**
+ * This method is like `_.indexOf` except that it iterates over elements of
+ * `array` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=array.length-1] The index to search from
+ * or `true` to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * // using `fromIndex`
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ *
+ * // performing a binary search
+ * _.lastIndexOf([4, 4, 5, 5, 6, 6], 5, true);
+ * // => 3
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return -1;
+ }
+ var index = length;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1;
+ } else if (fromIndex) {
+ index = binaryIndex(array, value, true) - 1;
+ var other = array[index];
+ return (value === value ? value === other : other !== other) ? index : -1;
+ }
+ if (value !== value) {
+ return indexOfNaN(array, index, true);
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all provided values from `array` using `SameValueZero` for equality
+ * comparisons.
+ *
+ * **Notes:**
+ * - Unlike `_.without`, this method mutates `array`.
+ * - `SameValueZero` comparisons are like strict equality comparisons, e.g. `===`,
+ * except that `NaN` matches `NaN`. See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...*} [values] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ * _.pull(array, 2, 3);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ function pull() {
+ var array = arguments[0];
+ if (!(array && array.length)) {
+ return array;
+ }
+ var index = 0,
+ indexOf = getIndexOf(),
+ length = arguments.length;
+
+ while (++index < length) {
+ var fromIndex = 0,
+ value = arguments[index];
+
+ while ((fromIndex = indexOf(array, value, fromIndex)) > -1) {
+ splice.call(array, fromIndex, 1);
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Removes elements from `array` corresponding to the given indexes and returns
+ * an array of the removed elements. Indexes may be specified as an array of
+ * indexes or as individual arguments.
+ *
+ * **Note:** Unlike `_.at`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...(number|number[])} [indexes] The indexes of elements to remove,
+ * specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = [5, 10, 15, 20];
+ * var evens = _.pullAt(array, [1, 3]);
+ *
+ * console.log(array);
+ * // => [5, 15]
+ *
+ * console.log(evens);
+ * // => [10, 20]
+ */
+ function pullAt(array) {
+ return basePullAt(array || [], baseFlatten(arguments, false, false, 1));
+ }
+
+ /**
+ * Removes all elements from `array` that `predicate` returns truthy for
+ * and returns an array of the removed elements. The predicate is bound to
+ * `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * **Note:** Unlike `_.filter`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4];
+ * var evens = _.remove(array, function(n) { return n % 2 == 0; });
+ *
+ * console.log(array);
+ * // => [1, 3]
+ *
+ * console.log(evens);
+ * // => [2, 4]
+ */
+ function remove(array, predicate, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ predicate = getCallback(predicate, thisArg, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (predicate(value, index, array)) {
+ result.push(value);
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets all but the first element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @alias tail
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ */
+ function rest(array) {
+ return drop(array, 1);
+ }
+
+ /**
+ * Creates a slice of `array` from `start` up to, but not including, `end`.
+ *
+ * **Note:** This function is used instead of `Array#slice` to support node
+ * lists in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
+ */
+ function slice(array, start, end) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
+ start = 0;
+ end = length;
+ }
+ return baseSlice(array, start, end);
+ }
+
+ /**
+ * Uses a binary search to determine the lowest index at which `value` should
+ * be inserted into `array` in order to maintain its sort order. If an iteratee
+ * function is provided it is invoked for `value` and each element of `array`
+ * to compute their sort ranking. The iteratee is bound to `thisArg` and
+ * invoked with one argument; (value).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([30, 50], 40);
+ * // => 1
+ *
+ * _.sortedIndex([4, 4, 5, 5, 6, 6], 5);
+ * // => 2
+ *
+ * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } };
+ *
+ * // using an iteratee function
+ * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) {
+ * return this.data[word];
+ * }, dict);
+ * // => 1
+ *
+ * // using the "_.property" callback shorthand
+ * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 1
+ */
+ function sortedIndex(array, value, iteratee, thisArg) {
+ var func = getCallback(iteratee);
+ return (func === baseCallback && iteratee == null)
+ ? binaryIndex(array, value)
+ : binaryIndexBy(array, value, func(iteratee, thisArg, 1));
+ }
+
+ /**
+ * This method is like `_.sortedIndex` except that it returns the highest
+ * index at which `value` should be inserted into `array` in order to
+ * maintain its sort order.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedLastIndex([4, 4, 5, 5, 6, 6], 5);
+ * // => 4
+ */
+ function sortedLastIndex(array, value, iteratee, thisArg) {
+ var func = getCallback(iteratee);
+ return (func === baseCallback && iteratee == null)
+ ? binaryIndex(array, value, true)
+ : binaryIndexBy(array, value, func(iteratee, thisArg, 1), true);
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements taken from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.take([1, 2, 3]);
+ * // => [1]
+ *
+ * _.take([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.take([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.take([1, 2, 3], 0);
+ * // => []
+ */
+ function take(array, n, guard) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ if (guard ? isIterateeCall(array, n, guard) : n == null) {
+ n = 1;
+ }
+ return baseSlice(array, 0, n < 0 ? 0 : n);
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements taken from the end.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRight([1, 2, 3]);
+ * // => [3]
+ *
+ * _.takeRight([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.takeRight([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.takeRight([1, 2, 3], 0);
+ * // => []
+ */
+ function takeRight(array, n, guard) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ if (guard ? isIterateeCall(array, n, guard) : n == null) {
+ n = 1;
+ }
+ n = length - (+n || 0);
+ return baseSlice(array, n < 0 ? 0 : n);
+ }
+
+ /**
+ * Creates a slice of `array` with elements taken from the end. Elements are
+ * taken until `predicate` returns falsey. The predicate is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per element.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRightWhile([1, 2, 3], function(n) { return n > 1; });
+ * // => [2, 3]
+ *
+ * var users = [
+ * { 'user': 'barney', 'status': 'busy', 'active': false },
+ * { 'user': 'fred', 'status': 'busy', 'active': true },
+ * { 'user': 'pebbles', 'status': 'away', 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.takeRightWhile(users, 'active'), 'user');
+ * // => ['fred', 'pebbles']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.takeRightWhile(users, { 'status': 'away' }), 'user');
+ * // => ['pebbles']
+ */
+ function takeRightWhile(array, predicate, thisArg) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ predicate = getCallback(predicate, thisArg, 3);
+ while (length-- && predicate(array[length], length, array)) {}
+ return baseSlice(array, length + 1);
+ }
+
+ /**
+ * Creates a slice of `array` with elements taken from the beginning. Elements
+ * are taken until `predicate` returns falsey. The predicate is bound to
+ * `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per element.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeWhile([1, 2, 3], function(n) { return n < 3; });
+ * // => [1, 2]
+ *
+ * var users = [
+ * { 'user': 'barney', 'status': 'busy', 'active': true },
+ * { 'user': 'fred', 'status': 'busy', 'active': false },
+ * { 'user': 'pebbles', 'status': 'away', 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.takeWhile(users, 'active'), 'user');
+ * // => ['barney']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.takeWhile(users, { 'status': 'busy' }), 'user');
+ * // => ['barney', 'fred']
+ */
+ function takeWhile(array, predicate, thisArg) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ var index = -1;
+ predicate = getCallback(predicate, thisArg, 3);
+ while (++index < length && predicate(array[index], index, array)) {}
+ return baseSlice(array, 0, index);
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * `SameValueZero` for equality comparisons.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, false, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using `SameValueZero`
+ * for equality comparisons. Providing `true` for `isSorted` performs a faster
+ * search algorithm for sorted arrays. If an iteratee function is provided it
+ * is invoked for each value in the array to generate the criterion by which
+ * uniqueness is computed. The `iteratee` is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {boolean} [isSorted] Specify the array is sorted.
+ * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+ * If a property name or object is provided it is used to create a "_.property"
+ * or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array} Returns the new duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1]);
+ * // => [1, 2]
+ *
+ * // using `isSorted`
+ * _.uniq([1, 1, 2], true);
+ * // => [1, 2]
+ *
+ * // using an iteratee function
+ * _.uniq([1, 2.5, 1.5, 2], function(n) { return this.floor(n); }, Math);
+ * // => [1, 2.5]
+ *
+ * // using the "_.property" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, iteratee, thisArg) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return [];
+ }
+ // Juggle arguments.
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = iteratee;
+ iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
+ isSorted = false;
+ }
+ var func = getCallback();
+ if (!(func === baseCallback && iteratee == null)) {
+ iteratee = func(iteratee, thisArg, 3);
+ }
+ return (isSorted && getIndexOf() == baseIndexOf)
+ ? sortedUniq(array, iteratee)
+ : baseUniq(array, iteratee);
+ }
+
+ /**
+ * This method is like `_.zip` except that it accepts an array of grouped
+ * elements and creates an array regrouping the elements to their pre-`_.zip`
+ * configuration.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array of grouped elements to process.
+ * @returns {Array} Returns the new array of regrouped elements.
+ * @example
+ *
+ * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ *
+ * _.unzip(zipped);
+ * // => [['fred', 'barney'], [30, 40], [true, false]]
+ */
+ function unzip(array) {
+ var index = -1,
+ length = (array && array.length && arrayMax(arrayMap(array, getLength))) >>> 0,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = arrayMap(array, baseProperty(index));
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all provided values using `SameValueZero` for
+ * equality comparisons.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to filter.
+ * @param {...*} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, baseSlice(arguments, 1));
+ }
+
+ /**
+ * Creates an array that is the symmetric difference of the provided arrays.
+ * See [Wikipedia](https://en.wikipedia.org/wiki/Symmetric_difference) for
+ * more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of values.
+ * @example
+ *
+ * _.xor([1, 2, 3], [5, 2, 1, 4]);
+ * // => [3, 5, 4]
+ *
+ * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+ * // => [1, 4, 5]
+ */
+ function xor() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var array = arguments[index];
+ if (isArray(array) || isArguments(array)) {
+ var result = result
+ ? baseDifference(result, array).concat(baseDifference(array, result))
+ : array;
+ }
+ }
+ return result ? baseUniq(result) : [];
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second elements
+ * of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to process.
+ * @returns {Array} Returns the new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var length = arguments.length,
+ array = Array(length);
+
+ while (length--) {
+ array[length] = arguments[length];
+ }
+ return unzip(array);
+ }
+
+ /**
+ * Creates an object composed from arrays of property names and values. Provide
+ * either a single two dimensional array, e.g. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of property names and one of corresponding values.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Array
+ * @param {Array} props The property names.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(props, values) {
+ var index = -1,
+ length = props ? props.length : 0,
+ result = {};
+
+ if (length && !values && !isArray(props[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = props[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object that wraps `value` with explicit method
+ * chaining enabled.
+ *
+ * @static
+ * @memberOf _
+ * @category Chain
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the new `lodash` object.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _.chain(users)
+ * .sortBy('age')
+ * .map(function(chr) { return chr.user + ' is ' + chr.age; })
+ * .first()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ var result = lodash(value);
+ result.__chain__ = true;
+ return result;
+ }
+
+ /**
+ * This method invokes `interceptor` and returns `value`. The interceptor is
+ * bound to `thisArg` and invoked with one argument; (value). The purpose of
+ * this method is to "tap into" a method chain in order to perform operations
+ * on intermediate results within the chain.
+ *
+ * @static
+ * @memberOf _
+ * @category Chain
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @param {*} [thisArg] The `this` binding of `interceptor`.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3])
+ * .tap(function(array) { array.pop(); })
+ * .reverse()
+ * .value();
+ * // => [2, 1]
+ */
+ function tap(value, interceptor, thisArg) {
+ interceptor.call(thisArg, value);
+ return value;
+ }
+
+ /**
+ * This method is like `_.tap` except that it returns the result of `interceptor`.
+ *
+ * @static
+ * @memberOf _
+ * @category Chain
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @param {*} [thisArg] The `this` binding of `interceptor`.
+ * @returns {*} Returns the result of `interceptor`.
+ * @example
+ *
+ * _([1, 2, 3])
+ * .last()
+ * .thru(function(value) { return [value]; })
+ * .value();
+ * // => [3]
+ */
+ function thru(value, interceptor, thisArg) {
+ return interceptor.call(thisArg, value);
+ }
+
+ /**
+ * Enables explicit method chaining on the wrapper object.
+ *
+ * @name chain
+ * @memberOf _
+ * @category Chain
+ * @returns {*} Returns the `lodash` object.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * // without explicit chaining
+ * _(users).first();
+ * // => { 'user': 'barney', 'age': 36 }
+ *
+ * // with explicit chaining
+ * _(users).chain()
+ * .first()
+ * .pick('user')
+ * .value();
+ * // => { 'user': 'barney' }
+ */
+ function wrapperChain() {
+ return chain(this);
+ }
+
+ /**
+ * Reverses the wrapped array so the first element becomes the last, the
+ * second element becomes the second to last, and so on.
+ *
+ * **Note:** This method mutates the wrapped array.
+ *
+ * @name reverse
+ * @memberOf _
+ * @category Chain
+ * @returns {Object} Returns the new reversed `lodash` object.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _(array).reverse().value()
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */
+ function wrapperReverse() {
+ var value = this.__wrapped__;
+ if (value instanceof LazyWrapper) {
+ if (this.__actions__.length) {
+ value = new LazyWrapper(this);
+ }
+ return new LodashWrapper(value.reverse());
+ }
+ return this.thru(function(value) {
+ return value.reverse();
+ });
+ }
+
+ /**
+ * Produces the result of coercing the unwrapped value to a string.
+ *
+ * @name toString
+ * @memberOf _
+ * @category Chain
+ * @returns {string} Returns the coerced string value.
+ * @example
+ *
+ * _([1, 2, 3]).toString();
+ * // => '1,2,3'
+ */
+ function wrapperToString() {
+ return (this.value() + '');
+ }
+
+ /**
+ * Executes the chained sequence to extract the unwrapped value.
+ *
+ * @name value
+ * @memberOf _
+ * @alias toJSON, valueOf
+ * @category Chain
+ * @returns {*} Returns the resolved unwrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).value();
+ * // => [1, 2, 3]
+ */
+ function wrapperValue() {
+ return baseWrapperValue(this.__wrapped__, this.__actions__);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements corresponding to the given keys, or indexes,
+ * of `collection`. Keys may be specified as individual arguments or as arrays
+ * of keys.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(number|number[]|string|string[])} [props] The property names
+ * or indexes of elements to pick, specified individually or in arrays.
+ * @returns {Array} Returns the new array of picked elements.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
+ */
+ function at(collection) {
+ var length = collection ? collection.length : 0;
+ if (isLength(length)) {
+ collection = toIterable(collection);
+ }
+ return baseAt(collection, baseFlatten(arguments, false, false, 1));
+ }
+
+ /**
+ * Checks if `value` is in `collection` using `SameValueZero` for equality
+ * comparisons. If `fromIndex` is negative, it is used as the offset from
+ * the end of `collection`.
+ *
+ * **Note:** `SameValueZero` comparisons are like strict equality comparisons,
+ * e.g. `===`, except that `NaN` matches `NaN`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @alias contains, include
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {*} target The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if a matching element is found, else `false`.
+ * @example
+ *
+ * _.includes([1, 2, 3], 1);
+ * // => true
+ *
+ * _.includes([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.includes('pebbles', 'eb');
+ * // => true
+ */
+ function includes(collection, target, fromIndex) {
+ var length = collection ? collection.length : 0;
+ if (!isLength(length)) {
+ collection = values(collection);
+ length = collection.length;
+ }
+ if (!length) {
+ return false;
+ }
+ if (typeof fromIndex == 'number') {
+ fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
+ } else {
+ fromIndex = 0;
+ }
+ return (typeof collection == 'string' || !isArray(collection) && isString(collection))
+ ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
+ : (getIndexOf(collection, target, fromIndex) > -1);
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through `iteratee`. The corresponding value
+ * of each key is the number of times the key was returned by `iteratee`.
+ * The `iteratee` is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(n) { return Math.floor(n); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(n) { return this.floor(n); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1);
+ });
+
+ /**
+ * Checks if `predicate` returns truthy for **all** elements of `collection`.
+ * The predicate is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.every(users, 'age');
+ * // => true
+ *
+ * // using the "_.matches" callback shorthand
+ * _.every(users, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, predicate, thisArg) {
+ var func = isArray(collection) ? arrayEvery : baseEvery;
+ if (typeof predicate != 'function' || typeof thisArg != 'undefined') {
+ predicate = getCallback(predicate, thisArg, 3);
+ }
+ return func(collection, predicate);
+ }
+
+ /**
+ * Iterates over elements of `collection`, returning an array of all elements
+ * `predicate` returns truthy for. The predicate is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the new filtered array.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4], function(n) { return n % 2 == 0; });
+ * // => [2, 4]
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.filter(users, 'active'), 'user');
+ * // => ['fred']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.filter(users, { 'age': 36 }), 'user');
+ * // => ['barney']
+ */
+ function filter(collection, predicate, thisArg) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ predicate = getCallback(predicate, thisArg, 3);
+ return func(collection, predicate);
+ }
+
+ /**
+ * Iterates over elements of `collection`, returning the first element
+ * `predicate` returns truthy for. The predicate is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
+ *
+ * _.result(_.find(users, function(chr) { return chr.age < 40; }), 'user');
+ * // => 'barney'
+ *
+ * // using the "_.matches" callback shorthand
+ * _.result(_.find(users, { 'age': 1 }), 'user');
+ * // => 'pebbles'
+ *
+ * // using the "_.property" callback shorthand
+ * _.result(_.find(users, 'active'), 'user');
+ * // => 'fred'
+ */
+ function find(collection, predicate, thisArg) {
+ if (isArray(collection)) {
+ var index = findIndex(collection, predicate, thisArg);
+ return index > -1 ? collection[index] : undefined;
+ }
+ predicate = getCallback(predicate, thisArg, 3);
+ return baseFind(collection, predicate, baseEach);
+ }
+
+ /**
+ * This method is like `_.find` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(n) { return n % 2 == 1; });
+ * // => 3
+ */
+ function findLast(collection, predicate, thisArg) {
+ predicate = getCallback(predicate, thisArg, 3);
+ return baseFind(collection, predicate, baseEachRight);
+ }
+
+ /**
+ * Performs a deep comparison between each element in `collection` and the
+ * source object, returning the first element that has equivalent property
+ * values.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Object} source The object of property values to match.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'status': 'busy' },
+ * { 'user': 'fred', 'age': 40, 'status': 'busy' }
+ * ];
+ *
+ * _.result(_.findWhere(users, { 'status': 'busy' }), 'user');
+ * // => 'barney'
+ *
+ * _.result(_.findWhere(users, { 'age': 40 }), 'user');
+ * // => 'fred'
+ */
+ function findWhere(collection, source) {
+ return find(collection, baseMatches(source));
+ }
+
+ /**
+ * Iterates over elements of `collection` invoking `iteratee` for each element.
+ * The `iteratee` is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Iterator functions may exit iteration early
+ * by explicitly returning `false`.
+ *
+ * **Note:** As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(n) { console.log(n); }).value();
+ * // => logs each value from left to right and returns the array
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(n, key) { console.log(n, key); });
+ * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
+ */
+ function forEach(collection, iteratee, thisArg) {
+ return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection))
+ ? arrayEach(collection, iteratee)
+ : baseEach(collection, bindCallback(iteratee, thisArg, 3));
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(n) { console.log(n); }).join(',');
+ * // => logs each value from right to left and returns the array
+ */
+ function forEachRight(collection, iteratee, thisArg) {
+ return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection))
+ ? arrayEachRight(collection, iteratee)
+ : baseEachRight(collection, bindCallback(iteratee, thisArg, 3));
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through `iteratee`. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The `iteratee` is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(n) { return Math.floor(n); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(n) { return this.floor(n); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using the "_.property" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ if (hasOwnProperty.call(result, key)) {
+ result[key].push(value);
+ } else {
+ result[key] = [value];
+ }
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through `iteratee`. The corresponding value
+ * of each key is the last element responsible for generating the key. The
+ * iteratee function is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keyData = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keyData, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keyData, function(object) { return String.fromCharCode(object.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keyData, function(object) { return this.fromCharCode(object.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in `collection`,
+ * returning an array of the results of each invoked method. Any additional
+ * arguments are provided to each invoked method. If `methodName` is a function
+ * it is invoked for, and `this` bound to, each element in `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [args] The arguments to invoke the method with.
+ * @returns {Array} Returns the array of results.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ return baseInvoke(collection, methodName, baseSlice(arguments, 2));
+ }
+
+ /**
+ * Creates an array of values by running each element in `collection` through
+ * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array} Returns the new mapped array.
+ * @example
+ *
+ * _.map([1, 2, 3], function(n) { return n * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(n) { return n * 3; });
+ * // => [3, 6, 9] (iteration order is not guaranteed)
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.map(users, 'user');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, iteratee, thisArg) {
+ var func = isArray(collection) ? arrayMap : baseMap;
+ iteratee = getCallback(iteratee, thisArg, 3);
+ return func(collection, iteratee);
+ }
+
+ /**
+ * Gets the maximum value of `collection`. If `collection` is empty or falsey
+ * `-Infinity` is returned. If an iteratee function is provided it is invoked
+ * for each value in `collection` to generate the criterion by which the value
+ * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+ * If a property name or object is provided it is used to create a "_.property"
+ * or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * _.max([]);
+ * // => -Infinity
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(users, function(chr) { return chr.age; });
+ * // => { 'user': 'fred', 'age': 40 };
+ *
+ * // using the "_.property" callback shorthand
+ * _.max(users, 'age');
+ * // => { 'user': 'fred', 'age': 40 };
+ */
+ var max = createExtremum(arrayMax);
+
+ /**
+ * Gets the minimum value of `collection`. If `collection` is empty or falsey
+ * `Infinity` is returned. If an iteratee function is provided it is invoked
+ * for each value in `collection` to generate the criterion by which the value
+ * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+ * If a property name or object is provided it is used to create a "_.property"
+ * or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * _.min([]);
+ * // => Infinity
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(users, function(chr) { return chr.age; });
+ * // => { 'user': 'barney', 'age': 36 };
+ *
+ * // using the "_.property" callback shorthand
+ * _.min(users, 'age');
+ * // => { 'user': 'barney', 'age': 36 };
+ */
+ var min = createExtremum(arrayMin, true);
+
+ /**
+ * Creates an array of elements split into two groups, the first of which
+ * contains elements `predicate` returns truthy for, while the second of which
+ * contains elements `predicate` returns falsey for. The predicate is bound
+ * to `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the array of grouped elements.
+ * @example
+ *
+ * _.partition([1, 2, 3], function(n) { return n % 2; });
+ * // => [[1, 3], [2]]
+ *
+ * _.partition([1.2, 2.3, 3.4], function(n) { return this.floor(n) % 2; }, Math);
+ * // => [[1, 3], [2]]
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
+ *
+ * // using the "_.matches" callback shorthand
+ * _.map(_.partition(users, { 'age': 1 }), function(array) { return _.pluck(array, 'user'); });
+ * // => [['pebbles'], ['barney', 'fred']]
+ *
+ * // using the "_.property" callback shorthand
+ * _.map(_.partition(users, 'active'), function(array) { return _.pluck(array, 'user'); });
+ * // => [['fred'], ['barney', 'pebbles']]
+ */
+ var partition = createAggregator(function(result, value, key) {
+ result[key ? 0 : 1].push(value);
+ }, function() { return [[], []]; });
+
+ /**
+ * Gets the value of `key` from all elements in `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} key The key of the property to pluck.
+ * @returns {Array} Returns the property values.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(users, 'user');
+ * // => ['barney', 'fred']
+ *
+ * var userIndex = _.indexBy(users, 'user');
+ * _.pluck(userIndex, 'age');
+ * // => [36, 40] (iteration order is not guaranteed)
+ */
+ function pluck(collection, key) {
+ return map(collection, baseProperty(key + ''));
+ }
+
+ /**
+ * Reduces `collection` to a value which is the accumulated result of running
+ * each element in `collection` through `iteratee`, where each successive
+ * invocation is supplied the return value of the previous. If `accumulator`
+ * is not provided the first element of `collection` is used as the initial
+ * value. The `iteratee` is bound to `thisArg`and invoked with four arguments;
+ * (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, n) { return sum + n; });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, n, key) {
+ * result[key] = n * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 } (iteration order is not guaranteed)
+ */
+ function reduce(collection, iteratee, accumulator, thisArg) {
+ var func = isArray(collection) ? arrayReduce : baseReduce;
+ return func(collection, getCallback(iteratee, thisArg, 4), accumulator, arguments.length < 3, baseEach);
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var array = [[0, 1], [2, 3], [4, 5]];
+ * _.reduceRight(array, function(flattened, other) { return flattened.concat(other); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, iteratee, accumulator, thisArg) {
+ var func = isArray(collection) ? arrayReduceRight : baseReduce;
+ return func(collection, getCallback(iteratee, thisArg, 4), accumulator, arguments.length < 3, baseEachRight);
+ }
+
+ /**
+ * The opposite of `_.filter`; this method returns the elements of `collection`
+ * that `predicate` does **not** return truthy for.
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the new filtered array.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4], function(n) { return n % 2 == 0; });
+ * // => [1, 3]
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.reject(users, 'active'), 'user');
+ * // => ['barney']
+ *
+ * // using the "_.matches" callback shorthand
+ * _.pluck(_.reject(users, { 'age': 36 }), 'user');
+ * // => ['fred']
+ */
+ function reject(collection, predicate, thisArg) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ predicate = getCallback(predicate, thisArg, 3);
+ return func(collection, function(value, index, collection) {
+ return !predicate(value, index, collection);
+ });
+ }
+
+ /**
+ * Gets a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {*} Returns the random sample(s).
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (guard ? isIterateeCall(collection, n, guard) : n == null) {
+ collection = toIterable(collection);
+ var length = collection.length;
+ return length > 0 ? collection[baseRandom(0, length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(n < 0 ? 0 : (+n || 0), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See [Wikipedia](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns the new shuffled array.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4]);
+ * // => [4, 1, 3, 2]
+ */
+ function shuffle(collection) {
+ collection = toIterable(collection);
+
+ var index = -1,
+ length = collection.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var rand = baseRandom(0, index);
+ if (index != rand) {
+ result[index] = result[rand];
+ }
+ result[rand] = collection[index];
+ }
+ return result;
+ }
+
+ /**
+ * Gets the size of `collection` by returning `collection.length` for
+ * array-like values or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns the size of `collection`.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return isLength(length) ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if `predicate` returns truthy for **any** element of `collection`.
+ * The function returns as soon as it finds a passing value and does not iterate
+ * over the entire collection. The predicate is bound to `thisArg` and invoked
+ * with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.some(users, 'active');
+ * // => true
+ *
+ * // using the "_.matches" callback shorthand
+ * _.some(users, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, predicate, thisArg) {
+ var func = isArray(collection) ? arraySome : baseSome;
+ if (typeof predicate != 'function' || typeof thisArg != 'undefined') {
+ predicate = getCallback(predicate, thisArg, 3);
+ }
+ return func(collection, predicate);
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through `iteratee`. This method performs
+ * a stable sort, that is, it preserves the original sort order of equal elements.
+ * The `iteratee` is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [iteratee=_.identity] The function
+ * invoked per iteration. If a property name or an object is provided it is
+ * used to create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(n) { return Math.sin(n); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(n) { return this.sin(n); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var users = [
+ * { 'user': 'fred' },
+ * { 'user': 'pebbles' },
+ * { 'user': 'barney' }
+ * ];
+ *
+ * // using the "_.property" callback shorthand
+ * _.pluck(_.sortBy(users, 'user'), 'user');
+ * // => ['barney', 'fred', 'pebbles']
+ */
+ function sortBy(collection, iteratee, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = isLength(length) ? Array(length) : [];
+
+ if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
+ iteratee = null;
+ }
+ iteratee = getCallback(iteratee, thisArg, 3);
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = { 'criteria': iteratee(value, key, collection), 'index': index, 'value': value };
+ });
+ return baseSortBy(result, compareAscending);
+ }
+
+ /**
+ * This method is like `_.sortBy` except that it sorts by property names
+ * instead of an iteratee function.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(string|string[])} props The property names to sort by,
+ * specified as individual property names or arrays of property names.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 26 },
+ * { 'user': 'fred', 'age': 30 }
+ * ];
+ *
+ * _.map(_.sortByAll(users, ['user', 'age']), _.values);
+ * // => [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortByAll(collection) {
+ var args = arguments;
+ if (args.length > 3 && isIterateeCall(args[1], args[2], args[3])) {
+ args = [collection, args[1]];
+ }
+ var index = -1,
+ length = collection ? collection.length : 0,
+ props = baseFlatten(args, false, false, 1),
+ result = isLength(length) ? Array(length) : [];
+
+ baseEach(collection, function(value, key, collection) {
+ var length = props.length,
+ criteria = Array(length);
+
+ while (length--) {
+ criteria[length] = value == null ? undefined : value[props[length]];
+ }
+ result[++index] = { 'criteria': criteria, 'index': index, 'value': value };
+ });
+ return baseSortBy(result, compareMultipleAscending);
+ }
+
+ /**
+ * Performs a deep comparison between each element in `collection` and the
+ * source object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Object} source The object of property values to match.
+ * @returns {Array} Returns the new filtered array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'status': 'busy', 'pets': ['hoppy'] },
+ * { 'user': 'fred', 'age': 40, 'status': 'busy', 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.pluck(_.where(users, { 'age': 36 }), 'user');
+ * // => ['barney']
+ *
+ * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user');
+ * // => ['fred']
+ *
+ * _.pluck(_.where(users, { 'status': 'busy' }), 'user');
+ * // => ['barney', 'fred']
+ */
+ function where(collection, source) {
+ return filter(collection, baseMatches(source));
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Date
+ * @example
+ *
+ * _.defer(function(stamp) { console.log(_.now() - stamp); }, _.now());
+ * // => logs the number of milliseconds it took for the deferred function to be invoked
+ */
+ var now = nativeNow || function() {
+ return new Date().getTime();
+ };
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * The opposite of `_.before`; this method creates a function that invokes
+ * `func` once it is called `n` or more times.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {number} n The number of calls before `func` is invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'done saving!' after the two async saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ if (isFunction(n)) {
+ var temp = n;
+ n = func;
+ func = temp;
+ } else {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ }
+ n = nativeIsFinite(n = +n) ? n : 0;
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that accepts up to `n` arguments ignoring any
+ * additional arguments.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @param {number} [n=func.length] The arity cap.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * _.map(['6', '8', '10'], _.ary(parseInt, 1));
+ * // => [6, 8, 10]
+ */
+ function ary(func, n, guard) {
+ if (guard && isIterateeCall(func, n, guard)) {
+ n = null;
+ }
+ n = (func && n == null) ? func.length : nativeMax(+n || 0, 0);
+ return createWrapper(func, ARY_FLAG, null, null, null, null, n);
+ }
+
+ /**
+ * Creates a function that invokes `func`, with the `this` binding and arguments
+ * of the created function, while it is called less than `n` times. Subsequent
+ * calls to the created function return the result of the last `func` invocation.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {number} n The number of calls at which `func` is no longer invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * jQuery('#add').on('click', _.before(5, addContactToList));
+ * // => allows adding up to 4 contacts to the list
+ */
+ function before(n, func) {
+ var result;
+ if (!isFunction(func)) {
+ if (isFunction(n)) {
+ var temp = n;
+ n = func;
+ func = temp;
+ } else {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ }
+ return function() {
+ if (--n > 0) {
+ result = func.apply(this, arguments);
+ } else {
+ func = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and prepends any additional `_.bind` arguments to those provided to the
+ * bound function.
+ *
+ * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** Unlike native `Function#bind` this method does not set the `length`
+ * property of bound functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {...*} [args] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var greet = function(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * };
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * var bound = _.bind(greet, object, 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * // using placeholders
+ * var bound = _.bind(greet, object, _, '!');
+ * bound('hi');
+ * // => 'hi fred!'
+ */
+ function bind(func, thisArg) {
+ var bitmask = BIND_FLAG;
+ if (arguments.length > 2) {
+ var partials = baseSlice(arguments, 2),
+ holders = replaceHolders(partials, bind.placeholder);
+
+ bitmask |= PARTIAL_FLAG;
+ }
+ return createWrapper(func, bitmask, thisArg, partials, holders);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all enumerable function
+ * properties, own and inherited, of `object` are bound.
+ *
+ * **Note:** This method does not set the `length` property of bound functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...(string|string[])} [methodNames] The object method names to bind,
+ * specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs' when the element is clicked
+ */
+ function bindAll(object) {
+ return baseBindAll(object,
+ arguments.length > 1
+ ? baseFlatten(arguments, false, false, 1)
+ : functions(object)
+ );
+ }
+
+ /**
+ * Creates a function that invokes the method at `object[key]` and prepends
+ * any additional `_.bindKey` arguments to those provided to the bound function.
+ *
+ * This method differs from `_.bind` by allowing bound functions to reference
+ * methods that may be redefined or don't yet exist.
+ * See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern)
+ * for more details.
+ *
+ * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Object} object The object the method belongs to.
+ * @param {string} key The key of the method.
+ * @param {...*} [args] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'user': 'fred',
+ * 'greet': function(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
+ * };
+ *
+ * var bound = _.bindKey(object, 'greet', 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * object.greet = function(greeting, punctuation) {
+ * return greeting + 'ya ' + this.user + punctuation;
+ * };
+ *
+ * bound('!');
+ * // => 'hiya fred!'
+ *
+ * // using placeholders
+ * var bound = _.bindKey(object, 'greet', _, '!');
+ * bound('hi');
+ * // => 'hiya fred!'
+ */
+ function bindKey(object, key) {
+ var bitmask = BIND_FLAG | BIND_KEY_FLAG;
+ if (arguments.length > 2) {
+ var partials = baseSlice(arguments, 2),
+ holders = replaceHolders(partials, bindKey.placeholder);
+
+ bitmask |= PARTIAL_FLAG;
+ }
+ return createWrapper(key, bitmask, object, partials, holders);
+ }
+
+ /**
+ * Creates a function that accepts one or more arguments of `func` that when
+ * called either invokes `func` returning its result, if all `func` arguments
+ * have been provided, or returns a function that accepts one or more of the
+ * remaining `func` arguments, and so on. The arity of `func` may be specified
+ * if `func.length` is not sufficient.
+ *
+ * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method does not set the `length` property of curried functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curry(abc);
+ *
+ * curried(1)(2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // using placeholders
+ * curried(1)(_, 3)(2);
+ * // => [1, 2, 3]
+ */
+ function curry(func, arity, guard) {
+ if (guard && isIterateeCall(func, arity, guard)) {
+ arity = null;
+ }
+ var result = createWrapper(func, CURRY_FLAG, null, null, null, null, null, arity);
+ result.placeholder = curry.placeholder;
+ return result;
+ }
+
+ /**
+ * This method is like `_.curry` except that arguments are applied to `func`
+ * in the manner of `_.partialRight` instead of `_.partial`.
+ *
+ * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method does not set the `length` property of curried functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curryRight(abc);
+ *
+ * curried(3)(2)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(2, 3)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // using placeholders
+ * curried(3)(1, _)(2);
+ * // => [1, 2, 3]
+ */
+ function curryRight(func, arity, guard) {
+ if (guard && isIterateeCall(func, arity, guard)) {
+ arity = null;
+ }
+ var result = createWrapper(func, CURRY_RIGHT_FLAG, null, null, null, null, null, arity);
+ result.placeholder = curryRight.placeholder;
+ return result;
+ }
+
+ /**
+ * Creates a function that delays invoking `func` until after `wait` milliseconds
+ * have elapsed since the last time it was invoked. The created function comes
+ * with a `cancel` method to cancel delayed invocations. Provide an options
+ * object to indicate that `func` should be invoked on the leading and/or
+ * trailing edge of the `wait` timeout. Subsequent calls to the debounced
+ * function return the result of the last `func` invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify invoking on the leading
+ * edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be
+ * delayed before it is invoked.
+ * @param {boolean} [options.trailing=true] Specify invoking on the trailing
+ * edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // ensure `batchLog` is invoked once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }));
+ *
+ * // cancel a debounced call
+ * var todoChanges = _.debounce(batchLog, 1000);
+ * Object.observe(models.todo, todoChanges);
+ *
+ * Object.observe(models, function(changes) {
+ * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
+ * todoChanges.cancel();
+ * }
+ * }, ['delete']);
+ *
+ * // ...at some point `models.todo` is changed
+ * models.todo.completed = true;
+ *
+ * // ...before 1 second has passed `models.todo` is deleted
+ * // which cancels the debounced `todoChanges` call
+ * delete models.todo;
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ wait = wait < 0 ? 0 : wait;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+
+ function cancel() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ }
+
+ function delayed() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0 || remaining > wait) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ }
+
+ function maxDelayed() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ }
+
+ function debounced() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0 || remaining > maxWait;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ }
+ debounced.cancel = cancel;
+ return debounced;
+ }
+
+ /**
+ * Defers invoking the `func` until the current call stack has cleared. Any
+ * additional arguments are provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to defer.
+ * @param {...*} [args] The arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ return baseDelay(func, 1, arguments, 1);
+ }
+
+ /**
+ * Invokes `func` after `wait` milliseconds. Any additional arguments are
+ * provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {...*} [args] The arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ return baseDelay(func, wait, arguments, 2);
+ }
+
+ /**
+ * Creates a function that returns the result of invoking the provided
+ * functions with the `this` binding of the created function, where each
+ * successive invocation is supplied the return value of the previous.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {...Function} [funcs] Functions to invoke.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * function add(x, y) {
+ * return x + y;
+ * }
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var addSquare = _.flow(add, square);
+ * addSquare(1, 2);
+ * // => 9
+ */
+ function flow() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ if (!length) {
+ return function() {};
+ }
+ if (!arrayEvery(funcs, isFunction)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return function() {
+ var index = 0,
+ result = funcs[index].apply(this, arguments);
+
+ while (++index < length) {
+ result = funcs[index].call(this, result);
+ }
+ return result;
+ };
+ }
+
+ /**
+ * This method is like `_.flow` except that it creates a function that
+ * invokes the provided functions from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias backflow, compose
+ * @category Function
+ * @param {...Function} [funcs] Functions to invoke.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * function add(x, y) {
+ * return x + y;
+ * }
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var addSquare = _.flowRight(square, add);
+ * addSquare(1, 2);
+ * // => 9
+ */
+ function flowRight() {
+ var funcs = arguments,
+ fromIndex = funcs.length - 1;
+
+ if (fromIndex < 0) {
+ return function() {};
+ }
+ if (!arrayEvery(funcs, isFunction)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return function() {
+ var index = fromIndex,
+ result = funcs[index].apply(this, arguments);
+
+ while (index--) {
+ result = funcs[index].call(this, result);
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it determines the cache key for storing the result based on the
+ * arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is coerced to a string and used as the
+ * cache key. The `func` is invoked with the `this` binding of the memoized
+ * function.
+ *
+ * **Note:** The cache is exposed as the `cache` property on the memoized
+ * function. Its creation may be customized by replacing the `_.memoize.Cache`
+ * constructor with one whose instances implement the ES `Map` method interface
+ * of `get`, `has`, and `set`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-properties-of-the-map-prototype-object)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] The function to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var upperCase = _.memoize(function(string) {
+ * return string.toUpperCase();
+ * });
+ *
+ * upperCase('fred');
+ * // => 'FRED'
+ *
+ * // modifying the result cache
+ * upperCase.cache.set('fred', 'BARNEY');
+ * upperCase('fred');
+ * // => 'BARNEY'
+ *
+ * // replacing `_.memoize.Cache`
+ * var object = { 'user': 'fred' };
+ * var other = { 'user': 'barney' };
+ * var identity = _.memoize(_.identity);
+ *
+ * identity(object);
+ * // => { 'user': 'fred' }
+ * identity(other);
+ * // => { 'user': 'fred' }
+ *
+ * _.memoize.Cache = WeakMap;
+ * var identity = _.memoize(_.identity);
+ *
+ * identity(object);
+ * // => { 'user': 'fred' }
+ * identity(other);
+ * // => { 'user': 'barney' }
+ */
+ function memoize(func, resolver) {
+ if (!isFunction(func) || (resolver && !isFunction(resolver))) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var memoized = function() {
+ var cache = memoized.cache,
+ key = resolver ? resolver.apply(this, arguments) : arguments[0];
+
+ if (cache.has(key)) {
+ return cache.get(key);
+ }
+ var result = func.apply(this, arguments);
+ cache.set(key, result);
+ return result;
+ };
+ memoized.cache = new memoize.Cache;
+ return memoized;
+ }
+
+ /**
+ * Creates a function that negates the result of the predicate `func`. The
+ * `func` predicate is invoked with the `this` binding and arguments of the
+ * created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} predicate The predicate to negate.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * function isEven(n) {
+ * return n % 2 == 0;
+ * }
+ *
+ * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+ * // => [1, 3, 5]
+ */
+ function negate(predicate) {
+ if (!isFunction(predicate)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return function() {
+ return !predicate.apply(this, arguments);
+ };
+ }
+
+ /**
+ * Creates a function that is restricted to invoking `func` once. Repeat calls
+ * to the function return the value of the first call. The `func` is invoked
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Function
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` invokes `createApplication` once
+ */
+ function once(func) {
+ return before(func, 2);
+ }
+
+ /**
+ * Creates a function that invokes `func` with `partial` arguments prepended
+ * to those provided to the new function. This method is like `_.bind` except
+ * it does **not** alter the `this` binding.
+ *
+ * The `_.partial.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** This method does not set the `length` property of partially
+ * applied functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [args] The arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) {
+ * return greeting + ' ' + name;
+ * };
+ *
+ * var sayHelloTo = _.partial(greet, 'hello');
+ * sayHelloTo('fred');
+ * // => 'hello fred'
+ *
+ * // using placeholders
+ * var greetFred = _.partial(greet, _, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ var partials = baseSlice(arguments, 1),
+ holders = replaceHolders(partials, partial.placeholder);
+
+ return createWrapper(func, PARTIAL_FLAG, null, partials, holders);
+ }
+
+ /**
+ * This method is like `_.partial` except that partially applied arguments
+ * are appended to those provided to the new function.
+ *
+ * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** This method does not set the `length` property of partially
+ * applied functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [args] The arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) {
+ * return greeting + ' ' + name;
+ * };
+ *
+ * var greetFred = _.partialRight(greet, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
+ *
+ * // using placeholders
+ * var sayHelloTo = _.partialRight(greet, 'hello', _);
+ * sayHelloTo('fred');
+ * // => 'hello fred'
+ */
+ function partialRight(func) {
+ var partials = baseSlice(arguments, 1),
+ holders = replaceHolders(partials, partialRight.placeholder);
+
+ return createWrapper(func, PARTIAL_RIGHT_FLAG, null, partials, holders);
+ }
+
+ /**
+ * Creates a function that invokes `func` with arguments arranged according
+ * to the specified indexes where the argument value at the first index is
+ * provided as the first argument, the argument value at the second index is
+ * provided as the second argument, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to rearrange arguments for.
+ * @param {...(number|number[])} indexes The arranged argument indexes,
+ * specified as individual indexes or arrays of indexes.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var rearged = _.rearg(function(a, b, c) {
+ * return [a, b, c];
+ * }, 2, 0, 1);
+ *
+ * rearged('b', 'c', 'a')
+ * // => ['a', 'b', 'c']
+ *
+ * var map = _.rearg(_.map, [1, 0]);
+ * map(function(n) { return n * 3; }, [1, 2, 3]);
+ * // => [3, 6, 9]
+ */
+ function rearg(func) {
+ var indexes = baseFlatten(arguments, false, false, 1);
+ return createWrapper(func, REARG_FLAG, null, null, null, indexes);
+ }
+
+ /**
+ * Creates a function that only invokes `func` at most once per every `wait`
+ * milliseconds. The created function comes with a `cancel` method to cancel
+ * delayed invocations. Provide an options object to indicate that `func`
+ * should be invoked on the leading and/or trailing edge of the `wait` timeout.
+ * Subsequent calls to the throttled function return the result of the last
+ * `func` call.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
+ * for details over the differences between `_.throttle` and `_.debounce`.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle invocations to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify invoking on the leading
+ * edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify invoking on the trailing
+ * edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ *
+ * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false })
+ * jQuery('.interactive').on('click', throttled);
+ *
+ * // cancel a trailing throttled call
+ * jQuery(window).on('popstate', throttled.cancel);
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+ debounceOptions.leading = leading;
+ debounceOptions.maxWait = +wait;
+ debounceOptions.trailing = trailing;
+ return debounce(func, wait, debounceOptions);
+ }
+
+ /**
+ * Creates a function that provides `value` to the wrapper function as its
+ * first argument. Any additional arguments provided to the function are
+ * appended to those provided to the wrapper function. The wrapper is invoked
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '' + func(text) + '
';
+ * });
+ *
+ * p('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles
'
+ */
+ function wrap(value, wrapper) {
+ wrapper = wrapper == null ? identity : wrapper;
+ return createWrapper(wrapper, PARTIAL_FLAG, null, [value], []);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
+ * otherwise they are assigned by reference. If `customizer` is provided it is
+ * invoked to produce the cloned values. If `customizer` returns `undefined`
+ * cloning is handled by the method instead. The `customizer` is bound to
+ * `thisArg` and invoked with two argument; (value [, index|key, object]).
+ *
+ * **Note:** This method is loosely based on the structured clone algorithm.
+ * The enumerable properties of `arguments` objects and objects created by
+ * constructors other than `Object` are cloned to plain `Object` objects. An
+ * empty object is returned for uncloneable values such as functions, DOM nodes,
+ * Maps, Sets, and WeakMaps. See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @param {Function} [customizer] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * var shallow = _.clone(users);
+ * shallow[0] === users[0];
+ * // => true
+ *
+ * var deep = _.clone(users, true);
+ * deep[0] === users[0];
+ * // => false
+ *
+ * // using a customizer callback
+ * var body = _.clone(document.body, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * });
+ *
+ * body === document.body
+ * // => false
+ * body.nodeName
+ * // => BODY
+ * body.childNodes.length;
+ * // => 0
+ */
+ function clone(value, isDeep, customizer, thisArg) {
+ // Juggle arguments.
+ if (typeof isDeep != 'boolean' && isDeep != null) {
+ thisArg = customizer;
+ customizer = isIterateeCall(value, isDeep, thisArg) ? null : isDeep;
+ isDeep = false;
+ }
+ customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 1);
+ return baseClone(value, isDeep, customizer);
+ }
+
+ /**
+ * Creates a deep clone of `value`. If `customizer` is provided it is invoked
+ * to produce the cloned values. If `customizer` returns `undefined` cloning
+ * is handled by the method instead. The `customizer` is bound to `thisArg`
+ * and invoked with two argument; (value [, index|key, object]).
+ *
+ * **Note:** This method is loosely based on the structured clone algorithm.
+ * The enumerable properties of `arguments` objects and objects created by
+ * constructors other than `Object` are cloned to plain `Object` objects. An
+ * empty object is returned for uncloneable values such as functions, DOM nodes,
+ * Maps, Sets, and WeakMaps. See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to deep clone.
+ * @param {Function} [customizer] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * var deep = _.cloneDeep(users);
+ * deep[0] === users[0];
+ * // => false
+ *
+ * // using a customizer callback
+ * var el = _.cloneDeep(document.body, function(value) {
+ * return _.isElement(value) ? value.cloneNode(true) : undefined;
+ * });
+ *
+ * body === document.body
+ * // => false
+ * body.nodeName
+ * // => BODY
+ * body.childNodes.length;
+ * // => 20
+ */
+ function cloneDeep(value, customizer, thisArg) {
+ customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 1);
+ return baseClone(value, true, customizer);
+ }
+
+ /**
+ * Checks if `value` is classified as an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })();
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ var length = isObjectLike(value) ? value.length : undefined;
+ return (isLength(length) && objToString.call(value) == argsTag) || false;
+ }
+
+ /**
+ * Checks if `value` is classified as an `Array` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ */
+ var isArray = nativeIsArray || function(value) {
+ return (isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag) || false;
+ };
+
+ /**
+ * Checks if `value` is classified as a boolean primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isBoolean(false);
+ * // => true
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return (value === true || value === false || isObjectLike(value) && objToString.call(value) == boolTag) || false;
+ }
+
+ /**
+ * Checks if `value` is classified as a `Date` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ *
+ * _.isDate('Mon April 23 2012');
+ * // => false
+ */
+ function isDate(value) {
+ return (isObjectLike(value) && objToString.call(value) == dateTag) || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ *
+ * _.isElement('');
+ * // => false
+ */
+ function isElement(value) {
+ return (value && value.nodeType === 1 && isObjectLike(value) &&
+ objToString.call(value).indexOf('Element') > -1) || false;
+ }
+ // Fallback for environments without DOM support.
+ if (!support.dom) {
+ isElement = function(value) {
+ return (value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value)) || false;
+ };
+ }
+
+ /**
+ * Checks if a value is empty. A value is considered empty unless it is an
+ * `arguments` object, array, string, or jQuery-like collection with a length
+ * greater than `0` or an object with own enumerable properties.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty(null);
+ * // => true
+ *
+ * _.isEmpty(true);
+ * // => true
+ *
+ * _.isEmpty(1);
+ * // => true
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({ 'a': 1 });
+ * // => false
+ */
+ function isEmpty(value) {
+ if (value == null) {
+ return true;
+ }
+ var length = value.length;
+ if (isLength(length) && (isArray(value) || isString(value) || isArguments(value) ||
+ (isObjectLike(value) && isFunction(value.splice)))) {
+ return !length;
+ }
+ return !keys(value).length;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent. If `customizer` is provided it is invoked to compare values.
+ * If `customizer` returns `undefined` comparisons are handled by the method
+ * instead. The `customizer` is bound to `thisArg` and invoked with three
+ * arguments; (value, other [, index|key]).
+ *
+ * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+ * numbers, `Object` objects, regexes, and strings. Functions and DOM nodes
+ * are **not** supported. Provide a customizer function to extend support
+ * for comparing other values.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ * var other = { 'user': 'fred' };
+ *
+ * object == other;
+ * // => false
+ *
+ * _.isEqual(object, other);
+ * // => true
+ *
+ * // using a customizer callback
+ * var array = ['hello', 'goodbye'];
+ * var other = ['hi', 'goodbye'];
+ *
+ * _.isEqual(array, other, function(value, other) {
+ * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined;
+ * });
+ * // => true
+ */
+ function isEqual(value, other, customizer, thisArg) {
+ customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3);
+ if (!customizer && isStrictComparable(value) && isStrictComparable(other)) {
+ return value === other;
+ }
+ var result = customizer ? customizer(value, other) : undefined;
+ return typeof result == 'undefined' ? baseIsEqual(value, other, customizer) : !!result;
+ }
+
+ /**
+ * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
+ * `SyntaxError`, `TypeError`, or `URIError` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
+ * @example
+ *
+ * _.isError(new Error);
+ * // => true
+ *
+ * _.isError(Error);
+ * // => false
+ */
+ function isError(value) {
+ return (isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag) || false;
+ }
+
+ /**
+ * Checks if `value` is a finite primitive number.
+ *
+ * **Note:** This method is based on ES `Number.isFinite`. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
+ * @example
+ *
+ * _.isFinite(10);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => false
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite(Object(10));
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ var isFinite = nativeNumIsFinite || function(value) {
+ return typeof value == 'number' && nativeIsFinite(value);
+ };
+
+ /**
+ * Checks if `value` is classified as a `Function` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
+ */
+ function isFunction(value) {
+ // Avoid a Chakra JIT bug in compatibility modes of IE 11.
+ // See https://github.com/jashkenas/underscore/issues/1621 for more details.
+ return typeof value == 'function' || false;
+ }
+ // Fallback for environments that return incorrect `typeof` operator results.
+ if (isFunction(/x/) || (Uint8Array && !isFunction(Uint8Array))) {
+ isFunction = function(value) {
+ // The use of `Object#toString` avoids issues with the `typeof` operator
+ // in older versions of Chrome and Safari which return 'function' for regexes
+ // and Safari 8 equivalents which return 'object' for typed array constructors.
+ return objToString.call(value) == funcTag;
+ };
+ }
+
+ /**
+ * Checks if `value` is the language type of `Object`.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * **Note:** See the [ES5 spec](https://es5.github.io/#x8) for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // Avoid a V8 JIT bug in Chrome 19-20.
+ // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
+ var type = typeof value;
+ return type == 'function' || (value && type == 'object') || false;
+ }
+
+ /**
+ * Performs a deep comparison between `object` and `source` to determine if
+ * `object` contains equivalent property values. If `customizer` is provided
+ * it is invoked to compare values. If `customizer` returns `undefined`
+ * comparisons are handled by the method instead. The `customizer` is bound
+ * to `thisArg` and invoked with three arguments; (value, other, index|key).
+ *
+ * **Note:** This method supports comparing properties of arrays, booleans,
+ * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions
+ * and DOM nodes are **not** supported. Provide a customizer function to extend
+ * support for comparing other values.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {Object} source The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @example
+ *
+ * var object = { 'user': 'fred', 'age': 40 };
+ *
+ * _.isMatch(object, { 'age': 40 });
+ * // => true
+ *
+ * _.isMatch(object, { 'age': 36 });
+ * // => false
+ *
+ * // using a customizer callback
+ * var object = { 'greeting': 'hello' };
+ * var source = { 'greeting': 'hi' };
+ *
+ * _.isMatch(object, source, function(value, other) {
+ * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined;
+ * });
+ * // => true
+ */
+ function isMatch(object, source, customizer, thisArg) {
+ var props = keys(source),
+ length = props.length;
+
+ customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3);
+ if (!customizer && length == 1) {
+ var key = props[0],
+ value = source[key];
+
+ if (isStrictComparable(value)) {
+ return object != null && value === object[key] && hasOwnProperty.call(object, key);
+ }
+ }
+ var values = Array(length),
+ strictCompareFlags = Array(length);
+
+ while (length--) {
+ value = values[length] = source[props[length]];
+ strictCompareFlags[length] = isStrictComparable(value);
+ }
+ return baseIsMatch(object, props, values, strictCompareFlags, customizer);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * **Note:** This method is not the same as native `isNaN` which returns `true`
+ * for `undefined` and other non-numeric values. See the [ES5 spec](https://es5.github.io/#x15.1.2.4)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // An `NaN` primitive is the only value that is not equal to itself.
+ // Perform the `toStringTag` check first to avoid errors with some host objects in IE.
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
+ * @example
+ *
+ * _.isNative(Array.prototype.push);
+ * // => true
+ *
+ * _.isNative(_);
+ * // => false
+ */
+ function isNative(value) {
+ if (value == null) {
+ return false;
+ }
+ if (objToString.call(value) == funcTag) {
+ return reNative.test(fnToString.call(value));
+ }
+ return (isObjectLike(value) && reHostCtor.test(value)) || false;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(void 0);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is classified as a `Number` primitive or object.
+ *
+ * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified
+ * as numbers, use the `_.isFinite` method.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4);
+ * // => true
+ *
+ * _.isNumber(NaN);
+ * // => true
+ *
+ * _.isNumber('8.4');
+ * // => false
+ */
+ function isNumber(value) {
+ return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag) || false;
+ }
+
+ /**
+ * Checks if `value` is a plain object, that is, an object created by the
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
+ *
+ * **Note:** This method assumes objects created by the `Object` constructor
+ * have no inherited enumerable properties.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * _.isPlainObject(new Foo);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && objToString.call(value) == objectTag)) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
+
+ /**
+ * Checks if `value` is classified as a `RegExp` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isRegExp(/abc/);
+ * // => true
+ *
+ * _.isRegExp('/abc/');
+ * // => false
+ */
+ function isRegExp(value) {
+ return (isObjectLike(value) && objToString.call(value) == regexpTag) || false;
+ }
+
+ /**
+ * Checks if `value` is classified as a `String` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isString('abc');
+ * // => true
+ *
+ * _.isString(1);
+ * // => false
+ */
+ function isString(value) {
+ return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag) || false;
+ }
+
+ /**
+ * Checks if `value` is classified as a typed array.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isTypedArray(new Uint8Array);
+ * // => true
+ *
+ * _.isTypedArray([]);
+ * // => false
+ */
+ function isTypedArray(value) {
+ return (isObjectLike(value) && isLength(value.length) && typedArrayTags[objToString.call(value)]) || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ *
+ * _.isUndefined(null);
+ * // => false
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Converts `value` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {Array} Returns the converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3);
+ * // => [2, 3]
+ */
+ function toArray(value) {
+ var length = value ? value.length : 0;
+ if (!isLength(length)) {
+ return values(value);
+ }
+ if (!length) {
+ return [];
+ }
+ return arrayCopy(value);
+ }
+
+ /**
+ * Converts `value` to a plain object flattening inherited enumerable
+ * properties of `value` to own properties of the plain object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {Object} Returns the converted plain object.
+ * @example
+ *
+ * function Foo() {
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.assign({ 'a': 1 }, new Foo);
+ * // => { 'a': 1, 'b': 2 }
+ *
+ * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
+ * // => { 'a': 1, 'b': 2, 'c': 3 }
+ */
+ function toPlainObject(value) {
+ return baseCopy(value, keysIn(value));
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources overwrite property assignments of previous sources.
+ * If `customizer` is provided it is invoked to produce the assigned values.
+ * The `customizer` is bound to `thisArg` and invoked with five arguments;
+ * (objectValue, sourceValue, key, object, source).
+ *
+ * @static
+ * @memberOf _
+ * @alias extend
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @param {Function} [customizer] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
+ * // => { 'user': 'fred', 'age': 40 }
+ *
+ * // using a customizer callback
+ * var defaults = _.partialRight(_.assign, function(value, other) {
+ * return typeof value == 'undefined' ? other : value;
+ * });
+ *
+ * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
+ * // => { 'user': 'barney', 'age': 36 }
+ */
+ var assign = createAssigner(baseAssign);
+
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties, guard) {
+ var result = baseCreate(prototype);
+ if (guard && isIterateeCall(prototype, properties, guard)) {
+ properties = null;
+ }
+ return properties ? baseCopy(properties, result, keys(properties)) : result;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property are ignored.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
+ * // => { 'user': 'barney', 'age': 36 }
+ */
+ function defaults(object) {
+ if (object == null) {
+ return object;
+ }
+ var args = arrayCopy(arguments);
+ args.push(assignDefaults);
+ return assign.apply(undefined, args);
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it returns the key of the
+ * first element `predicate` returns truthy for, instead of the element itself.
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
+ * @example
+ *
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
+ *
+ * _.findKey(users, function(chr) { return chr.age < 40; });
+ * // => 'barney' (iteration order is not guaranteed)
+ *
+ * // using the "_.matches" callback shorthand
+ * _.findKey(users, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using the "_.property" callback shorthand
+ * _.findKey(users, 'active');
+ * // => 'barney'
+ */
+ function findKey(object, predicate, thisArg) {
+ predicate = getCallback(predicate, thisArg, 3);
+ return baseFind(object, predicate, baseForOwn, true);
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements of
+ * a collection in the opposite order.
+ *
+ * If a property name is provided for `predicate` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
+ * @example
+ *
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
+ *
+ * _.findLastKey(users, function(chr) { return chr.age < 40; });
+ * // => returns `pebbles` assuming `_.findKey` returns `barney`
+ *
+ * // using the "_.matches" callback shorthand
+ * _.findLastKey(users, { 'age': 36 });
+ * // => 'barney'
+ *
+ * // using the "_.property" callback shorthand
+ * _.findLastKey(users, 'active');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, predicate, thisArg) {
+ predicate = getCallback(predicate, thisArg, 3);
+ return baseFind(object, predicate, baseForOwnRight, true);
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object invoking
+ * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked
+ * with three arguments; (value, key, object). Iterator functions may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.forIn(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed)
+ */
+ function forIn(object, iteratee, thisArg) {
+ if (typeof iteratee != 'function' || typeof thisArg != 'undefined') {
+ iteratee = bindCallback(iteratee, thisArg, 3);
+ }
+ return baseFor(object, iteratee, keysIn);
+ }
+
+ /**
+ * This method is like `_.forIn` except that it iterates over properties of
+ * `object` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.forInRight(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c'
+ */
+ function forInRight(object, iteratee, thisArg) {
+ iteratee = bindCallback(iteratee, thisArg, 3);
+ return baseForRight(object, iteratee, keysIn);
+ }
+
+ /**
+ * Iterates over own enumerable properties of an object invoking `iteratee`
+ * for each property. The `iteratee` is bound to `thisArg` and invoked with
+ * three arguments; (value, key, object). Iterator functions may exit iteration
+ * early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(n, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (iteration order is not guaranteed)
+ */
+ function forOwn(object, iteratee, thisArg) {
+ if (typeof iteratee != 'function' || typeof thisArg != 'undefined') {
+ iteratee = bindCallback(iteratee, thisArg, 3);
+ }
+ return baseForOwn(object, iteratee);
+ }
+
+ /**
+ * This method is like `_.forOwn` except that it iterates over properties of
+ * `object` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(n, key) {
+ * console.log(key);
+ * });
+ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+ */
+ function forOwnRight(object, iteratee, thisArg) {
+ iteratee = bindCallback(iteratee, thisArg, 3);
+ return baseForRight(object, iteratee, keys);
+ }
+
+ /**
+ * Creates an array of function property names from all enumerable properties,
+ * own and inherited, of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the new array of property names.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', ...]
+ */
+ function functions(object) {
+ return baseFunctions(object, keysIn(object));
+ }
+
+ /**
+ * Checks if `key` exists as a direct property of `object` instead of an
+ * inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @param {string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of `object`.
+ * If `object` contains duplicate values, subsequent values overwrite property
+ * assignments of previous values unless `multiValue` is `true`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to invert.
+ * @param {boolean} [multiValue] Allow multiple values per key.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {Object} Returns the new inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ *
+ * // without `multiValue`
+ * _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' });
+ * // => { 'fred': 'third', 'barney': 'second' }
+ *
+ * // with `multiValue`
+ * _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' }, true);
+ * // => { 'fred': ['first', 'third'], 'barney': ['second'] }
+ */
+ function invert(object, multiValue, guard) {
+ if (guard && isIterateeCall(object, multiValue, guard)) {
+ multiValue = null;
+ }
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index],
+ value = object[key];
+
+ if (multiValue) {
+ if (hasOwnProperty.call(result, value)) {
+ result[value].push(key);
+ } else {
+ result[value] = [key];
+ }
+ }
+ else {
+ result[value] = key;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array of the own enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keys(new Foo);
+ * // => ['a', 'b'] (iteration order is not guaranteed)
+ *
+ * _.keys('hi');
+ * // => ['0', '1']
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (object) {
+ var Ctor = object.constructor,
+ length = object.length;
+ }
+ if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
+ (typeof object != 'function' && (length && isLength(length)))) {
+ return shimKeys(object);
+ }
+ return isObject(object) ? nativeKeys(object) : [];
+ };
+
+ /**
+ * Creates an array of the own and inherited enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keysIn(new Foo);
+ * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
+ */
+ function keysIn(object) {
+ if (object == null) {
+ return [];
+ }
+ if (!isObject(object)) {
+ object = Object(object);
+ }
+ var length = object.length;
+ length = (length && isLength(length) &&
+ (isArray(object) || (support.nonEnumArgs && isArguments(object))) && length) || 0;
+
+ var Ctor = object.constructor,
+ index = -1,
+ isProto = typeof Ctor == 'function' && Ctor.prototype == object,
+ result = Array(length),
+ skipIndexes = length > 0;
+
+ while (++index < length) {
+ result[index] = (index + '');
+ }
+ for (var key in object) {
+ if (!(skipIndexes && isIndex(key, length)) &&
+ !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object with the same keys as `object` and values generated by
+ * running each own enumerable property of `object` through `iteratee`. The
+ * iteratee function is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * If a property name is provided for `iteratee` the created "_.property"
+ * style callback returns the property value of the given element.
+ *
+ * If an object is provided for `iteratee` the created "_.matches" style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration. If a property name or object is provided it is used to
+ * create a "_.property" or "_.matches" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Object} Returns the new mapped object.
+ * @example
+ *
+ * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(n) { return n * 3; });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ *
+ * var users = {
+ * 'fred': { 'user': 'fred', 'age': 40 },
+ * 'pebbles': { 'user': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // using the "_.property" callback shorthand
+ * _.mapValues(users, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
+ */
+ function mapValues(object, iteratee, thisArg) {
+ var result = {};
+ iteratee = getCallback(iteratee, thisArg, 3);
+
+ baseForOwn(object, function(value, key, object) {
+ result[key] = iteratee(value, key, object);
+ });
+ return result;
+ }
+
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined` into the destination object. Subsequent sources
+ * overwrite property assignments of previous sources. If `customizer` is
+ * provided it is invoked to produce the merged values of the destination and
+ * source properties. If `customizer` returns `undefined` merging is handled
+ * by the method instead. The `customizer` is bound to `thisArg` and invoked
+ * with five arguments; (objectValue, sourceValue, key, object, source).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @param {Function} [customizer] The function to customize merging properties.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var users = {
+ * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
+ * };
+ *
+ * var ages = {
+ * 'data': [{ 'age': 36 }, { 'age': 40 }]
+ * };
+ *
+ * _.merge(users, ages);
+ * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
+ *
+ * // using a customizer callback
+ * var object = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var other = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(object, other, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
+ */
+ var merge = createAssigner(baseMerge);
+
+ /**
+ * The opposite of `_.pick`; this method creates an object composed of the
+ * own and inherited enumerable properties of `object` that are not omitted.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If `predicate` is provided it is invoked for each property
+ * of `object` omitting the properties `predicate` returns truthy for. The
+ * predicate is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function|...(string|string[])} [predicate] The function invoked per
+ * iteration or property names to omit, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'user': 'fred', 'age': 40 };
+ *
+ * _.omit(object, 'age');
+ * // => { 'user': 'fred' }
+ *
+ * _.omit(object, _.isNumber);
+ * // => { 'user': 'fred' }
+ */
+ function omit(object, predicate, thisArg) {
+ if (object == null) {
+ return {};
+ }
+ if (typeof predicate != 'function') {
+ var props = arrayMap(baseFlatten(arguments, false, false, 1), String);
+ return pickByArray(object, baseDifference(keysIn(object), props));
+ }
+ predicate = bindCallback(predicate, thisArg, 3);
+ return pickByCallback(object, function(value, key, object) {
+ return !predicate(value, key, object);
+ });
+ }
+
+ /**
+ * Creates a two dimensional array of the key-value pairs for `object`,
+ * e.g. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of the picked `object` properties. Property
+ * names may be specified as individual arguments or as arrays of property
+ * names. If `predicate` is provided it is invoked for each property of `object`
+ * picking the properties `predicate` returns truthy for. The predicate is
+ * bound to `thisArg` and invoked with three arguments; (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function|...(string|string[])} [predicate] The function invoked per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'user': 'fred', 'age': 40 };
+ *
+ * _.pick(object, 'user');
+ * // => { 'user': 'fred' }
+ *
+ * _.pick(object, _.isString);
+ * // => { 'user': 'fred' }
+ */
+ function pick(object, predicate, thisArg) {
+ if (object == null) {
+ return {};
+ }
+ return typeof predicate == 'function'
+ ? pickByCallback(object, bindCallback(predicate, thisArg, 3))
+ : pickByArray(object, baseFlatten(arguments, false, false, 1));
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If the value of `key` is
+ * a function it is invoked with the `this` binding of `object` and its result
+ * is returned, else the property value is returned. If the property value is
+ * `undefined` the `defaultValue` is used in its place.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the property to resolve.
+ * @param {*} [defaultValue] The value returned if the property value
+ * resolves to `undefined`.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = { 'user': 'fred', 'age': _.constant(40) };
+ *
+ * _.result(object, 'user');
+ * // => 'fred'
+ *
+ * _.result(object, 'age');
+ * // => 40
+ *
+ * _.result(object, 'status', 'busy');
+ * // => 'busy'
+ *
+ * _.result(object, 'status', _.constant('busy'));
+ * // => 'busy'
+ */
+ function result(object, key, defaultValue) {
+ var value = object == null ? undefined : object[key];
+ if (typeof value == 'undefined') {
+ value = defaultValue;
+ }
+ return isFunction(value) ? value.call(object) : value;
+ }
+
+ /**
+ * An alternative to `_.reduce`; this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own enumerable
+ * properties through `iteratee`, with each invocation potentially mutating
+ * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked
+ * with four arguments; (accumulator, value, key, object). Iterator functions
+ * may exit iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var squares = _.transform([1, 2, 3, 4, 5, 6], function(result, n) {
+ * n *= n;
+ * if (n % 2) {
+ * return result.push(n) < 3;
+ * }
+ * });
+ * // => [1, 9, 25]
+ *
+ * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, n, key) {
+ * result[key] = n * 3;
+ * });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function transform(object, iteratee, accumulator, thisArg) {
+ var isArr = isArray(object) || isTypedArray(object);
+ iteratee = getCallback(iteratee, thisArg, 4);
+
+ if (accumulator == null) {
+ if (isArr || isObject(object)) {
+ var Ctor = object.constructor;
+ if (isArr) {
+ accumulator = isArray(object) ? new Ctor : [];
+ } else {
+ accumulator = baseCreate(typeof Ctor == 'function' && Ctor.prototype);
+ }
+ } else {
+ accumulator = {};
+ }
+ }
+ (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {
+ return iteratee(accumulator, value, index, object);
+ });
+ return accumulator;
+ }
+
+ /**
+ * Creates an array of the own enumerable property values of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property values.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.values(new Foo);
+ * // => [1, 2] (iteration order is not guaranteed)
+ *
+ * _.values('hi');
+ * // => ['h', 'i']
+ */
+ function values(object) {
+ return baseValues(object, keys(object));
+ }
+
+ /**
+ * Creates an array of the own and inherited enumerable property values
+ * of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property values.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.valuesIn(new Foo);
+ * // => [1, 2, 3] (iteration order is not guaranteed)
+ */
+ function valuesIn(object) {
+ return baseValues(object, keysIn(object));
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number is returned.
+ * If `floating` is `true`, or either `min` or `max` are floats, a floating-point
+ * number is returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Number
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating] Specify returning a floating-point number.
+ * @returns {number} Returns the random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max, floating) {
+ if (floating && isIterateeCall(min, max, floating)) {
+ max = floating = null;
+ }
+ var noMin = min == null,
+ noMax = max == null;
+
+ if (floating == null) {
+ if (noMax && typeof min == 'boolean') {
+ floating = min;
+ min = 1;
+ }
+ else if (typeof max == 'boolean') {
+ floating = max;
+ noMax = true;
+ }
+ }
+ if (noMin && noMax) {
+ max = 1;
+ noMax = false;
+ }
+ min = +min || 0;
+ if (noMax) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max);
+ }
+ return baseRandom(min, max);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Converts `string` to camel case.
+ * See [Wikipedia](https://en.wikipedia.org/wiki/CamelCase) for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the camel cased string.
+ * @example
+ *
+ * _.camelCase('Foo Bar');
+ * // => 'fooBar'
+ *
+ * _.camelCase('--foo-bar');
+ * // => 'fooBar'
+ *
+ * _.camelCase('__foo_bar__');
+ * // => 'fooBar'
+ */
+ var camelCase = createCompounder(function(result, word, index) {
+ word = word.toLowerCase();
+ return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word);
+ });
+
+ /**
+ * Capitalizes the first character of `string`.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to capitalize.
+ * @returns {string} Returns the capitalized string.
+ * @example
+ *
+ * _.capitalize('fred');
+ * // => 'Fred'
+ */
+ function capitalize(string) {
+ string = baseToString(string);
+ return string && (string.charAt(0).toUpperCase() + string.slice(1));
+ }
+
+ /**
+ * Deburrs `string` by converting latin-1 supplementary letters to basic latin letters.
+ * See [Wikipedia](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to deburr.
+ * @returns {string} Returns the deburred string.
+ * @example
+ *
+ * _.deburr('déjà vu');
+ * // => 'deja vu'
+ */
+ function deburr(string) {
+ string = baseToString(string);
+ return string && string.replace(reLatin1, deburrLetter);
+ }
+
+ /**
+ * Checks if `string` ends with the given target string.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to search.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=string.length] The position to search from.
+ * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`.
+ * @example
+ *
+ * _.endsWith('abc', 'c');
+ * // => true
+ *
+ * _.endsWith('abc', 'b');
+ * // => false
+ *
+ * _.endsWith('abc', 'b', 2);
+ * // => true
+ */
+ function endsWith(string, target, position) {
+ string = baseToString(string);
+ target = (target + '');
+
+ var length = string.length;
+ position = (typeof position == 'undefined' ? length : nativeMin(position < 0 ? 0 : (+position || 0), length)) - target.length;
+ return position >= 0 && string.indexOf(target, position) == position;
+ }
+
+ /**
+ * Converts the characters "&", "<", ">", '"', "'", and '`', in `string` to
+ * their corresponding HTML entities.
+ *
+ * **Note:** No other characters are escaped. To escape additional characters
+ * use a third-party library like [_he_](https://mths.be/he).
+ *
+ * Though the ">" character is escaped for symmetry, characters like
+ * ">" and "/" don't require escaping in HTML and have no special meaning
+ * unless they're part of a tag or unquoted attribute value.
+ * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
+ * (under "semi-related fun fact") for more details.
+ *
+ * Backticks are escaped because in Internet Explorer < 9, they can break out
+ * of attribute values or HTML comments. See [#102](https://html5sec.org/#102),
+ * [#108](https://html5sec.org/#108), and [#133](https://html5sec.org/#133) of
+ * the [HTML5 Security Cheatsheet](https://html5sec.org/) for more details.
+ *
+ * When working with HTML you should always quote attribute values to reduce
+ * XSS vectors. See [Ryan Grove's article](http://wonko.com/post/html-escaping)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles'
+ */
+ function escape(string) {
+ // Reset `lastIndex` because in IE < 9 `String#replace` does not.
+ string = baseToString(string);
+ return (string && reHasUnescapedHtml.test(string))
+ ? string.replace(reUnescapedHtml, escapeHtmlChar)
+ : string;
+ }
+
+ /**
+ * Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*",
+ * "+", "(", ")", "[", "]", "{" and "}" in `string`.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escapeRegExp('[lodash](https://lodash.com/)');
+ * // => '\[lodash\]\(https://lodash\.com/\)'
+ */
+ function escapeRegExp(string) {
+ string = baseToString(string);
+ return (string && reHasRegExpChars.test(string))
+ ? string.replace(reRegExpChars, '\\$&')
+ : string;
+ }
+
+ /**
+ * Converts `string` to kebab case (a.k.a. spinal case).
+ * See [Wikipedia](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles) for
+ * more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the kebab cased string.
+ * @example
+ *
+ * _.kebabCase('Foo Bar');
+ * // => 'foo-bar'
+ *
+ * _.kebabCase('fooBar');
+ * // => 'foo-bar'
+ *
+ * _.kebabCase('__foo_bar__');
+ * // => 'foo-bar'
+ */
+ var kebabCase = createCompounder(function(result, word, index) {
+ return result + (index ? '-' : '') + word.toLowerCase();
+ });
+
+ /**
+ * Pads `string` on the left and right sides if it is shorter then the given
+ * padding length. The `chars` string may be truncated if the number of padding
+ * characters can't be evenly divided by the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.pad('abc', 8);
+ * // => ' abc '
+ *
+ * _.pad('abc', 8, '_-');
+ * // => '_-abc_-_'
+ *
+ * _.pad('abc', 3);
+ * // => 'abc'
+ */
+ function pad(string, length, chars) {
+ string = baseToString(string);
+ length = +length;
+
+ var strLength = string.length;
+ if (strLength >= length || !nativeIsFinite(length)) {
+ return string;
+ }
+ var mid = (length - strLength) / 2,
+ leftLength = floor(mid),
+ rightLength = ceil(mid);
+
+ chars = createPad('', rightLength, chars);
+ return chars.slice(0, leftLength) + string + chars;
+ }
+
+ /**
+ * Pads `string` on the left side if it is shorter then the given padding
+ * length. The `chars` string may be truncated if the number of padding
+ * characters exceeds the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.padLeft('abc', 6);
+ * // => ' abc'
+ *
+ * _.padLeft('abc', 6, '_-');
+ * // => '_-_abc'
+ *
+ * _.padLeft('abc', 3);
+ * // => 'abc'
+ */
+ function padLeft(string, length, chars) {
+ string = baseToString(string);
+ return string && (createPad(string, length, chars) + string);
+ }
+
+ /**
+ * Pads `string` on the right side if it is shorter then the given padding
+ * length. The `chars` string may be truncated if the number of padding
+ * characters exceeds the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.padRight('abc', 6);
+ * // => 'abc '
+ *
+ * _.padRight('abc', 6, '_-');
+ * // => 'abc_-_'
+ *
+ * _.padRight('abc', 3);
+ * // => 'abc'
+ */
+ function padRight(string, length, chars) {
+ string = baseToString(string);
+ return string && (string + createPad(string, length, chars));
+ }
+
+ /**
+ * Converts `string` to an integer of the specified radix. If `radix` is
+ * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal,
+ * in which case a `radix` of `16` is used.
+ *
+ * **Note:** This method aligns with the ES5 implementation of `parseInt`.
+ * See the [ES5 spec](https://es5.github.io/#E) for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} string The string to convert.
+ * @param {number} [radix] The radix to interpret `value` by.
+ * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+ * @returns {number} Returns the converted integer.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ *
+ * _.map(['6', '08', '10'], _.parseInt);
+ * // => [6, 8, 10]
+ */
+ function parseInt(string, radix, guard) {
+ if (guard && isIterateeCall(string, radix, guard)) {
+ radix = 0;
+ }
+ return nativeParseInt(string, radix);
+ }
+ // Fallback for environments with pre-ES5 implementations.
+ if (nativeParseInt(whitespace + '08') != 8) {
+ parseInt = function(string, radix, guard) {
+ // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`.
+ // Chrome fails to trim leading whitespace characters.
+ // See https://code.google.com/p/v8/issues/detail?id=3109 for more details.
+ if (guard ? isIterateeCall(string, radix, guard) : radix == null) {
+ radix = 0;
+ } else if (radix) {
+ radix = +radix;
+ }
+ string = trim(string);
+ return nativeParseInt(string, radix || (reHexPrefix.test(string) ? 16 : 10));
+ };
+ }
+
+ /**
+ * Repeats the given string `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to repeat.
+ * @param {number} [n=0] The number of times to repeat the string.
+ * @returns {string} Returns the repeated string.
+ * @example
+ *
+ * _.repeat('*', 3);
+ * // => '***'
+ *
+ * _.repeat('abc', 2);
+ * // => 'abcabc'
+ *
+ * _.repeat('abc', 0);
+ * // => ''
+ */
+ function repeat(string, n) {
+ var result = '';
+ string = baseToString(string);
+ n = +n;
+ if (n < 1 || !string || !nativeIsFinite(n)) {
+ return result;
+ }
+ // Leverage the exponentiation by squaring algorithm for a faster repeat.
+ // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+ do {
+ if (n % 2) {
+ result += string;
+ }
+ n = floor(n / 2);
+ string += string;
+ } while (n);
+
+ return result;
+ }
+
+ /**
+ * Converts `string` to snake case.
+ * See [Wikipedia](https://en.wikipedia.org/wiki/Snake_case) for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the snake cased string.
+ * @example
+ *
+ * _.snakeCase('Foo Bar');
+ * // => 'foo_bar'
+ *
+ * _.snakeCase('fooBar');
+ * // => 'foo_bar'
+ *
+ * _.snakeCase('--foo-bar');
+ * // => 'foo_bar'
+ */
+ var snakeCase = createCompounder(function(result, word, index) {
+ return result + (index ? '_' : '') + word.toLowerCase();
+ });
+
+ /**
+ * Converts `string` to start case.
+ * See [Wikipedia](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the start cased string.
+ * @example
+ *
+ * _.startCase('--foo-bar');
+ * // => 'Foo Bar'
+ *
+ * _.startCase('fooBar');
+ * // => 'Foo Bar'
+ *
+ * _.startCase('__foo_bar__');
+ * // => 'Foo Bar'
+ */
+ var startCase = createCompounder(function(result, word, index) {
+ return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1));
+ });
+
+ /**
+ * Checks if `string` starts with the given target string.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to search.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=0] The position to search from.
+ * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`.
+ * @example
+ *
+ * _.startsWith('abc', 'a');
+ * // => true
+ *
+ * _.startsWith('abc', 'b');
+ * // => false
+ *
+ * _.startsWith('abc', 'b', 1);
+ * // => true
+ */
+ function startsWith(string, target, position) {
+ string = baseToString(string);
+ position = position == null ? 0 : nativeMin(position < 0 ? 0 : (+position || 0), string.length);
+ return string.lastIndexOf(target, position) == position;
+ }
+
+ /**
+ * Creates a compiled template function that can interpolate data properties
+ * in "interpolate" delimiters, HTML-escape interpolated data properties in
+ * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
+ * properties may be accessed as free variables in the template. If a setting
+ * object is provided it takes precedence over `_.templateSettings` values.
+ *
+ * **Note:** In the development build `_.template` utilizes sourceURLs for easier debugging.
+ * See the [HTML5 Rocks article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
+ * for more details.
+ *
+ * For more information on precompiling templates see
+ * [lodash's custom builds documentation](https://lodash.com/custom-builds).
+ *
+ * For more information on Chrome extension sandboxes see
+ * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The template string.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The HTML "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as free variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [options.sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [options.variable] The data object variable name.
+ * @param- {Object} [otherOptions] Enables the legacy `options` param signature.
+ * @returns {Function} Returns the compiled template function.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= user %>!');
+ * compiled({ 'user': 'fred' });
+ * // => 'hello fred!'
+ *
+ * // using the HTML "escape" delimiter to escape data property values
+ * var compiled = _.template('<%- value %>');
+ * compiled({ 'value': '