( function ( e ) { if ( "function" == typeof bootstrap ) bootstrap ( "nedb" , e ) ; else if ( "object" == typeof exports ) module . exports = e ( ) ; else if ( "function" == typeof define && define . amd ) define ( e ) ; else if ( "undefined" != typeof ses ) { if ( ! ses . ok ( ) ) return ; ses . makeNedb = e } else "undefined" != typeof window ? window . Nedb = e ( ) : global . Nedb = e ( ) } ) ( function ( ) { var define , ses , bootstrap , module , exports ;
return ( function ( e , t , n ) { function i ( n , s ) { if ( ! t [ n ] ) { if ( ! e [ n ] ) { var o = typeof require == "function" && require ; if ( ! s && o ) return o ( n , ! 0 ) ; if ( r ) return r ( n , ! 0 ) ; throw new Error ( "Cannot find module '" + n + "'" ) } var u = t [ n ] = { exports : { } } ; e [ n ] [ 0 ] . call ( u . exports , function ( t ) { var r = e [ n ] [ 1 ] [ t ] ; return i ( r ? r : t ) } , u , u . exports ) } return t [ n ] . exports } var r = typeof require == "function" && require ; for ( var s = 0 ; s < n . length ; s ++ ) i ( n [ s ] ) ; return i } ) ( { 1 : [ function ( require , module , exports ) {
var process = require ( "__browserify_process" ) ; if ( ! process . EventEmitter ) process . EventEmitter = function ( ) { } ;
var EventEmitter = exports . EventEmitter = process . EventEmitter ;
var isArray = typeof Array . isArray === 'function'
? Array . isArray
: function ( xs ) {
return Object . prototype . toString . call ( xs ) === '[object Array]'
}
;
function indexOf ( xs , x ) {
if ( xs . indexOf ) return xs . indexOf ( x ) ;
for ( var i = 0 ; i < xs . length ; i ++ ) {
if ( x === xs [ i ] ) return i ;
}
return - 1 ;
}
// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
var defaultMaxListeners = 10 ;
EventEmitter . prototype . setMaxListeners = function ( n ) {
if ( ! this . _events ) this . _events = { } ;
this . _events . maxListeners = n ;
} ;
EventEmitter . prototype . emit = function ( type ) {
// If there is no 'error' event listener then throw.
if ( type === 'error' ) {
if ( ! this . _events || ! this . _events . error ||
( isArray ( this . _events . error ) && ! this . _events . error . length ) )
{
if ( arguments [ 1 ] instanceof Error ) {
throw arguments [ 1 ] ; // Unhandled 'error' event
} else {
throw new Error ( "Uncaught, unspecified 'error' event." ) ;
}
return false ;
}
}
if ( ! this . _events ) return false ;
var handler = this . _events [ type ] ;
if ( ! handler ) return false ;
if ( typeof handler == 'function' ) {
switch ( arguments . length ) {
// fast cases
case 1 :
handler . call ( this ) ;
break ;
case 2 :
handler . call ( this , arguments [ 1 ] ) ;
break ;
case 3 :
handler . call ( this , arguments [ 1 ] , arguments [ 2 ] ) ;
break ;
// slower
default :
var args = Array . prototype . slice . call ( arguments , 1 ) ;
handler . apply ( this , args ) ;
}
return true ;
} else if ( isArray ( handler ) ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
var listeners = handler . slice ( ) ;
for ( var i = 0 , l = listeners . length ; i < l ; i ++ ) {
listeners [ i ] . apply ( this , args ) ;
}
return true ;
} else {
return false ;
}
} ;
// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter . prototype . addListener = function ( type , listener ) {
if ( 'function' !== typeof listener ) {
throw new Error ( 'addListener only takes instances of Function' ) ;
}
if ( ! this . _events ) this . _events = { } ;
// To avoid recursion in the case that type == "newListeners"! Before
// adding it to the listeners, first emit "newListeners".
this . emit ( 'newListener' , type , listener ) ;
if ( ! this . _events [ type ] ) {
// Optimize the case of one listener. Don't need the extra array object.
this . _events [ type ] = listener ;
} else if ( isArray ( this . _events [ type ] ) ) {
// Check for listener leak
if ( ! this . _events [ type ] . warned ) {
var m ;
if ( this . _events . maxListeners !== undefined ) {
m = this . _events . maxListeners ;
} else {
m = defaultMaxListeners ;
}
if ( m && m > 0 && this . _events [ type ] . length > m ) {
this . _events [ type ] . warned = true ;
console . error ( '(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.' ,
this . _events [ type ] . length ) ;
console . trace ( ) ;
}
}
// If we've already got an array, just append.
this . _events [ type ] . push ( listener ) ;
} else {
// Adding the second element, need to change to array.
this . _events [ type ] = [ this . _events [ type ] , listener ] ;
}
return this ;
} ;
EventEmitter . prototype . on = EventEmitter . prototype . addListener ;
EventEmitter . prototype . once = function ( type , listener ) {
var self = this ;
self . on ( type , function g ( ) {
self . removeListener ( type , g ) ;
listener . apply ( this , arguments ) ;
} ) ;
return this ;
} ;
EventEmitter . prototype . removeListener = function ( type , listener ) {
if ( 'function' !== typeof listener ) {
throw new Error ( 'removeListener only takes instances of Function' ) ;
}
// does not use listeners(), so no side effect of creating _events[type]
if ( ! this . _events || ! this . _events [ type ] ) return this ;
var list = this . _events [ type ] ;
if ( isArray ( list ) ) {
var i = indexOf ( list , listener ) ;
if ( i < 0 ) return this ;
list . splice ( i , 1 ) ;
if ( list . length == 0 )
delete this . _events [ type ] ;
} else if ( this . _events [ type ] === listener ) {
delete this . _events [ type ] ;
}
return this ;
} ;
EventEmitter . prototype . removeAllListeners = function ( type ) {
if ( arguments . length === 0 ) {
this . _events = { } ;
return this ;
}
// does not use listeners(), so no side effect of creating _events[type]
if ( type && this . _events && this . _events [ type ] ) this . _events [ type ] = null ;
return this ;
} ;
EventEmitter . prototype . listeners = function ( type ) {
if ( ! this . _events ) this . _events = { } ;
if ( ! this . _events [ type ] ) this . _events [ type ] = [ ] ;
if ( ! isArray ( this . _events [ type ] ) ) {
this . _events [ type ] = [ this . _events [ type ] ] ;
}
return this . _events [ type ] ;
} ;
EventEmitter . listenerCount = function ( emitter , type ) {
var ret ;
if ( ! emitter . _events || ! emitter . _events [ type ] )
ret = 0 ;
else if ( typeof emitter . _events [ type ] === 'function' )
ret = 1 ;
else
ret = emitter . _events [ type ] . length ;
return ret ;
} ;
} , { "__browserify_process" : 3 } ] , 2 : [ function ( require , module , exports ) {
var events = require ( 'events' ) ;
exports . isArray = isArray ;
exports . isDate = function ( obj ) { return Object . prototype . toString . call ( obj ) === '[object Date]' } ;
exports . isRegExp = function ( obj ) { return Object . prototype . toString . call ( obj ) === '[object RegExp]' } ;
exports . print = function ( ) { } ;
exports . puts = function ( ) { } ;
exports . debug = function ( ) { } ;
exports . inspect = function ( obj , showHidden , depth , colors ) {
var seen = [ ] ;
var stylize = function ( str , styleType ) {
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
var styles =
{ 'bold' : [ 1 , 22 ] ,
'italic' : [ 3 , 23 ] ,
'underline' : [ 4 , 24 ] ,
'inverse' : [ 7 , 27 ] ,
'white' : [ 37 , 39 ] ,
'grey' : [ 90 , 39 ] ,
'black' : [ 30 , 39 ] ,
'blue' : [ 34 , 39 ] ,
'cyan' : [ 36 , 39 ] ,
'green' : [ 32 , 39 ] ,
'magenta' : [ 35 , 39 ] ,
'red' : [ 31 , 39 ] ,
'yellow' : [ 33 , 39 ] } ;
var style =
{ 'special' : 'cyan' ,
'number' : 'blue' ,
'boolean' : 'yellow' ,
'undefined' : 'grey' ,
'null' : 'bold' ,
'string' : 'green' ,
'date' : 'magenta' ,
// "name": intentionally not styling
'regexp' : 'red' } [ styleType ] ;
if ( style ) {
return '\u001b[' + styles [ style ] [ 0 ] + 'm' + str +
'\u001b[' + styles [ style ] [ 1 ] + 'm' ;
} else {
return str ;
}
} ;
if ( ! colors ) {
stylize = function ( str , styleType ) { return str ; } ;
}
function format ( value , recurseTimes ) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
if ( value && typeof value . inspect === 'function' &&
// Filter out the util module, it's inspect function is special
value !== exports &&
// Also filter out any prototype objects using the circular check.
! ( value . constructor && value . constructor . prototype === value ) ) {
return value . inspect ( recurseTimes ) ;
}
// Primitive types cannot have properties
switch ( typeof value ) {
case 'undefined' :
return stylize ( 'undefined' , 'undefined' ) ;
case 'string' :
var simple = '\'' + JSON . stringify ( value ) . replace ( /^"|"$/g , '' )
. replace ( /'/g , "\\'" )
. replace ( /\\"/g , '"' ) + '\'' ;
return stylize ( simple , 'string' ) ;
case 'number' :
return stylize ( '' + value , 'number' ) ;
case 'boolean' :
return stylize ( '' + value , 'boolean' ) ;
}
// For some reason typeof null is "object", so special case here.
if ( value === null ) {
return stylize ( 'null' , 'null' ) ;
}
// Look up the keys of the object.
var visible _keys = Object _keys ( value ) ;
var keys = showHidden ? Object _getOwnPropertyNames ( value ) : visible _keys ;
// Functions without properties can be shortcutted.
if ( typeof value === 'function' && keys . length === 0 ) {
if ( isRegExp ( value ) ) {
return stylize ( '' + value , 'regexp' ) ;
} else {
var name = value . name ? ': ' + value . name : '' ;
return stylize ( '[Function' + name + ']' , 'special' ) ;
}
}
// Dates without properties can be shortcutted
if ( isDate ( value ) && keys . length === 0 ) {
return stylize ( value . toUTCString ( ) , 'date' ) ;
}
var base , type , braces ;
// Determine the object type
if ( isArray ( value ) ) {
type = 'Array' ;
braces = [ '[' , ']' ] ;
} else {
type = 'Object' ;
braces = [ '{' , '}' ] ;
}
// Make functions say that they are functions
if ( typeof value === 'function' ) {
var n = value . name ? ': ' + value . name : '' ;
base = ( isRegExp ( value ) ) ? ' ' + value : ' [Function' + n + ']' ;
} else {
base = '' ;
}
// Make dates with properties first say the date
if ( isDate ( value ) ) {
base = ' ' + value . toUTCString ( ) ;
}
if ( keys . length === 0 ) {
return braces [ 0 ] + base + braces [ 1 ] ;
}
if ( recurseTimes < 0 ) {
if ( isRegExp ( value ) ) {
return stylize ( '' + value , 'regexp' ) ;
} else {
return stylize ( '[Object]' , 'special' ) ;
}
}
seen . push ( value ) ;
var output = keys . map ( function ( key ) {
var name , str ;
if ( value . _ _lookupGetter _ _ ) {
if ( value . _ _lookupGetter _ _ ( key ) ) {
if ( value . _ _lookupSetter _ _ ( key ) ) {
str = stylize ( '[Getter/Setter]' , 'special' ) ;
} else {
str = stylize ( '[Getter]' , 'special' ) ;
}
} else {
if ( value . _ _lookupSetter _ _ ( key ) ) {
str = stylize ( '[Setter]' , 'special' ) ;
}
}
}
if ( visible _keys . indexOf ( key ) < 0 ) {
name = '[' + key + ']' ;
}
if ( ! str ) {
if ( seen . indexOf ( value [ key ] ) < 0 ) {
if ( recurseTimes === null ) {
str = format ( value [ key ] ) ;
} else {
str = format ( value [ key ] , recurseTimes - 1 ) ;
}
if ( str . indexOf ( '\n' ) > - 1 ) {
if ( isArray ( value ) ) {
str = str . split ( '\n' ) . map ( function ( line ) {
return ' ' + line ;
} ) . join ( '\n' ) . substr ( 2 ) ;
} else {
str = '\n' + str . split ( '\n' ) . map ( function ( line ) {
return ' ' + line ;
} ) . join ( '\n' ) ;
}
}
} else {
str = stylize ( '[Circular]' , 'special' ) ;
}
}
if ( typeof name === 'undefined' ) {
if ( type === 'Array' && key . match ( /^\d+$/ ) ) {
return str ;
}
name = JSON . stringify ( '' + key ) ;
if ( name . match ( /^"([a-zA-Z_][a-zA-Z_0-9]*)"$/ ) ) {
name = name . substr ( 1 , name . length - 2 ) ;
name = stylize ( name , 'name' ) ;
} else {
name = name . replace ( /'/g , "\\'" )
. replace ( /\\"/g , '"' )
. replace ( /(^"|"$)/g , "'" ) ;
name = stylize ( name , 'string' ) ;
}
}
return name + ': ' + str ;
} ) ;
seen . pop ( ) ;
var numLinesEst = 0 ;
var length = output . reduce ( function ( prev , cur ) {
numLinesEst ++ ;
if ( cur . indexOf ( '\n' ) >= 0 ) numLinesEst ++ ;
return prev + cur . length + 1 ;
} , 0 ) ;
if ( length > 50 ) {
output = braces [ 0 ] +
( base === '' ? '' : base + '\n ' ) +
' ' +
output . join ( ',\n ' ) +
' ' +
braces [ 1 ] ;
} else {
output = braces [ 0 ] + base + ' ' + output . join ( ', ' ) + ' ' + braces [ 1 ] ;
}
return output ;
}
return format ( obj , ( typeof depth === 'undefined' ? 2 : depth ) ) ;
} ;
function isArray ( ar ) {
return Array . isArray ( ar ) ||
( typeof ar === 'object' && Object . prototype . toString . call ( ar ) === '[object Array]' ) ;
}
function isRegExp ( re ) {
typeof re === 'object' && Object . prototype . toString . call ( re ) === '[object RegExp]' ;
}
function isDate ( d ) {
return typeof d === 'object' && Object . prototype . toString . call ( d ) === '[object Date]' ;
}
function pad ( n ) {
return n < 10 ? '0' + n . toString ( 10 ) : n . toString ( 10 ) ;
}
var months = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' ,
'Oct' , 'Nov' , 'Dec' ] ;
// 26 Feb 16:19:34
function timestamp ( ) {
var d = new Date ( ) ;
var time = [ pad ( d . getHours ( ) ) ,
pad ( d . getMinutes ( ) ) ,
pad ( d . getSeconds ( ) ) ] . join ( ':' ) ;
return [ d . getDate ( ) , months [ d . getMonth ( ) ] , time ] . join ( ' ' ) ;
}
exports . log = function ( msg ) { } ;
exports . pump = null ;
var Object _keys = Object . keys || function ( obj ) {
var res = [ ] ;
for ( var key in obj ) res . push ( key ) ;
return res ;
} ;
var Object _getOwnPropertyNames = Object . getOwnPropertyNames || function ( obj ) {
var res = [ ] ;
for ( var key in obj ) {
if ( Object . hasOwnProperty . call ( obj , key ) ) res . push ( key ) ;
}
return res ;
} ;
var Object _create = Object . create || function ( prototype , properties ) {
// from es5-shim
var object ;
if ( prototype === null ) {
object = { '__proto__' : null } ;
}
else {
if ( typeof prototype !== 'object' ) {
throw new TypeError (
'typeof prototype[' + ( typeof prototype ) + '] != \'object\''
) ;
}
var Type = function ( ) { } ;
Type . prototype = prototype ;
object = new Type ( ) ;
object . _ _proto _ _ = prototype ;
}
if ( typeof properties !== 'undefined' && Object . defineProperties ) {
Object . defineProperties ( object , properties ) ;
}
return object ;
} ;
exports . inherits = function ( ctor , superCtor ) {
ctor . super _ = superCtor ;
ctor . prototype = Object _create ( superCtor . prototype , {
constructor : {
value : ctor ,
enumerable : false ,
writable : true ,
configurable : true
}
} ) ;
} ;
var formatRegExp = /%[sdj%]/g ;
exports . format = function ( f ) {
if ( typeof f !== 'string' ) {
var objects = [ ] ;
for ( var i = 0 ; i < arguments . length ; i ++ ) {
objects . push ( exports . inspect ( arguments [ i ] ) ) ;
}
return objects . join ( ' ' ) ;
}
var i = 1 ;
var args = arguments ;
var len = args . length ;
var str = String ( f ) . replace ( formatRegExp , function ( x ) {
if ( x === '%%' ) return '%' ;
if ( i >= len ) return x ;
switch ( x ) {
case '%s' : return String ( args [ i ++ ] ) ;
case '%d' : return Number ( args [ i ++ ] ) ;
case '%j' : return JSON . stringify ( args [ i ++ ] ) ;
default :
return x ;
}
} ) ;
for ( var x = args [ i ] ; i < len ; x = args [ ++ i ] ) {
if ( x === null || typeof x !== 'object' ) {
str += ' ' + x ;
} else {
str += ' ' + exports . inspect ( x ) ;
}
}
return str ;
} ;
} , { "events" : 1 } ] , 3 : [ function ( require , module , exports ) {
// shim for using process in browser
var process = module . exports = { } ;
process . nextTick = ( function ( ) {
var canSetImmediate = typeof window !== 'undefined'
&& window . setImmediate ;
var canPost = typeof window !== 'undefined'
&& window . postMessage && window . addEventListener
;
if ( canSetImmediate ) {
return function ( f ) { return window . setImmediate ( f ) } ;
}
if ( canPost ) {
var queue = [ ] ;
window . addEventListener ( 'message' , function ( ev ) {
if ( ev . source === window && ev . data === 'process-tick' ) {
ev . stopPropagation ( ) ;
if ( queue . length > 0 ) {
var fn = queue . shift ( ) ;
fn ( ) ;
}
}
} , true ) ;
return function nextTick ( fn ) {
queue . push ( fn ) ;
window . postMessage ( 'process-tick' , '*' ) ;
} ;
}
return function nextTick ( fn ) {
setTimeout ( fn , 0 ) ;
} ;
} ) ( ) ;
process . title = 'browser' ;
process . browser = true ;
process . env = { } ;
process . argv = [ ] ;
process . binding = function ( name ) {
throw new Error ( 'process.binding is not supported' ) ;
}
// TODO(shtylman)
process . cwd = function ( ) { return '/' } ;
process . chdir = function ( dir ) {
throw new Error ( 'process.chdir is not supported' ) ;
} ;
} , { } ] , 4 : [ function ( require , module , exports ) {
/ * *
* Manage access to data , be it to find , update or remove it
* /
var model = require ( './model' )
, _ = require ( 'underscore' )
;
/ * *
* Create a new cursor for this collection
* @ param { Datastore } db - The datastore this cursor is bound to
* @ param { Query } query - The query this cursor will operate on
* @ param { Function } execDn - Handler to be executed after cursor has found the results and before the callback passed to find / findOne / update / remove
* /
function Cursor ( db , query , execFn ) {
this . db = db ;
this . query = query || { } ;
if ( execFn ) { this . execFn = execFn ; }
}
/ * *
* Set a limit to the number of results
* /
Cursor . prototype . limit = function ( limit ) {
this . _limit = limit ;
return this ;
} ;
/ * *
* Skip a the number of results
* /
Cursor . prototype . skip = function ( skip ) {
this . _skip = skip ;
return this ;
} ;
/ * *
* Sort results of the query
* @ param { SortQuery } sortQuery - SortQuery is { field : order } , field can use the dot - notation , order is 1 for ascending and - 1 for descending
* /
Cursor . prototype . sort = function ( sortQuery ) {
this . _sort = sortQuery ;
return this ;
} ;
/ * *
* Add the use of a projection
* @ param { Object } projection - MongoDB - style projection . { } means take all fields . Then it ' s { key1 : 1 , key2 : 1 } to take only key1 and key2
* { key1 : 0 , key2 : 0 } to omit only key1 and key2 . Except _id , you can ' t mix takes and omits
* /
Cursor . prototype . projection = function ( projection ) {
this . _projection = projection ;
return this ;
} ;
/ * *
* Apply the projection
* /
Cursor . prototype . project = function ( candidates ) {
var res = [ ] , self = this
, keepId , action , keys
;
if ( this . _projection === undefined || Object . keys ( this . _projection ) . length === 0 ) {
return candidates ;
}
keepId = this . _projection . _id === 0 ? false : true ;
this . _projection = _ . omit ( this . _projection , '_id' ) ;
// Check for consistency
keys = Object . keys ( this . _projection ) ;
keys . forEach ( function ( k ) {
if ( action !== undefined && self . _projection [ k ] !== action ) { throw "Can't both keep and omit fields except for _id" ; }
action = self . _projection [ k ] ;
} ) ;
// Do the actual projection
candidates . forEach ( function ( candidate ) {
var toPush = action === 1 ? _ . pick ( candidate , keys ) : _ . omit ( candidate , keys ) ;
if ( keepId ) {
toPush . _id = candidate . _id ;
} else {
delete toPush . _id ;
}
res . push ( toPush ) ;
} ) ;
return res ;
} ;
/ * *
* Get all matching elements
* Will return pointers to matched elements ( shallow copies ) , returning full copies is the role of find or findOne
* This is an internal function , use exec which uses the executor
*
* @ param { Function } callback - Signature : err , results
* /
Cursor . prototype . _exec = function ( callback ) {
var candidates = this . db . getCandidates ( this . query )
, res = [ ] , added = 0 , skipped = 0 , self = this
, error = null
, i , keys , key
;
try {
for ( i = 0 ; i < candidates . length ; i += 1 ) {
if ( model . match ( candidates [ i ] , this . query ) ) {
// If a sort is defined, wait for the results to be sorted before applying limit and skip
if ( ! this . _sort ) {
if ( this . _skip && this . _skip > skipped ) {
skipped += 1 ;
} else {
res . push ( candidates [ i ] ) ;
added += 1 ;
if ( this . _limit && this . _limit <= added ) { break ; }
}
} else {
res . push ( candidates [ i ] ) ;
}
}
}
} catch ( err ) {
return callback ( err ) ;
}
// Apply all sorts
if ( this . _sort ) {
keys = Object . keys ( this . _sort ) ;
// Sorting
var criteria = [ ] ;
for ( i = 0 ; i < keys . length ; i ++ ) {
key = keys [ i ] ;
criteria . push ( { key : key , direction : self . _sort [ key ] } ) ;
}
res . sort ( function ( a , b ) {
var criterion , compare , i ;
for ( i = 0 ; i < criteria . length ; i ++ ) {
criterion = criteria [ i ] ;
compare = criterion . direction * model . compareThings ( model . getDotValue ( a , criterion . key ) , model . getDotValue ( b , criterion . key ) ) ;
if ( compare !== 0 ) {
return compare ;
}
}
return 0 ;
} ) ;
// Applying limit and skip
var limit = this . _limit || res . length
, skip = this . _skip || 0 ;
res = res . slice ( skip , skip + limit ) ;
}
// Apply projection
try {
res = this . project ( res ) ;
} catch ( e ) {
error = e ;
res = undefined ;
}
if ( this . execFn ) {
return this . execFn ( error , res , callback ) ;
} else {
return callback ( error , res ) ;
}
} ;
Cursor . prototype . exec = function ( ) {
this . db . executor . push ( { this : this , fn : this . _exec , arguments : arguments } ) ;
} ;
// Interface
module . exports = Cursor ;
} , { "./model" : 9 , "underscore" : 16 } ] , 5 : [ function ( require , module , exports ) {
/ * *
* Specific customUtils for the browser , where we don ' t have access to the Crypto and Buffer modules
* /
/ * *
* Taken from the crypto - browserify module
* https : //github.com/dominictarr/crypto-browserify
* NOTE : Math . random ( ) does not guarantee "cryptographic quality" but we actually don ' t need it
* /
function randomBytes ( size ) {
var bytes = new Array ( size ) ;
var r ;
for ( var i = 0 , r ; i < size ; i ++ ) {
if ( ( i & 0x03 ) == 0 ) r = Math . random ( ) * 0x100000000 ;
bytes [ i ] = r >>> ( ( i & 0x03 ) << 3 ) & 0xff ;
}
return bytes ;
}
/ * *
* Taken from the base64 - js module
* https : //github.com/beatgammit/base64-js/
* /
function byteArrayToBase64 ( uint8 ) {
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
, extraBytes = uint8 . length % 3 // if we have 1 byte left, pad 2 bytes
, output = ""
, temp , length , i ;
function tripletToBase64 ( num ) {
return lookup [ num >> 18 & 0x3F ] + lookup [ num >> 12 & 0x3F ] + lookup [ num >> 6 & 0x3F ] + lookup [ 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 += lookup [ temp >> 2 ] ;
output += lookup [ ( temp << 4 ) & 0x3F ] ;
output += '==' ;
break ;
case 2 :
temp = ( uint8 [ uint8 . length - 2 ] << 8 ) + ( uint8 [ uint8 . length - 1 ] ) ;
output += lookup [ temp >> 10 ] ;
output += lookup [ ( temp >> 4 ) & 0x3F ] ;
output += lookup [ ( temp << 2 ) & 0x3F ] ;
output += '=' ;
break ;
}
return output ;
}
/ * *
* Return a random alphanumerical string of length len
* There is a very small probability ( less than 1 / 1 , 000 , 000 ) for the length to be less than len
* ( il the base64 conversion yields too many pluses and slashes ) but
* that ' s not an issue here
* The probability of a collision is extremely small ( need 3 * 10 ^ 12 documents to have one chance in a million of a collision )
* See http : //en.wikipedia.org/wiki/Birthday_problem
* /
function uid ( len ) {
return byteArrayToBase64 ( randomBytes ( Math . ceil ( Math . max ( 8 , len * 2 ) ) ) ) . replace ( /[+\/]/g , '' ) . slice ( 0 , len ) ;
}
module . exports . uid = uid ;
} , { } ] , 6 : [ function ( require , module , exports ) {
var customUtils = require ( './customUtils' )
, model = require ( './model' )
, async = require ( 'async' )
, Executor = require ( './executor' )
, Index = require ( './indexes' )
, util = require ( 'util' )
, _ = require ( 'underscore' )
, Persistence = require ( './persistence' )
, Cursor = require ( './cursor' )
;
/ * *
* Create a new collection
* @ param { String } options . filename Optional , datastore will be in - memory only if not provided
* @ param { Boolean } options . inMemoryOnly Optional , default to false
* @ param { Boolean } options . nodeWebkitAppName Optional , specify the name of your NW app if you want options . filename to be relative to the directory where
* Node Webkit stores application data such as cookies and local storage ( the best place to store data in my opinion )
* @ param { Boolean } options . autoload Optional , defaults to false
* @ param { Function } options . onload Optional , if autoload is used this will be called after the load database with the error object as parameter . If you don ' t pass it the error will be thrown
* /
function Datastore ( options ) {
var filename ;
// Retrocompatibility with v0.6 and before
if ( typeof options === 'string' ) {
filename = options ;
this . inMemoryOnly = false ; // Default
} else {
options = options || { } ;
filename = options . filename ;
this . inMemoryOnly = options . inMemoryOnly || false ;
this . autoload = options . autoload || false ;
}
// Determine whether in memory or persistent
if ( ! filename || typeof filename !== 'string' || filename . length === 0 ) {
this . filename = null ;
this . inMemoryOnly = true ;
} else {
this . filename = filename ;
}
// Persistence handling
this . persistence = new Persistence ( { db : this , nodeWebkitAppName : options . nodeWebkitAppName } ) ;
// This new executor is ready if we don't use persistence
// If we do, it will only be ready once loadDatabase is called
this . executor = new Executor ( ) ;
if ( this . inMemoryOnly ) { this . executor . ready = true ; }
// Indexed by field name, dot notation can be used
// _id is always indexed and since _ids are generated randomly the underlying
// binary is always well-balanced
this . indexes = { } ;
this . indexes . _id = new Index ( { fieldName : '_id' , unique : true } ) ;
// Queue a load of the database right away and call the onload handler
// By default (no onload handler), if there is an error there, no operation will be possible so warn the user by throwing an exception
if ( this . autoload ) { this . loadDatabase ( options . onload || function ( err ) {
if ( err ) { throw err ; }
} ) ; }
}
/ * *
* Load the database from the datafile , and trigger the execution of buffered commands if any
* /
Datastore . prototype . loadDatabase = function ( ) {
this . executor . push ( { this : this . persistence , fn : this . persistence . loadDatabase , arguments : arguments } , true ) ;
} ;
/ * *
* Get an array of all the data in the database
* /
Datastore . prototype . getAllData = function ( ) {
return this . indexes . _id . getAll ( ) ;
} ;
/ * *
* Reset all currently defined indexes
* /
Datastore . prototype . resetIndexes = function ( newData ) {
var self = this ;
Object . keys ( this . indexes ) . forEach ( function ( i ) {
self . indexes [ i ] . reset ( newData ) ;
} ) ;
} ;
/ * *
* Ensure an index is kept for this field . Same parameters as lib / indexes
* For now this function is synchronous , we need to test how much time it takes
* We use an async API for consistency with the rest of the code
* @ param { String } options . fieldName
* @ param { Boolean } options . unique
* @ param { Boolean } options . sparse
* @ param { Function } cb Optional callback , signature : err
* /
Datastore . prototype . ensureIndex = function ( options , cb ) {
var callback = cb || function ( ) { } ;
options = options || { } ;
if ( ! options . fieldName ) { return callback ( { missingFieldName : true } ) ; }
if ( this . indexes [ options . fieldName ] ) { return callback ( null ) ; }
this . indexes [ options . fieldName ] = new Index ( options ) ;
try {
this . indexes [ options . fieldName ] . insert ( this . getAllData ( ) ) ;
} catch ( e ) {
delete this . indexes [ options . fieldName ] ;
return callback ( e ) ;
}
this . persistence . persistNewState ( [ { $$indexCreated : options } ] , function ( err ) {
if ( err ) { return callback ( err ) ; }
return callback ( null ) ;
} ) ;
} ;
/ * *
* Remove an index
* @ param { String } fieldName
* @ param { Function } cb Optional callback , signature : err
* /
Datastore . prototype . removeIndex = function ( fieldName , cb ) {
var callback = cb || function ( ) { } ;
delete this . indexes [ fieldName ] ;
this . persistence . persistNewState ( [ { $$indexRemoved : fieldName } ] , function ( err ) {
if ( err ) { return callback ( err ) ; }
return callback ( null ) ;
} ) ;
} ;
/ * *
* Add one or several document ( s ) to all indexes
* /
Datastore . prototype . addToIndexes = function ( doc ) {
var i , failingIndex , error
, keys = Object . keys ( this . indexes )
;
for ( i = 0 ; i < keys . length ; i += 1 ) {
try {
this . indexes [ keys [ i ] ] . insert ( doc ) ;
} catch ( e ) {
failingIndex = i ;
error = e ;
break ;
}
}
// If an error happened, we need to rollback the insert on all other indexes
if ( error ) {
for ( i = 0 ; i < failingIndex ; i += 1 ) {
this . indexes [ keys [ i ] ] . remove ( doc ) ;
}
throw error ;
}
} ;
/ * *
* Remove one or several document ( s ) from all indexes
* /
Datastore . prototype . removeFromIndexes = function ( doc ) {
var self = this ;
Object . keys ( this . indexes ) . forEach ( function ( i ) {
self . indexes [ i ] . remove ( doc ) ;
} ) ;
} ;
/ * *
* Update one or several documents in all indexes
* To update multiple documents , oldDoc must be an array of { oldDoc , newDoc } pairs
* If one update violates a constraint , all changes are rolled back
* /
Datastore . prototype . updateIndexes = function ( oldDoc , newDoc ) {
var i , failingIndex , error
, keys = Object . keys ( this . indexes )
;
for ( i = 0 ; i < keys . length ; i += 1 ) {
try {
this . indexes [ keys [ i ] ] . update ( oldDoc , newDoc ) ;
} catch ( e ) {
failingIndex = i ;
error = e ;
break ;
}
}
// If an error happened, we need to rollback the update on all other indexes
if ( error ) {
for ( i = 0 ; i < failingIndex ; i += 1 ) {
this . indexes [ keys [ i ] ] . revertUpdate ( oldDoc , newDoc ) ;
}
throw error ;
}
} ;
/ * *
* Return the list of candidates for a given query
* Crude implementation for now , we return the candidates given by the first usable index if any
* We try the following query types , in this order : basic match , $in match , comparison match
* One way to make it better would be to enable the use of multiple indexes if the first usable index
* returns too much data . I may do it in the future .
*
* TODO : needs to be moved to the Cursor module
* /
Datastore . prototype . getCandidates = function ( query ) {
var indexNames = Object . keys ( this . indexes )
, usableQueryKeys ;
// For a basic match
usableQueryKeys = [ ] ;
Object . keys ( query ) . forEach ( function ( k ) {
if ( typeof query [ k ] === 'string' || typeof query [ k ] === 'number' || typeof query [ k ] === 'boolean' || util . isDate ( query [ k ] ) || query [ k ] === null ) {
usableQueryKeys . push ( k ) ;
}
} ) ;
usableQueryKeys = _ . intersection ( usableQueryKeys , indexNames ) ;
if ( usableQueryKeys . length > 0 ) {
return this . indexes [ usableQueryKeys [ 0 ] ] . getMatching ( query [ usableQueryKeys [ 0 ] ] ) ;
}
// For a $in match
usableQueryKeys = [ ] ;
Object . keys ( query ) . forEach ( function ( k ) {
if ( query [ k ] && query [ k ] . hasOwnProperty ( '$in' ) ) {
usableQueryKeys . push ( k ) ;
}
} ) ;
usableQueryKeys = _ . intersection ( usableQueryKeys , indexNames ) ;
if ( usableQueryKeys . length > 0 ) {
return this . indexes [ usableQueryKeys [ 0 ] ] . getMatching ( query [ usableQueryKeys [ 0 ] ] . $in ) ;
}
// For a comparison match
usableQueryKeys = [ ] ;
Object . keys ( query ) . forEach ( function ( k ) {
if ( query [ k ] && ( query [ k ] . hasOwnProperty ( '$lt' ) || query [ k ] . hasOwnProperty ( '$lte' ) || query [ k ] . hasOwnProperty ( '$gt' ) || query [ k ] . hasOwnProperty ( '$gte' ) ) ) {
usableQueryKeys . push ( k ) ;
}
} ) ;
usableQueryKeys = _ . intersection ( usableQueryKeys , indexNames ) ;
if ( usableQueryKeys . length > 0 ) {
return this . indexes [ usableQueryKeys [ 0 ] ] . getBetweenBounds ( query [ usableQueryKeys [ 0 ] ] ) ;
}
// By default, return all the DB data
return this . getAllData ( ) ;
} ;
/ * *
* Insert a new document
* @ param { Function } cb Optional callback , signature : err , insertedDoc
*
* @ api private Use Datastore . insert which has the same signature
* /
Datastore . prototype . _insert = function ( newDoc , cb ) {
var callback = cb || function ( ) { }
;
try {
this . _insertInCache ( newDoc ) ;
} catch ( e ) {
return callback ( e ) ;
}
this . persistence . persistNewState ( util . isArray ( newDoc ) ? newDoc : [ newDoc ] , function ( err ) {
if ( err ) { return callback ( err ) ; }
return callback ( null , newDoc ) ;
} ) ;
} ;
/ * *
* Create a new _id that ' s not already in use
* /
Datastore . prototype . createNewId = function ( ) {
var tentativeId = customUtils . uid ( 16 ) ;
// Try as many times as needed to get an unused _id. As explained in customUtils, the probability of this ever happening is extremely small, so this is O(1)
if ( this . indexes . _id . getMatching ( tentativeId ) . length > 0 ) {
tentativeId = this . createNewId ( ) ;
}
return tentativeId ;
} ;
/ * *
* Prepare a document ( or array of documents ) to be inserted in a database
* @ api private
* /
Datastore . prototype . prepareDocumentForInsertion = function ( newDoc ) {
var preparedDoc , self = this ;
if ( util . isArray ( newDoc ) ) {
preparedDoc = [ ] ;
newDoc . forEach ( function ( doc ) { preparedDoc . push ( self . prepareDocumentForInsertion ( doc ) ) ; } ) ;
} else {
newDoc . _id = newDoc . _id || this . createNewId ( ) ;
preparedDoc = model . deepCopy ( newDoc ) ;
model . checkObject ( preparedDoc ) ;
}
return preparedDoc ;
} ;
/ * *
* If newDoc is an array of documents , this will insert all documents in the cache
* @ api private
* /
Datastore . prototype . _insertInCache = function ( newDoc ) {
if ( util . isArray ( newDoc ) ) {
this . _insertMultipleDocsInCache ( newDoc ) ;
} else {
this . addToIndexes ( this . prepareDocumentForInsertion ( newDoc ) ) ;
}
} ;
/ * *
* If one insertion fails ( e . g . because of a unique constraint ) , roll back all previous
* inserts and throws the error
* @ api private
* /
Datastore . prototype . _insertMultipleDocsInCache = function ( newDocs ) {
var i , failingI , error
, preparedDocs = this . prepareDocumentForInsertion ( newDocs )
;
for ( i = 0 ; i < preparedDocs . length ; i += 1 ) {
try {
this . addToIndexes ( preparedDocs [ i ] ) ;
} catch ( e ) {
error = e ;
failingI = i ;
break ;
}
}
if ( error ) {
for ( i = 0 ; i < failingI ; i += 1 ) {
this . removeFromIndexes ( preparedDocs [ i ] ) ;
}
throw error ;
}
} ;
Datastore . prototype . insert = function ( ) {
this . executor . push ( { this : this , fn : this . _insert , arguments : arguments } ) ;
} ;
/ * *
* Count all documents matching the query
* @ param { Object } query MongoDB - style query
* /
Datastore . prototype . count = function ( query , callback ) {
var cursor = new Cursor ( this , query , function ( err , docs , callback ) {
if ( err ) { return callback ( err ) ; }
return callback ( null , docs . length ) ;
} ) ;
if ( typeof callback === 'function' ) {
cursor . exec ( callback ) ;
} else {
return cursor ;
}
} ;
/ * *
* Find all documents matching the query
* If no callback is passed , we return the cursor so that user can limit , skip and finally exec
* @ param { Object } query MongoDB - style query
* @ param { Object } projection MongoDB - style projection
* /
Datastore . prototype . find = function ( query , projection , callback ) {
switch ( arguments . length ) {
case 1 :
projection = { } ;
// callback is undefined, will return a cursor
break ;
case 2 :
if ( typeof projection === 'function' ) {
callback = projection ;
projection = { } ;
} // If not assume projection is an object and callback undefined
break ;
}
var cursor = new Cursor ( this , query , function ( err , docs , callback ) {
var res = [ ] , i ;
if ( err ) { return callback ( err ) ; }
for ( i = 0 ; i < docs . length ; i += 1 ) {
res . push ( model . deepCopy ( docs [ i ] ) ) ;
}
return callback ( null , res ) ;
} ) ;
cursor . projection ( projection ) ;
if ( typeof callback === 'function' ) {
cursor . exec ( callback ) ;
} else {
return cursor ;
}
} ;
/ * *
* Find one document matching the query
* @ param { Object } query MongoDB - style query
* @ param { Object } projection MongoDB - style projection
* /
Datastore . prototype . findOne = function ( query , projection , callback ) {
switch ( arguments . length ) {
case 1 :
projection = { } ;
// callback is undefined, will return a cursor
break ;
case 2 :
if ( typeof projection === 'function' ) {
callback = projection ;
projection = { } ;
} // If not assume projection is an object and callback undefined
break ;
}
var cursor = new Cursor ( this , query , function ( err , docs , callback ) {
if ( err ) { return callback ( err ) ; }
if ( docs . length === 1 ) {
return callback ( null , model . deepCopy ( docs [ 0 ] ) ) ;
} else {
return callback ( null , null ) ;
}
} ) ;
cursor . projection ( projection ) . limit ( 1 ) ;
if ( typeof callback === 'function' ) {
cursor . exec ( callback ) ;
} else {
return cursor ;
}
} ;
/ * *
* Update all docs matching query
* For now , very naive implementation ( recalculating the whole database )
* @ param { Object } query
* @ param { Object } updateQuery
* @ param { Object } options Optional options
* options . multi If true , can update multiple documents ( defaults to false )
* options . upsert If true , document is inserted if the query doesn ' t match anything
* @ param { Function } cb Optional callback , signature : err , numReplaced , upsert ( set to true if the update was in fact an upsert )
*
* @ api private Use Datastore . update which has the same signature
* /
Datastore . prototype . _update = function ( query , updateQuery , options , cb ) {
var callback
, self = this
, numReplaced = 0
, multi , upsert
, i
;
if ( typeof options === 'function' ) { cb = options ; options = { } ; }
callback = cb || function ( ) { } ;
multi = options . multi !== undefined ? options . multi : false ;
upsert = options . upsert !== undefined ? options . upsert : false ;
async . waterfall ( [
function ( cb ) { // If upsert option is set, check whether we need to insert the doc
if ( ! upsert ) { return cb ( ) ; }
// Need to use an internal function not tied to the executor to avoid deadlock
var cursor = new Cursor ( self , query ) ;
cursor . limit ( 1 ) . _exec ( function ( err , docs ) {
if ( err ) { return callback ( err ) ; }
if ( docs . length === 1 ) {
return cb ( ) ;
} else {
return self . _insert ( model . modify ( query , updateQuery ) , function ( err , newDoc ) {
if ( err ) { return callback ( err ) ; }
return callback ( null , 1 , newDoc ) ;
} ) ;
}
} ) ;
}
, function ( ) { // Perform the update
var modifiedDoc
, candidates = self . getCandidates ( query )
, modifications = [ ]
;
// Preparing update (if an error is thrown here neither the datafile nor
// the in-memory indexes are affected)
try {
for ( i = 0 ; i < candidates . length ; i += 1 ) {
if ( model . match ( candidates [ i ] , query ) && ( multi || numReplaced === 0 ) ) {
numReplaced += 1 ;
modifiedDoc = model . modify ( candidates [ i ] , updateQuery ) ;
modifications . push ( { oldDoc : candidates [ i ] , newDoc : modifiedDoc } ) ;
}
}
} catch ( err ) {
return callback ( err ) ;
}
// Change the docs in memory
try {
self . updateIndexes ( modifications ) ;
} catch ( err ) {
return callback ( err ) ;
}
// Update the datafile
self . persistence . persistNewState ( _ . pluck ( modifications , 'newDoc' ) , function ( err ) {
if ( err ) { return callback ( err ) ; }
return callback ( null , numReplaced ) ;
} ) ;
}
] ) ;
} ;
Datastore . prototype . update = function ( ) {
this . executor . push ( { this : this , fn : this . _update , arguments : arguments } ) ;
} ;
/ * *
* Remove all docs matching the query
* For now very naive implementation ( similar to update )
* @ param { Object } query
* @ param { Object } options Optional options
* options . multi If true , can update multiple documents ( defaults to false )
* @ param { Function } cb Optional callback , signature : err , numRemoved
*
* @ api private Use Datastore . remove which has the same signature
* /
Datastore . prototype . _remove = function ( query , options , cb ) {
var callback
, self = this
, numRemoved = 0
, multi
, removedDocs = [ ]
, candidates = this . getCandidates ( query )
;
if ( typeof options === 'function' ) { cb = options ; options = { } ; }
callback = cb || function ( ) { } ;
multi = options . multi !== undefined ? options . multi : false ;
try {
candidates . forEach ( function ( d ) {
if ( model . match ( d , query ) && ( multi || numRemoved === 0 ) ) {
numRemoved += 1 ;
removedDocs . push ( { $$deleted : true , _id : d . _id } ) ;
self . removeFromIndexes ( d ) ;
}
} ) ;
} catch ( err ) { return callback ( err ) ; }
self . persistence . persistNewState ( removedDocs , function ( err ) {
if ( err ) { return callback ( err ) ; }
return callback ( null , numRemoved ) ;
} ) ;
} ;
Datastore . prototype . remove = function ( ) {
this . executor . push ( { this : this , fn : this . _remove , arguments : arguments } ) ;
} ;
module . exports = Datastore ;
} , { "./cursor" : 4 , "./customUtils" : 5 , "./executor" : 7 , "./indexes" : 8 , "./model" : 9 , "./persistence" : 10 , "async" : 11 , "underscore" : 16 , "util" : 2 } ] , 7 : [ function ( require , module , exports ) {
var process = require ( "__browserify_process" ) ; / * *
* Responsible for sequentially executing actions on the database
* /
var async = require ( 'async' )
;
function Executor ( ) {
this . buffer = [ ] ;
this . ready = false ;
// This queue will execute all commands, one-by-one in order
this . queue = async . queue ( function ( task , cb ) {
var callback
, lastArg = task . arguments [ task . arguments . length - 1 ]
, i , newArguments = [ ]
;
// task.arguments is an array-like object on which adding a new field doesn't work, so we transform it into a real array
for ( i = 0 ; i < task . arguments . length ; i += 1 ) { newArguments . push ( task . arguments [ i ] ) ; }
// Always tell the queue task is complete. Execute callback if any was given.
if ( typeof lastArg === 'function' ) {
callback = function ( ) {
if ( typeof setImmediate === 'function' ) {
setImmediate ( cb ) ;
} else {
process . nextTick ( cb ) ;
}
lastArg . apply ( null , arguments ) ;
} ;
newArguments [ newArguments . length - 1 ] = callback ;
} else {
callback = function ( ) { cb ( ) ; } ;
newArguments . push ( callback ) ;
}
task . fn . apply ( task . this , newArguments ) ;
} , 1 ) ;
}
/ * *
* If executor is ready , queue task ( and process it immediately if executor was idle )
* If not , buffer task for later processing
* @ param { Object } task
* task . this - Object to use as this
* task . fn - Function to execute
* task . arguments - Array of arguments
* @ param { Boolean } forceQueuing Optional ( defaults to false ) force executor to queue task even if it is not ready
* /
Executor . prototype . push = function ( task , forceQueuing ) {
if ( this . ready || forceQueuing ) {
this . queue . push ( task ) ;
} else {
this . buffer . push ( task ) ;
}
} ;
/ * *
* Queue all tasks in buffer ( in the same order they came in )
* Automatically sets executor as ready
* /
Executor . prototype . processBuffer = function ( ) {
var i ;
this . ready = true ;
for ( i = 0 ; i < this . buffer . length ; i += 1 ) { this . queue . push ( this . buffer [ i ] ) ; }
this . buffer = [ ] ;
} ;
// Interface
module . exports = Executor ;
} , { "__browserify_process" : 3 , "async" : 11 } ] , 8 : [ function ( require , module , exports ) {
var BinarySearchTree = require ( 'binary-search-tree' ) . AVLTree
, model = require ( './model' )
, _ = require ( 'underscore' )
, util = require ( 'util' )
;
/ * *
* Two indexed pointers are equal iif they point to the same place
* /
function checkValueEquality ( a , b ) {
return a === b ;
}
/ * *
* Type - aware projection
* /
function projectForUnique ( elt ) {
if ( elt === null ) { return '$null' ; }
if ( typeof elt === 'string' ) { return '$string' + elt ; }
if ( typeof elt === 'boolean' ) { return '$boolean' + elt ; }
if ( typeof elt === 'number' ) { return '$number' + elt ; }
if ( util . isArray ( elt ) ) { return '$date' + elt . getTime ( ) ; }
return elt ; // Arrays and objects, will check for pointer equality
}
/ * *
* Create a new index
* All methods on an index guarantee that either the whole operation was successful and the index changed
* or the operation was unsuccessful and an error is thrown while the index is unchanged
* @ param { String } options . fieldName On which field should the index apply ( can use dot notation to index on sub fields )
* @ param { Boolean } options . unique Optional , enforce a unique constraint ( default : false )
* @ param { Boolean } options . sparse Optional , allow a sparse index ( we can have documents for which fieldName is undefined ) ( default : false )
* /
function Index ( options ) {
this . fieldName = options . fieldName ;
this . unique = options . unique || false ;
this . sparse = options . sparse || false ;
this . treeOptions = { unique : this . unique , compareKeys : model . compareThings , checkValueEquality : checkValueEquality } ;
this . reset ( ) ; // No data in the beginning
}
/ * *
* Reset an index
* @ param { Document or Array of documents } newData Optional , data to initialize the index with
* If an error is thrown during insertion , the index is not modified
* /
Index . prototype . reset = function ( newData ) {
this . tree = new BinarySearchTree ( this . treeOptions ) ;
if ( newData ) { this . insert ( newData ) ; }
} ;
/ * *
* Insert a new document in the index
* If an array is passed , we insert all its elements ( if one insertion fails the index is not modified )
* O ( log ( n ) )
* /
Index . prototype . insert = function ( doc ) {
var key , self = this
, keys , i , failingI , error
;
if ( util . isArray ( doc ) ) { this . insertMultipleDocs ( doc ) ; return ; }
key = model . getDotValue ( doc , this . fieldName ) ;
// We don't index documents that don't contain the field if the index is sparse
if ( key === undefined && this . sparse ) { return ; }
if ( ! util . isArray ( key ) ) {
this . tree . insert ( key , doc ) ;
} else {
// If an insert fails due to a unique constraint, roll back all inserts before it
keys = _ . uniq ( key , projectForUnique ) ;
for ( i = 0 ; i < keys . length ; i += 1 ) {
try {
this . tree . insert ( keys [ i ] , doc ) ;
} catch ( e ) {
error = e ;
failingI = i ;
break ;
}
}
if ( error ) {
for ( i = 0 ; i < failingI ; i += 1 ) {
this . tree . delete ( keys [ i ] , doc ) ;
}
throw error ;
}
}
} ;
/ * *
* Insert an array of documents in the index
* If a constraint is violated , the changes should be rolled back and an error thrown
*
* @ API private
* /
Index . prototype . insertMultipleDocs = function ( docs ) {
var i , error , failingI ;
for ( i = 0 ; i < docs . length ; i += 1 ) {
try {
this . insert ( docs [ i ] ) ;
} catch ( e ) {
error = e ;
failingI = i ;
break ;
}
}
if ( error ) {
for ( i = 0 ; i < failingI ; i += 1 ) {
this . remove ( docs [ i ] ) ;
}
throw error ;
}
} ;
/ * *
* Remove a document from the index
* If an array is passed , we remove all its elements
* The remove operation is safe with regards to the 'unique' constraint
* O ( log ( n ) )
* /
Index . prototype . remove = function ( doc ) {
var key , self = this ;
if ( util . isArray ( doc ) ) { doc . forEach ( function ( d ) { self . remove ( d ) ; } ) ; return ; }
key = model . getDotValue ( doc , this . fieldName ) ;
if ( key === undefined && this . sparse ) { return ; }
if ( ! util . isArray ( key ) ) {
this . tree . delete ( key , doc ) ;
} else {
_ . uniq ( key , projectForUnique ) . forEach ( function ( _key ) {
self . tree . delete ( _key , doc ) ;
} ) ;
}
} ;
/ * *
* Update a document in the index
* If a constraint is violated , changes are rolled back and an error thrown
* Naive implementation , still in O ( log ( n ) )
* /
Index . prototype . update = function ( oldDoc , newDoc ) {
if ( util . isArray ( oldDoc ) ) { this . updateMultipleDocs ( oldDoc ) ; return ; }
this . remove ( oldDoc ) ;
try {
this . insert ( newDoc ) ;
} catch ( e ) {
this . insert ( oldDoc ) ;
throw e ;
}
} ;
/ * *
* Update multiple documents in the index
* If a constraint is violated , the changes need to be rolled back
* and an error thrown
* @ param { Array of oldDoc , newDoc pairs } pairs
*
* @ API private
* /
Index . prototype . updateMultipleDocs = function ( pairs ) {
var i , failingI , error ;
for ( i = 0 ; i < pairs . length ; i += 1 ) {
this . remove ( pairs [ i ] . oldDoc ) ;
}
for ( i = 0 ; i < pairs . length ; i += 1 ) {
try {
this . insert ( pairs [ i ] . newDoc ) ;
} catch ( e ) {
error = e ;
failingI = i ;
break ;
}
}
// If an error was raised, roll back changes in the inverse order
if ( error ) {
for ( i = 0 ; i < failingI ; i += 1 ) {
this . remove ( pairs [ i ] . newDoc ) ;
}
for ( i = 0 ; i < pairs . length ; i += 1 ) {
this . insert ( pairs [ i ] . oldDoc ) ;
}
throw error ;
}
} ;
/ * *
* Revert an update
* /
Index . prototype . revertUpdate = function ( oldDoc , newDoc ) {
var revert = [ ] ;
if ( ! util . isArray ( oldDoc ) ) {
this . update ( newDoc , oldDoc ) ;
} else {
oldDoc . forEach ( function ( pair ) {
revert . push ( { oldDoc : pair . newDoc , newDoc : pair . oldDoc } ) ;
} ) ;
this . update ( revert ) ;
}
} ;
// Append all elements in toAppend to array
function append ( array , toAppend ) {
var i ;
for ( i = 0 ; i < toAppend . length ; i += 1 ) {
array . push ( toAppend [ i ] ) ;
}
}
/ * *
* Get all documents in index whose key match value ( if it is a Thing ) or one of the elements of value ( if it is an array of Things )
* @ param { Thing } value Value to match the key against
* @ return { Array of documents }
* /
Index . prototype . getMatching = function ( value ) {
var res , self = this ;
if ( ! util . isArray ( value ) ) {
return this . tree . search ( value ) ;
} else {
res = [ ] ;
value . forEach ( function ( v ) { append ( res , self . getMatching ( v ) ) ; } ) ;
return res ;
}
} ;
/ * *
* Get all documents in index whose key is between bounds are they are defined by query
* Documents are sorted by key
* @ param { Query } query
* @ return { Array of documents }
* /
Index . prototype . getBetweenBounds = function ( query ) {
return this . tree . betweenBounds ( query ) ;
} ;
/ * *
* Get all elements in the index
* @ return { Array of documents }
* /
Index . prototype . getAll = function ( ) {
var res = [ ] ;
this . tree . executeOnEveryNode ( function ( node ) {
var i ;
for ( i = 0 ; i < node . data . length ; i += 1 ) {
res . push ( node . data [ i ] ) ;
}
} ) ;
return res ;
} ;
// Interface
module . exports = Index ;
} , { "./model" : 9 , "binary-search-tree" : 12 , "underscore" : 16 , "util" : 2 } ] , 9 : [ function ( require , module , exports ) {
/ * *
* Handle models ( i . e . docs )
* Serialization / deserialization
* Copying
* Querying , update
* /
var util = require ( 'util' )
, _ = require ( 'underscore' )
, modifierFunctions = { }
, lastStepModifierFunctions = { }
, comparisonFunctions = { }
, logicalOperators = { }
, arrayComparisonFunctions = { }
;
/ * *
* Check a key , throw an error if the key is non valid
* @ param { String } k key
* @ param { Model } v value , needed to treat the Date edge case
* Non - treatable edge cases here : if part of the object if of the form { $$date : number } or { $$deleted : true }
* Its serialized - then - deserialized version it will transformed into a Date object
* But you really need to want it to trigger such behaviour , even when warned not to use '$' at the beginning of the field names ...
* /
function checkKey ( k , v ) {
if ( k [ 0 ] === '$' && ! ( k === '$$date' && typeof v === 'number' ) && ! ( k === '$$deleted' && v === true ) && ! ( k === '$$indexCreated' ) && ! ( k === '$$indexRemoved' ) ) {
throw 'Field names cannot begin with the $ character' ;
}
if ( k . indexOf ( '.' ) !== - 1 ) {
throw 'Field names cannot contain a .' ;
}
}
/ * *
* Check a DB object and throw an error if it ' s not valid
* Works by applying the above checkKey function to all fields recursively
* /
function checkObject ( obj ) {
if ( util . isArray ( obj ) ) {
obj . forEach ( function ( o ) {
checkObject ( o ) ;
} ) ;
}
if ( typeof obj === 'object' && obj !== null ) {
Object . keys ( obj ) . forEach ( function ( k ) {
checkKey ( k , obj [ k ] ) ;
checkObject ( obj [ k ] ) ;
} ) ;
}
}
/ * *
* Serialize an object to be persisted to a one - line string
* For serialization / deserialization , we use the native JSON parser and not eval or Function
* That gives us less freedom but data entered in the database may come from users
* so eval and the like are not safe
* Accepted primitive types : Number , String , Boolean , Date , null
* Accepted secondary types : Objects , Arrays
* /
function serialize ( obj ) {
var res ;
res = JSON . stringify ( obj , function ( k , v ) {
checkKey ( k , v ) ;
if ( v === undefined ) { return undefined ; }
if ( v === null ) { return null ; }
// Hackish way of checking if object is Date (this way it works between execution contexts in node-webkit).
// We can't use value directly because for dates it is already string in this function (date.toJSON was already called), so we use this
if ( typeof this [ k ] . getTime === 'function' ) { return { $$date : this [ k ] . getTime ( ) } ; }
return v ;
} ) ;
return res ;
}
/ * *
* From a one - line representation of an object generate by the serialize function
* Return the object itself
* /
function deserialize ( rawData ) {
return JSON . parse ( rawData , function ( k , v ) {
if ( k === '$$date' ) { return new Date ( v ) ; }
if ( typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null ) { return v ; }
if ( v && v . $$date ) { return v . $$date ; }
return v ;
} ) ;
}
/ * *
* Deep copy a DB object
* /
function deepCopy ( obj ) {
var res ;
if ( typeof obj === 'boolean' ||
typeof obj === 'number' ||
typeof obj === 'string' ||
obj === null ||
( util . isDate ( obj ) ) ) {
return obj ;
}
if ( util . isArray ( obj ) ) {
res = [ ] ;
obj . forEach ( function ( o ) { res . push ( o ) ; } ) ;
return res ;
}
if ( typeof obj === 'object' ) {
res = { } ;
Object . keys ( obj ) . forEach ( function ( k ) {
res [ k ] = deepCopy ( obj [ k ] ) ;
} ) ;
return res ;
}
return undefined ; // For now everything else is undefined. We should probably throw an error instead
}
/ * *
* Tells if an object is a primitive type or a "real" object
* Arrays are considered primitive
* /
function isPrimitiveType ( obj ) {
return ( typeof obj === 'boolean' ||
typeof obj === 'number' ||
typeof obj === 'string' ||
obj === null ||
util . isDate ( obj ) ||
util . isArray ( obj ) ) ;
}
/ * *
* Utility functions for comparing things
* Assumes type checking was already done ( a and b already have the same type )
* compareNSB works for numbers , strings and booleans
* /
function compareNSB ( a , b ) {
if ( a < b ) { return - 1 ; }
if ( a > b ) { return 1 ; }
return 0 ;
}
function compareArrays ( a , b ) {
var i , comp ;
for ( i = 0 ; i < Math . min ( a . length , b . length ) ; i += 1 ) {
comp = compareThings ( a [ i ] , b [ i ] ) ;
if ( comp !== 0 ) { return comp ; }
}
// Common section was identical, longest one wins
return compareNSB ( a . length , b . length ) ;
}
/ * *
* Compare { things U undefined }
* Things are defined as any native types ( string , number , boolean , null , date ) and objects
* We need to compare with undefined as it will be used in indexes
* In the case of objects and arrays , we deep - compare
* If two objects dont have the same type , the ( arbitrary ) type hierarchy is : undefined , null , number , strings , boolean , dates , arrays , objects
* Return - 1 if a < b , 1 if a > b and 0 if a = b ( note that equality here is NOT the same as defined in areThingsEqual ! )
* /
function compareThings ( a , b ) {
var aKeys , bKeys , comp , i ;
// undefined
if ( a === undefined ) { return b === undefined ? 0 : - 1 ; }
if ( b === undefined ) { return a === undefined ? 0 : 1 ; }
// null
if ( a === null ) { return b === null ? 0 : - 1 ; }
if ( b === null ) { return a === null ? 0 : 1 ; }
// Numbers
if ( typeof a === 'number' ) { return typeof b === 'number' ? compareNSB ( a , b ) : - 1 ; }
if ( typeof b === 'number' ) { return typeof a === 'number' ? compareNSB ( a , b ) : 1 ; }
// Strings
if ( typeof a === 'string' ) { return typeof b === 'string' ? compareNSB ( a , b ) : - 1 ; }
if ( typeof b === 'string' ) { return typeof a === 'string' ? compareNSB ( a , b ) : 1 ; }
// Booleans
if ( typeof a === 'boolean' ) { return typeof b === 'boolean' ? compareNSB ( a , b ) : - 1 ; }
if ( typeof b === 'boolean' ) { return typeof a === 'boolean' ? compareNSB ( a , b ) : 1 ; }
// Dates
if ( util . isDate ( a ) ) { return util . isDate ( b ) ? compareNSB ( a . getTime ( ) , b . getTime ( ) ) : - 1 ; }
if ( util . isDate ( b ) ) { return util . isDate ( a ) ? compareNSB ( a . getTime ( ) , b . getTime ( ) ) : 1 ; }
// Arrays (first element is most significant and so on)
if ( util . isArray ( a ) ) { return util . isArray ( b ) ? compareArrays ( a , b ) : - 1 ; }
if ( util . isArray ( b ) ) { return util . isArray ( a ) ? compareArrays ( a , b ) : 1 ; }
// Objects
aKeys = Object . keys ( a ) . sort ( ) ;
bKeys = Object . keys ( b ) . sort ( ) ;
for ( i = 0 ; i < Math . min ( aKeys . length , bKeys . length ) ; i += 1 ) {
comp = compareThings ( a [ aKeys [ i ] ] , b [ bKeys [ i ] ] ) ;
if ( comp !== 0 ) { return comp ; }
}
return compareNSB ( aKeys . length , bKeys . length ) ;
}
// ==============================================================
// Updating documents
// ==============================================================
/ * *
* The signature of modifier functions is as follows
* Their structure is always the same : recursively follow the dot notation while creating
* the nested documents if needed , then apply the "last step modifier"
* @ param { Object } obj The model to modify
* @ param { String } field Can contain dots , in that case that means we will set a subfield recursively
* @ param { Model } value
* /
/ * *
* Set a field to a new value
* /
lastStepModifierFunctions . $set = function ( obj , field , value ) {
obj [ field ] = value ;
} ;
/ * *
* Unset a field
* /
lastStepModifierFunctions . $unset = function ( obj , field , value ) {
delete obj [ field ] ;
} ;
/ * *
* Push an element to the end of an array field
* /
lastStepModifierFunctions . $push = function ( obj , field , value ) {
// Create the array if it doesn't exist
if ( ! obj . hasOwnProperty ( field ) ) { obj [ field ] = [ ] ; }
if ( ! util . isArray ( obj [ field ] ) ) { throw "Can't $push an element on non-array values" ; }
if ( value !== null && typeof value === 'object' && value . $each ) {
if ( Object . keys ( value ) . length > 1 ) { throw "Can't use another field in conjunction with $each" ; }
if ( ! util . isArray ( value . $each ) ) { throw "$each requires an array value" ; }
value . $each . forEach ( function ( v ) {
obj [ field ] . push ( v ) ;
} ) ;
} else {
obj [ field ] . push ( value ) ;
}
} ;
/ * *
* Add an element to an array field only if it is not already in it
* No modification if the element is already in the array
* Note that it doesn ' t check whether the original array contains duplicates
* /
lastStepModifierFunctions . $addToSet = function ( obj , field , value ) {
var addToSet = true ;
// Create the array if it doesn't exist
if ( ! obj . hasOwnProperty ( field ) ) { obj [ field ] = [ ] ; }
if ( ! util . isArray ( obj [ field ] ) ) { throw "Can't $addToSet an element on non-array values" ; }
if ( value !== null && typeof value === 'object' && value . $each ) {
if ( Object . keys ( value ) . length > 1 ) { throw "Can't use another field in conjunction with $each" ; }
if ( ! util . isArray ( value . $each ) ) { throw "$each requires an array value" ; }
value . $each . forEach ( function ( v ) {
lastStepModifierFunctions . $addToSet ( obj , field , v ) ;
} ) ;
} else {
obj [ field ] . forEach ( function ( v ) {
if ( compareThings ( v , value ) === 0 ) { addToSet = false ; }
} ) ;
if ( addToSet ) { obj [ field ] . push ( value ) ; }
}
} ;
/ * *
* Remove the first or last element of an array
* /
lastStepModifierFunctions . $pop = function ( obj , field , value ) {
if ( ! util . isArray ( obj [ field ] ) ) { throw "Can't $pop an element from non-array values" ; }
if ( typeof value !== 'number' ) { throw value + " isn't an integer, can't use it with $pop" ; }
if ( value === 0 ) { return ; }
if ( value > 0 ) {
obj [ field ] = obj [ field ] . slice ( 0 , obj [ field ] . length - 1 ) ;
} else {
obj [ field ] = obj [ field ] . slice ( 1 ) ;
}
} ;
/ * *
* Removes all instances of a value from an existing array
* /
lastStepModifierFunctions . $pull = function ( obj , field , value ) {
var arr , i ;
if ( ! util . isArray ( obj [ field ] ) ) { throw "Can't $pull an element from non-array values" ; }
arr = obj [ field ] ;
for ( i = arr . length - 1 ; i >= 0 ; i -= 1 ) {
if ( match ( arr [ i ] , value ) ) {
arr . splice ( i , 1 ) ;
}
}
} ;
/ * *
* Increment a numeric field ' s value
* /
lastStepModifierFunctions . $inc = function ( obj , field , value ) {
if ( typeof value !== 'number' ) { throw value + " must be a number" ; }
if ( typeof obj [ field ] !== 'number' ) {
if ( ! _ . has ( obj , field ) ) {
obj [ field ] = value ;
} else {
throw "Don't use the $inc modifier on non-number fields" ;
}
} else {
obj [ field ] += value ;
}
} ;
// Given its name, create the complete modifier function
function createModifierFunction ( modifier ) {
return function ( obj , field , value ) {
var fieldParts = typeof field === 'string' ? field . split ( '.' ) : field ;
if ( fieldParts . length === 1 ) {
lastStepModifierFunctions [ modifier ] ( obj , field , value ) ;
} else {
obj [ fieldParts [ 0 ] ] = obj [ fieldParts [ 0 ] ] || { } ;
modifierFunctions [ modifier ] ( obj [ fieldParts [ 0 ] ] , fieldParts . slice ( 1 ) , value ) ;
}
} ;
}
// Actually create all modifier functions
Object . keys ( lastStepModifierFunctions ) . forEach ( function ( modifier ) {
modifierFunctions [ modifier ] = createModifierFunction ( modifier ) ;
} ) ;
/ * *
* Modify a DB object according to an update query
* For now the updateQuery only replaces the object
* /
function modify ( obj , updateQuery ) {
var keys = Object . keys ( updateQuery )
, firstChars = _ . map ( keys , function ( item ) { return item [ 0 ] ; } )
, dollarFirstChars = _ . filter ( firstChars , function ( c ) { return c === '$' ; } )
, newDoc , modifiers
;
if ( keys . indexOf ( '_id' ) !== - 1 && updateQuery . _id !== obj . _id ) { throw "You cannot change a document's _id" ; }
if ( dollarFirstChars . length !== 0 && dollarFirstChars . length !== firstChars . length ) {
throw "You cannot mix modifiers and normal fields" ;
}
if ( dollarFirstChars . length === 0 ) {
// Simply replace the object with the update query contents
newDoc = deepCopy ( updateQuery ) ;
newDoc . _id = obj . _id ;
} else {
// Apply modifiers
modifiers = _ . uniq ( keys ) ;
newDoc = deepCopy ( obj ) ;
modifiers . forEach ( function ( m ) {
var keys ;
if ( ! modifierFunctions [ m ] ) { throw "Unknown modifier " + m ; }
try {
keys = Object . keys ( updateQuery [ m ] ) ;
} catch ( e ) {
throw "Modifier " + m + "'s argument must be an object" ;
}
keys . forEach ( function ( k ) {
modifierFunctions [ m ] ( newDoc , k , updateQuery [ m ] [ k ] ) ;
} ) ;
} ) ;
}
// Check result is valid and return it
checkObject ( newDoc ) ;
if ( obj . _id !== newDoc . _id ) { throw "You can't change a document's _id" ; }
return newDoc ;
} ;
// ==============================================================
// Finding documents
// ==============================================================
/ * *
* Get a value from object with dot notation
* @ param { Object } obj
* @ param { String } field
* /
function getDotValue ( obj , field ) {
var fieldParts = typeof field === 'string' ? field . split ( '.' ) : field
, i , objs ;
if ( ! obj ) { return undefined ; } // field cannot be empty so that means we should return undefined so that nothing can match
if ( fieldParts . length === 0 ) { return obj ; }
if ( fieldParts . length === 1 ) { return obj [ fieldParts [ 0 ] ] ; }
if ( util . isArray ( obj [ fieldParts [ 0 ] ] ) ) {
// If the next field is an integer, return only this item of the array
i = parseInt ( fieldParts [ 1 ] , 10 ) ;
if ( typeof i === 'number' && ! isNaN ( i ) ) {
return getDotValue ( obj [ fieldParts [ 0 ] ] [ i ] , fieldParts . slice ( 2 ) )
}
// Return the array of values
objs = new Array ( ) ;
for ( i = 0 ; i < obj [ fieldParts [ 0 ] ] . length ; i += 1 ) {
objs . push ( getDotValue ( obj [ fieldParts [ 0 ] ] [ i ] , fieldParts . slice ( 1 ) ) ) ;
}
return objs ;
} else {
return getDotValue ( obj [ fieldParts [ 0 ] ] , fieldParts . slice ( 1 ) ) ;
}
}
/ * *
* Check whether 'things' are equal
* Things are defined as any native types ( string , number , boolean , null , date ) and objects
* In the case of object , we check deep equality
* Returns true if they are , false otherwise
* /
function areThingsEqual ( a , b ) {
var aKeys , bKeys , i ;
// Strings, booleans, numbers, null
if ( a === null || typeof a === 'string' || typeof a === 'boolean' || typeof a === 'number' ||
b === null || typeof b === 'string' || typeof b === 'boolean' || typeof b === 'number' ) { return a === b ; }
// Dates
if ( util . isDate ( a ) || util . isDate ( b ) ) { return util . isDate ( a ) && util . isDate ( b ) && a . getTime ( ) === b . getTime ( ) ; }
// Arrays (no match since arrays are used as a $in)
// undefined (no match since they mean field doesn't exist and can't be serialized)
if ( util . isArray ( a ) || util . isArray ( b ) || a === undefined || b === undefined ) { return false ; }
// General objects (check for deep equality)
// a and b should be objects at this point
try {
aKeys = Object . keys ( a ) ;
bKeys = Object . keys ( b ) ;
} catch ( e ) {
return false ;
}
if ( aKeys . length !== bKeys . length ) { return false ; }
for ( i = 0 ; i < aKeys . length ; i += 1 ) {
if ( bKeys . indexOf ( aKeys [ i ] ) === - 1 ) { return false ; }
if ( ! areThingsEqual ( a [ aKeys [ i ] ] , b [ aKeys [ i ] ] ) ) { return false ; }
}
return true ;
}
/ * *
* Check that two values are comparable
* /
function areComparable ( a , b ) {
if ( typeof a !== 'string' && typeof a !== 'number' && ! util . isDate ( a ) &&
typeof b !== 'string' && typeof b !== 'number' && ! util . isDate ( b ) ) {
return false ;
}
if ( typeof a !== typeof b ) { return false ; }
return true ;
}
/ * *
* Arithmetic and comparison operators
* @ param { Native value } a Value in the object
* @ param { Native value } b Value in the query
* /
comparisonFunctions . $lt = function ( a , b ) {
return areComparable ( a , b ) && a < b ;
} ;
comparisonFunctions . $lte = function ( a , b ) {
return areComparable ( a , b ) && a <= b ;
} ;
comparisonFunctions . $gt = function ( a , b ) {
return areComparable ( a , b ) && a > b ;
} ;
comparisonFunctions . $gte = function ( a , b ) {
return areComparable ( a , b ) && a >= b ;
} ;
comparisonFunctions . $ne = function ( a , b ) {
if ( ! a ) { return true ; }
return ! areThingsEqual ( a , b ) ;
} ;
comparisonFunctions . $in = function ( a , b ) {
var i ;
if ( ! util . isArray ( b ) ) { throw "$in operator called with a non-array" ; }
for ( i = 0 ; i < b . length ; i += 1 ) {
if ( areThingsEqual ( a , b [ i ] ) ) { return true ; }
}
return false ;
} ;
comparisonFunctions . $nin = function ( a , b ) {
if ( ! util . isArray ( b ) ) { throw "$nin operator called with a non-array" ; }
return ! comparisonFunctions . $in ( a , b ) ;
} ;
comparisonFunctions . $regex = function ( a , b ) {
if ( ! util . isRegExp ( b ) ) { throw "$regex operator called with non regular expression" ; }
if ( typeof a !== 'string' ) {
return false
} else {
return b . test ( a ) ;
}
} ;
comparisonFunctions . $exists = function ( value , exists ) {
if ( exists || exists === '' ) { // This will be true for all values of exists except false, null, undefined and 0
exists = true ; // That's strange behaviour (we should only use true/false) but that's the way Mongo does it...
} else {
exists = false ;
}
if ( value === undefined ) {
return ! exists
} else {
return exists ;
}
} ;
// Specific to arrays
comparisonFunctions . $size = function ( obj , value ) {
if ( ! util . isArray ( obj ) ) { return false ; }
if ( value % 1 !== 0 ) { throw "$size operator called without an integer" ; }
return ( obj . length == value ) ;
} ;
arrayComparisonFunctions . $size = true ;
/ * *
* Match any of the subqueries
* @ param { Model } obj
* @ param { Array of Queries } query
* /
logicalOperators . $or = function ( obj , query ) {
var i ;
if ( ! util . isArray ( query ) ) { throw "$or operator used without an array" ; }
for ( i = 0 ; i < query . length ; i += 1 ) {
if ( match ( obj , query [ i ] ) ) { return true ; }
}
return false ;
} ;
/ * *
* Match all of the subqueries
* @ param { Model } obj
* @ param { Array of Queries } query
* /
logicalOperators . $and = function ( obj , query ) {
var i ;
if ( ! util . isArray ( query ) ) { throw "$and operator used without an array" ; }
for ( i = 0 ; i < query . length ; i += 1 ) {
if ( ! match ( obj , query [ i ] ) ) { return false ; }
}
return true ;
} ;
/ * *
* Inverted match of the query
* @ param { Model } obj
* @ param { Query } query
* /
logicalOperators . $not = function ( obj , query ) {
return ! match ( obj , query ) ;
} ;
/ * *
* Use a function to match
* @ param { Model } obj
* @ param { Query } query
* /
logicalOperators . $where = function ( obj , fn ) {
var result ;
if ( ! _ . isFunction ( fn ) ) { throw "$where operator used without a function" ; }
result = fn . call ( obj ) ;
if ( ! _ . isBoolean ( result ) ) { throw "$where function must return boolean" ; }
return result ;
} ;
/ * *
* Tell if a given document matches a query
* @ param { Object } obj Document to check
* @ param { Object } query
* /
function match ( obj , query ) {
var queryKeys , queryKey , queryValue , i ;
// Primitive query against a primitive type
// This is a bit of a hack since we construct an object with an arbitrary key only to dereference it later
// But I don't have time for a cleaner implementation now
if ( isPrimitiveType ( obj ) || isPrimitiveType ( query ) ) {
return matchQueryPart ( { needAKey : obj } , 'needAKey' , query ) ;
}
// Normal query
queryKeys = Object . keys ( query ) ;
for ( i = 0 ; i < queryKeys . length ; i += 1 ) {
queryKey = queryKeys [ i ] ;
queryValue = query [ queryKey ] ;
if ( queryKey [ 0 ] === '$' ) {
if ( ! logicalOperators [ queryKey ] ) { throw "Unknown logical operator " + queryKey ; }
if ( ! logicalOperators [ queryKey ] ( obj , queryValue ) ) { return false ; }
} else {
if ( ! matchQueryPart ( obj , queryKey , queryValue ) ) { return false ; }
}
}
return true ;
} ;
/ * *
* Match an object against a specific { key : value } part of a query
* if the treatObjAsValue flag is set , don ' t try to match every part separately , but the array as a whole
* /
function matchQueryPart ( obj , queryKey , queryValue , treatObjAsValue ) {
var objValue = getDotValue ( obj , queryKey )
, i , keys , firstChars , dollarFirstChars ;
// Check if the value is an array if we don't force a treatment as value
if ( util . isArray ( objValue ) && ! treatObjAsValue ) {
// Check if we are using an array-specific comparison function
if ( queryValue !== null && typeof queryValue === 'object' && ! util . isRegExp ( queryValue ) ) {
keys = Object . keys ( queryValue ) ;
for ( i = 0 ; i < keys . length ; i += 1 ) {
if ( arrayComparisonFunctions [ keys [ i ] ] ) { return matchQueryPart ( obj , queryKey , queryValue , true ) ; }
}
}
// If not, treat it as an array of { obj, query } where there needs to be at least one match
for ( i = 0 ; i < objValue . length ; i += 1 ) {
if ( matchQueryPart ( { k : objValue [ i ] } , 'k' , queryValue ) ) { return true ; } // k here could be any string
}
return false ;
}
// queryValue is an actual object. Determine whether it contains comparison operators
// or only normal fields. Mixed objects are not allowed
if ( queryValue !== null && typeof queryValue === 'object' && ! util . isRegExp ( queryValue ) ) {
keys = Object . keys ( queryValue ) ;
firstChars = _ . map ( keys , function ( item ) { return item [ 0 ] ; } ) ;
dollarFirstChars = _ . filter ( firstChars , function ( c ) { return c === '$' ; } ) ;
if ( dollarFirstChars . length !== 0 && dollarFirstChars . length !== firstChars . length ) {
throw "You cannot mix operators and normal fields" ;
}
// queryValue is an object of this form: { $comparisonOperator1: value1, ... }
if ( dollarFirstChars . length > 0 ) {
for ( i = 0 ; i < keys . length ; i += 1 ) {
if ( ! comparisonFunctions [ keys [ i ] ] ) { throw "Unknown comparison function " + keys [ i ] ; }
if ( ! comparisonFunctions [ keys [ i ] ] ( objValue , queryValue [ keys [ i ] ] ) ) { return false ; }
}
return true ;
}
}
// Using regular expressions with basic querying
if ( util . isRegExp ( queryValue ) ) { return comparisonFunctions . $regex ( objValue , queryValue ) ; }
// queryValue is either a native value or a normal object
// Basic matching is possible
if ( ! areThingsEqual ( objValue , queryValue ) ) { return false ; }
return true ;
}
// Interface
module . exports . serialize = serialize ;
module . exports . deserialize = deserialize ;
module . exports . deepCopy = deepCopy ;
module . exports . checkObject = checkObject ;
module . exports . isPrimitiveType = isPrimitiveType ;
module . exports . modify = modify ;
module . exports . getDotValue = getDotValue ;
module . exports . match = match ;
module . exports . areThingsEqual = areThingsEqual ;
module . exports . compareThings = compareThings ;
} , { "underscore" : 16 , "util" : 2 } ] , 10 : [ function ( require , module , exports ) {
/ * *
* Handle every persistence - related task
* The interface Datastore expects to be implemented is
* * Persistence . loadDatabase ( callback ) and callback has signature err
* * Persistence . persistNewState ( newDocs , callback ) where newDocs is an array of documents and callback has signature err
*
* Shim for the browser
* /
/ * *
* Create a new Persistence object for database options . db
* For now , no browser persistence supported , in - memory only mode forced
* @ param { Datastore } options . db
* /
function Persistence ( options ) {
this . db = options . db ;
this . db . inMemoryOnly = true ;
this . db . filename = null ;
this . inMemoryOnly = true ;
} ;
/ * *
* No persistence in the browser ( for now )
* /
Persistence . prototype . persistNewState = function ( newDocs , cb ) {
if ( cb ) { return cb ( ) ; }
} ;
/ * *
* No persistence in the browser ( for now )
* /
Persistence . prototype . loadDatabase = function ( cb ) {
if ( cb ) { return cb ( ) ; }
} ;
// Interface
module . exports = Persistence ;
} , { } ] , 11 : [ function ( require , module , exports ) {
var process = require ( "__browserify_process" ) ; /*global setImmediate: false, setTimeout: false, console: false */
( function ( ) {
var async = { } ;
// global on the server, window in the browser
var root , previous _async ;
root = this ;
if ( root != null ) {
previous _async = root . async ;
}
async . noConflict = function ( ) {
root . async = previous _async ;
return async ;
} ;
function only _once ( fn ) {
var called = false ;
return function ( ) {
if ( called ) throw new Error ( "Callback was already called." ) ;
called = true ;
fn . apply ( root , arguments ) ;
}
}
//// cross-browser compatiblity functions ////
var _each = function ( arr , iterator ) {
if ( arr . forEach ) {
return arr . forEach ( iterator ) ;
}
for ( var i = 0 ; i < arr . length ; i += 1 ) {
iterator ( arr [ i ] , i , arr ) ;
}
} ;
var _map = function ( arr , iterator ) {
if ( arr . map ) {
return arr . map ( iterator ) ;
}
var results = [ ] ;
_each ( arr , function ( x , i , a ) {
results . push ( iterator ( x , i , a ) ) ;
} ) ;
return results ;
} ;
var _reduce = function ( arr , iterator , memo ) {
if ( arr . reduce ) {
return arr . reduce ( iterator , memo ) ;
}
_each ( arr , function ( x , i , a ) {
memo = iterator ( memo , x , i , a ) ;
} ) ;
return memo ;
} ;
var _keys = function ( obj ) {
if ( Object . keys ) {
return Object . keys ( obj ) ;
}
var keys = [ ] ;
for ( var k in obj ) {
if ( obj . hasOwnProperty ( k ) ) {
keys . push ( k ) ;
}
}
return keys ;
} ;
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
if ( typeof process === 'undefined' || ! ( process . nextTick ) ) {
if ( typeof setImmediate === 'function' ) {
async . nextTick = function ( fn ) {
// not a direct alias for IE10 compatibility
setImmediate ( fn ) ;
} ;
async . setImmediate = async . nextTick ;
}
else {
async . nextTick = function ( fn ) {
setTimeout ( fn , 0 ) ;
} ;
async . setImmediate = async . nextTick ;
}
}
else {
async . nextTick = process . nextTick ;
if ( typeof setImmediate !== 'undefined' ) {
async . setImmediate = function ( fn ) {
// not a direct alias for IE10 compatibility
setImmediate ( fn ) ;
} ;
}
else {
async . setImmediate = async . nextTick ;
}
}
async . each = function ( arr , iterator , callback ) {
callback = callback || function ( ) { } ;
if ( ! arr . length ) {
return callback ( ) ;
}
var completed = 0 ;
_each ( arr , function ( x ) {
iterator ( x , only _once ( function ( err ) {
if ( err ) {
callback ( err ) ;
callback = function ( ) { } ;
}
else {
completed += 1 ;
if ( completed >= arr . length ) {
callback ( null ) ;
}
}
} ) ) ;
} ) ;
} ;
async . forEach = async . each ;
async . eachSeries = function ( arr , iterator , callback ) {
callback = callback || function ( ) { } ;
if ( ! arr . length ) {
return callback ( ) ;
}
var completed = 0 ;
var iterate = function ( ) {
iterator ( arr [ completed ] , function ( err ) {
if ( err ) {
callback ( err ) ;
callback = function ( ) { } ;
}
else {
completed += 1 ;
if ( completed >= arr . length ) {
callback ( null ) ;
}
else {
iterate ( ) ;
}
}
} ) ;
} ;
iterate ( ) ;
} ;
async . forEachSeries = async . eachSeries ;
async . eachLimit = function ( arr , limit , iterator , callback ) {
var fn = _eachLimit ( limit ) ;
fn . apply ( null , [ arr , iterator , callback ] ) ;
} ;
async . forEachLimit = async . eachLimit ;
var _eachLimit = function ( limit ) {
return function ( arr , iterator , callback ) {
callback = callback || function ( ) { } ;
if ( ! arr . length || limit <= 0 ) {
return callback ( ) ;
}
var completed = 0 ;
var started = 0 ;
var running = 0 ;
( function replenish ( ) {
if ( completed >= arr . length ) {
return callback ( ) ;
}
while ( running < limit && started < arr . length ) {
started += 1 ;
running += 1 ;
iterator ( arr [ started - 1 ] , function ( err ) {
if ( err ) {
callback ( err ) ;
callback = function ( ) { } ;
}
else {
completed += 1 ;
running -= 1 ;
if ( completed >= arr . length ) {
callback ( ) ;
}
else {
replenish ( ) ;
}
}
} ) ;
}
} ) ( ) ;
} ;
} ;
var doParallel = function ( fn ) {
return function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
return fn . apply ( null , [ async . each ] . concat ( args ) ) ;
} ;
} ;
var doParallelLimit = function ( limit , fn ) {
return function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
return fn . apply ( null , [ _eachLimit ( limit ) ] . concat ( args ) ) ;
} ;
} ;
var doSeries = function ( fn ) {
return function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
return fn . apply ( null , [ async . eachSeries ] . concat ( args ) ) ;
} ;
} ;
var _asyncMap = function ( eachfn , arr , iterator , callback ) {
var results = [ ] ;
arr = _map ( arr , function ( x , i ) {
return { index : i , value : x } ;
} ) ;
eachfn ( arr , function ( x , callback ) {
iterator ( x . value , function ( err , v ) {
results [ x . index ] = v ;
callback ( err ) ;
} ) ;
} , function ( err ) {
callback ( err , results ) ;
} ) ;
} ;
async . map = doParallel ( _asyncMap ) ;
async . mapSeries = doSeries ( _asyncMap ) ;
async . mapLimit = function ( arr , limit , iterator , callback ) {
return _mapLimit ( limit ) ( arr , iterator , callback ) ;
} ;
var _mapLimit = function ( limit ) {
return doParallelLimit ( limit , _asyncMap ) ;
} ;
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async . reduce = function ( arr , memo , iterator , callback ) {
async . eachSeries ( arr , function ( x , callback ) {
iterator ( memo , x , function ( err , v ) {
memo = v ;
callback ( err ) ;
} ) ;
} , function ( err ) {
callback ( err , memo ) ;
} ) ;
} ;
// inject alias
async . inject = async . reduce ;
// foldl alias
async . foldl = async . reduce ;
async . reduceRight = function ( arr , memo , iterator , callback ) {
var reversed = _map ( arr , function ( x ) {
return x ;
} ) . reverse ( ) ;
async . reduce ( reversed , memo , iterator , callback ) ;
} ;
// foldr alias
async . foldr = async . reduceRight ;
var _filter = function ( eachfn , arr , iterator , callback ) {
var results = [ ] ;
arr = _map ( arr , function ( x , i ) {
return { index : i , value : x } ;
} ) ;
eachfn ( arr , function ( x , callback ) {
iterator ( x . value , function ( v ) {
if ( v ) {
results . push ( x ) ;
}
callback ( ) ;
} ) ;
} , function ( err ) {
callback ( _map ( results . sort ( function ( a , b ) {
return a . index - b . index ;
} ) , function ( x ) {
return x . value ;
} ) ) ;
} ) ;
} ;
async . filter = doParallel ( _filter ) ;
async . filterSeries = doSeries ( _filter ) ;
// select alias
async . select = async . filter ;
async . selectSeries = async . filterSeries ;
var _reject = function ( eachfn , arr , iterator , callback ) {
var results = [ ] ;
arr = _map ( arr , function ( x , i ) {
return { index : i , value : x } ;
} ) ;
eachfn ( arr , function ( x , callback ) {
iterator ( x . value , function ( v ) {
if ( ! v ) {
results . push ( x ) ;
}
callback ( ) ;
} ) ;
} , function ( err ) {
callback ( _map ( results . sort ( function ( a , b ) {
return a . index - b . index ;
} ) , function ( x ) {
return x . value ;
} ) ) ;
} ) ;
} ;
async . reject = doParallel ( _reject ) ;
async . rejectSeries = doSeries ( _reject ) ;
var _detect = function ( eachfn , arr , iterator , main _callback ) {
eachfn ( arr , function ( x , callback ) {
iterator ( x , function ( result ) {
if ( result ) {
main _callback ( x ) ;
main _callback = function ( ) { } ;
}
else {
callback ( ) ;
}
} ) ;
} , function ( err ) {
main _callback ( ) ;
} ) ;
} ;
async . detect = doParallel ( _detect ) ;
async . detectSeries = doSeries ( _detect ) ;
async . some = function ( arr , iterator , main _callback ) {
async . each ( arr , function ( x , callback ) {
iterator ( x , function ( v ) {
if ( v ) {
main _callback ( true ) ;
main _callback = function ( ) { } ;
}
callback ( ) ;
} ) ;
} , function ( err ) {
main _callback ( false ) ;
} ) ;
} ;
// any alias
async . any = async . some ;
async . every = function ( arr , iterator , main _callback ) {
async . each ( arr , function ( x , callback ) {
iterator ( x , function ( v ) {
if ( ! v ) {
main _callback ( false ) ;
main _callback = function ( ) { } ;
}
callback ( ) ;
} ) ;
} , function ( err ) {
main _callback ( true ) ;
} ) ;
} ;
// all alias
async . all = async . every ;
async . sortBy = function ( arr , iterator , callback ) {
async . map ( arr , function ( x , callback ) {
iterator ( x , function ( err , criteria ) {
if ( err ) {
callback ( err ) ;
}
else {
callback ( null , { value : x , criteria : criteria } ) ;
}
} ) ;
} , function ( err , results ) {
if ( err ) {
return callback ( err ) ;
}
else {
var fn = function ( left , right ) {
var a = left . criteria , b = right . criteria ;
return a < b ? - 1 : a > b ? 1 : 0 ;
} ;
callback ( null , _map ( results . sort ( fn ) , function ( x ) {
return x . value ;
} ) ) ;
}
} ) ;
} ;
async . auto = function ( tasks , callback ) {
callback = callback || function ( ) { } ;
var keys = _keys ( tasks ) ;
if ( ! keys . length ) {
return callback ( null ) ;
}
var results = { } ;
var listeners = [ ] ;
var addListener = function ( fn ) {
listeners . unshift ( fn ) ;
} ;
var removeListener = function ( fn ) {
for ( var i = 0 ; i < listeners . length ; i += 1 ) {
if ( listeners [ i ] === fn ) {
listeners . splice ( i , 1 ) ;
return ;
}
}
} ;
var taskComplete = function ( ) {
_each ( listeners . slice ( 0 ) , function ( fn ) {
fn ( ) ;
} ) ;
} ;
addListener ( function ( ) {
if ( _keys ( results ) . length === keys . length ) {
callback ( null , results ) ;
callback = function ( ) { } ;
}
} ) ;
_each ( keys , function ( k ) {
var task = ( tasks [ k ] instanceof Function ) ? [ tasks [ k ] ] : tasks [ k ] ;
var taskCallback = function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( args . length <= 1 ) {
args = args [ 0 ] ;
}
if ( err ) {
var safeResults = { } ;
_each ( _keys ( results ) , function ( rkey ) {
safeResults [ rkey ] = results [ rkey ] ;
} ) ;
safeResults [ k ] = args ;
callback ( err , safeResults ) ;
// stop subsequent errors hitting callback multiple times
callback = function ( ) { } ;
}
else {
results [ k ] = args ;
async . setImmediate ( taskComplete ) ;
}
} ;
var requires = task . slice ( 0 , Math . abs ( task . length - 1 ) ) || [ ] ;
var ready = function ( ) {
return _reduce ( requires , function ( a , x ) {
return ( a && results . hasOwnProperty ( x ) ) ;
} , true ) && ! results . hasOwnProperty ( k ) ;
} ;
if ( ready ( ) ) {
task [ task . length - 1 ] ( taskCallback , results ) ;
}
else {
var listener = function ( ) {
if ( ready ( ) ) {
removeListener ( listener ) ;
task [ task . length - 1 ] ( taskCallback , results ) ;
}
} ;
addListener ( listener ) ;
}
} ) ;
} ;
async . waterfall = function ( tasks , callback ) {
callback = callback || function ( ) { } ;
if ( tasks . constructor !== Array ) {
var err = new Error ( 'First argument to waterfall must be an array of functions' ) ;
return callback ( err ) ;
}
if ( ! tasks . length ) {
return callback ( ) ;
}
var wrapIterator = function ( iterator ) {
return function ( err ) {
if ( err ) {
callback . apply ( null , arguments ) ;
callback = function ( ) { } ;
}
else {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
var next = iterator . next ( ) ;
if ( next ) {
args . push ( wrapIterator ( next ) ) ;
}
else {
args . push ( callback ) ;
}
async . setImmediate ( function ( ) {
iterator . apply ( null , args ) ;
} ) ;
}
} ;
} ;
wrapIterator ( async . iterator ( tasks ) ) ( ) ;
} ;
var _parallel = function ( eachfn , tasks , callback ) {
callback = callback || function ( ) { } ;
if ( tasks . constructor === Array ) {
eachfn . map ( tasks , function ( fn , callback ) {
if ( fn ) {
fn ( function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( args . length <= 1 ) {
args = args [ 0 ] ;
}
callback . call ( null , err , args ) ;
} ) ;
}
} , callback ) ;
}
else {
var results = { } ;
eachfn . each ( _keys ( tasks ) , function ( k , callback ) {
tasks [ k ] ( function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( args . length <= 1 ) {
args = args [ 0 ] ;
}
results [ k ] = args ;
callback ( err ) ;
} ) ;
} , function ( err ) {
callback ( err , results ) ;
} ) ;
}
} ;
async . parallel = function ( tasks , callback ) {
_parallel ( { map : async . map , each : async . each } , tasks , callback ) ;
} ;
async . parallelLimit = function ( tasks , limit , callback ) {
_parallel ( { map : _mapLimit ( limit ) , each : _eachLimit ( limit ) } , tasks , callback ) ;
} ;
async . series = function ( tasks , callback ) {
callback = callback || function ( ) { } ;
if ( tasks . constructor === Array ) {
async . mapSeries ( tasks , function ( fn , callback ) {
if ( fn ) {
fn ( function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( args . length <= 1 ) {
args = args [ 0 ] ;
}
callback . call ( null , err , args ) ;
} ) ;
}
} , callback ) ;
}
else {
var results = { } ;
async . eachSeries ( _keys ( tasks ) , function ( k , callback ) {
tasks [ k ] ( function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( args . length <= 1 ) {
args = args [ 0 ] ;
}
results [ k ] = args ;
callback ( err ) ;
} ) ;
} , function ( err ) {
callback ( err , results ) ;
} ) ;
}
} ;
async . iterator = function ( tasks ) {
var makeCallback = function ( index ) {
var fn = function ( ) {
if ( tasks . length ) {
tasks [ index ] . apply ( null , arguments ) ;
}
return fn . next ( ) ;
} ;
fn . next = function ( ) {
return ( index < tasks . length - 1 ) ? makeCallback ( index + 1 ) : null ;
} ;
return fn ;
} ;
return makeCallback ( 0 ) ;
} ;
async . apply = function ( fn ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
return function ( ) {
return fn . apply (
null , args . concat ( Array . prototype . slice . call ( arguments ) )
) ;
} ;
} ;
var _concat = function ( eachfn , arr , fn , callback ) {
var r = [ ] ;
eachfn ( arr , function ( x , cb ) {
fn ( x , function ( err , y ) {
r = r . concat ( y || [ ] ) ;
cb ( err ) ;
} ) ;
} , function ( err ) {
callback ( err , r ) ;
} ) ;
} ;
async . concat = doParallel ( _concat ) ;
async . concatSeries = doSeries ( _concat ) ;
async . whilst = function ( test , iterator , callback ) {
if ( test ( ) ) {
iterator ( function ( err ) {
if ( err ) {
return callback ( err ) ;
}
async . whilst ( test , iterator , callback ) ;
} ) ;
}
else {
callback ( ) ;
}
} ;
async . doWhilst = function ( iterator , test , callback ) {
iterator ( function ( err ) {
if ( err ) {
return callback ( err ) ;
}
if ( test ( ) ) {
async . doWhilst ( iterator , test , callback ) ;
}
else {
callback ( ) ;
}
} ) ;
} ;
async . until = function ( test , iterator , callback ) {
if ( ! test ( ) ) {
iterator ( function ( err ) {
if ( err ) {
return callback ( err ) ;
}
async . until ( test , iterator , callback ) ;
} ) ;
}
else {
callback ( ) ;
}
} ;
async . doUntil = function ( iterator , test , callback ) {
iterator ( function ( err ) {
if ( err ) {
return callback ( err ) ;
}
if ( ! test ( ) ) {
async . doUntil ( iterator , test , callback ) ;
}
else {
callback ( ) ;
}
} ) ;
} ;
async . queue = function ( worker , concurrency ) {
if ( concurrency === undefined ) {
concurrency = 1 ;
}
function _insert ( q , data , pos , callback ) {
if ( data . constructor !== Array ) {
data = [ data ] ;
}
_each ( data , function ( task ) {
var item = {
data : task ,
callback : typeof callback === 'function' ? callback : null
} ;
if ( pos ) {
q . tasks . unshift ( item ) ;
} else {
q . tasks . push ( item ) ;
}
if ( q . saturated && q . tasks . length === concurrency ) {
q . saturated ( ) ;
}
async . setImmediate ( q . process ) ;
} ) ;
}
var workers = 0 ;
var q = {
tasks : [ ] ,
concurrency : concurrency ,
saturated : null ,
empty : null ,
drain : null ,
push : function ( data , callback ) {
_insert ( q , data , false , callback ) ;
} ,
unshift : function ( data , callback ) {
_insert ( q , data , true , callback ) ;
} ,
process : function ( ) {
if ( workers < q . concurrency && q . tasks . length ) {
var task = q . tasks . shift ( ) ;
if ( q . empty && q . tasks . length === 0 ) {
q . empty ( ) ;
}
workers += 1 ;
var next = function ( ) {
workers -= 1 ;
if ( task . callback ) {
task . callback . apply ( task , arguments ) ;
}
if ( q . drain && q . tasks . length + workers === 0 ) {
q . drain ( ) ;
}
q . process ( ) ;
} ;
var cb = only _once ( next ) ;
worker ( task . data , cb ) ;
}
} ,
length : function ( ) {
return q . tasks . length ;
} ,
running : function ( ) {
return workers ;
}
} ;
return q ;
} ;
async . cargo = function ( worker , payload ) {
var working = false ,
tasks = [ ] ;
var cargo = {
tasks : tasks ,
payload : payload ,
saturated : null ,
empty : null ,
drain : null ,
push : function ( data , callback ) {
if ( data . constructor !== Array ) {
data = [ data ] ;
}
_each ( data , function ( task ) {
tasks . push ( {
data : task ,
callback : typeof callback === 'function' ? callback : null
} ) ;
if ( cargo . saturated && tasks . length === payload ) {
cargo . saturated ( ) ;
}
} ) ;
async . setImmediate ( cargo . process ) ;
} ,
process : function process ( ) {
if ( working ) return ;
if ( tasks . length === 0 ) {
if ( cargo . drain ) cargo . drain ( ) ;
return ;
}
var ts = typeof payload === 'number'
? tasks . splice ( 0 , payload )
: tasks . splice ( 0 ) ;
var ds = _map ( ts , function ( task ) {
return task . data ;
} ) ;
if ( cargo . empty ) cargo . empty ( ) ;
working = true ;
worker ( ds , function ( ) {
working = false ;
var args = arguments ;
_each ( ts , function ( data ) {
if ( data . callback ) {
data . callback . apply ( null , args ) ;
}
} ) ;
process ( ) ;
} ) ;
} ,
length : function ( ) {
return tasks . length ;
} ,
running : function ( ) {
return working ;
}
} ;
return cargo ;
} ;
var _console _fn = function ( name ) {
return function ( fn ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
fn . apply ( null , args . concat ( [ function ( err ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ;
if ( typeof console !== 'undefined' ) {
if ( err ) {
if ( console . error ) {
console . error ( err ) ;
}
}
else if ( console [ name ] ) {
_each ( args , function ( x ) {
console [ name ] ( x ) ;
} ) ;
}
}
} ] ) ) ;
} ;
} ;
async . log = _console _fn ( 'log' ) ;
async . dir = _console _fn ( 'dir' ) ;
/ * a s y n c . i n f o = _ c o n s o l e _ f n ( ' i n f o ' ) ;
async . warn = _console _fn ( 'warn' ) ;
async . error = _console _fn ( 'error' ) ; * /
async . memoize = function ( fn , hasher ) {
var memo = { } ;
var queues = { } ;
hasher = hasher || function ( x ) {
return x ;
} ;
var memoized = function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
var callback = args . pop ( ) ;
var key = hasher . apply ( null , args ) ;
if ( key in memo ) {
callback . apply ( null , memo [ key ] ) ;
}
else if ( key in queues ) {
queues [ key ] . push ( callback ) ;
}
else {
queues [ key ] = [ callback ] ;
fn . apply ( null , args . concat ( [ function ( ) {
memo [ key ] = arguments ;
var q = queues [ key ] ;
delete queues [ key ] ;
for ( var i = 0 , l = q . length ; i < l ; i ++ ) {
q [ i ] . apply ( null , arguments ) ;
}
} ] ) ) ;
}
} ;
memoized . memo = memo ;
memoized . unmemoized = fn ;
return memoized ;
} ;
async . unmemoize = function ( fn ) {
return function ( ) {
return ( fn . unmemoized || fn ) . apply ( null , arguments ) ;
} ;
} ;
async . times = function ( count , iterator , callback ) {
var counter = [ ] ;
for ( var i = 0 ; i < count ; i ++ ) {
counter . push ( i ) ;
}
return async . map ( counter , iterator , callback ) ;
} ;
async . timesSeries = function ( count , iterator , callback ) {
var counter = [ ] ;
for ( var i = 0 ; i < count ; i ++ ) {
counter . push ( i ) ;
}
return async . mapSeries ( counter , iterator , callback ) ;
} ;
async . compose = function ( /* functions... */ ) {
var fns = Array . prototype . reverse . call ( arguments ) ;
return function ( ) {
var that = this ;
var args = Array . prototype . slice . call ( arguments ) ;
var callback = args . pop ( ) ;
async . reduce ( fns , args , function ( newargs , fn , cb ) {
fn . apply ( that , newargs . concat ( [ function ( ) {
var err = arguments [ 0 ] ;
var nextargs = Array . prototype . slice . call ( arguments , 1 ) ;
cb ( err , nextargs ) ;
} ] ) )
} ,
function ( err , results ) {
callback . apply ( that , [ err ] . concat ( results ) ) ;
} ) ;
} ;
} ;
var _applyEach = function ( eachfn , fns /*args...*/ ) {
var go = function ( ) {
var that = this ;
var args = Array . prototype . slice . call ( arguments ) ;
var callback = args . pop ( ) ;
return eachfn ( fns , function ( fn , cb ) {
fn . apply ( that , args . concat ( [ cb ] ) ) ;
} ,
callback ) ;
} ;
if ( arguments . length > 2 ) {
var args = Array . prototype . slice . call ( arguments , 2 ) ;
return go . apply ( this , args ) ;
}
else {
return go ;
}
} ;
async . applyEach = doParallel ( _applyEach ) ;
async . applyEachSeries = doSeries ( _applyEach ) ;
async . forever = function ( fn , callback ) {
function next ( err ) {
if ( err ) {
if ( callback ) {
return callback ( err ) ;
}
throw err ;
}
fn ( next ) ;
}
next ( ) ;
} ;
// AMD / RequireJS
if ( typeof define !== 'undefined' && define . amd ) {
define ( [ ] , function ( ) {
return async ;
} ) ;
}
// Node.js
else if ( typeof module !== 'undefined' && module . exports ) {
module . exports = async ;
}
// included directly via <script> tag
else {
root . async = async ;
}
} ( ) ) ;
} , { "__browserify_process" : 3 } ] , 12 : [ function ( require , module , exports ) {
module . exports . BinarySearchTree = require ( './lib/bst' ) ;
module . exports . AVLTree = require ( './lib/avltree' ) ;
} , { "./lib/avltree" : 13 , "./lib/bst" : 14 } ] , 13 : [ function ( require , module , exports ) {
/ * *
* Self - balancing binary search tree using the AVL implementation
* /
var BinarySearchTree = require ( './bst' )
, customUtils = require ( './customUtils' )
, util = require ( 'util' )
, _ = require ( 'underscore' )
;
/ * *
* Constructor
* We can ' t use a direct pointer to the root node ( as in the simple binary search tree )
* as the root will change during tree rotations
* @ param { Boolean } options . unique Whether to enforce a 'unique' constraint on the key or not
* @ param { Function } options . compareKeys Initialize this BST ' s compareKeys
* /
function AVLTree ( options ) {
this . tree = new _AVLTree ( options ) ;
}
/ * *
* Constructor of the internal AVLTree
* @ param { Object } options Optional
* @ param { Boolean } options . unique Whether to enforce a 'unique' constraint on the key or not
* @ param { Key } options . key Initialize this BST ' s key with key
* @ param { Value } options . value Initialize this BST ' s data with [ value ]
* @ param { Function } options . compareKeys Initialize this BST ' s compareKeys
* /
function _AVLTree ( options ) {
options = options || { } ;
this . left = null ;
this . right = null ;
this . parent = options . parent !== undefined ? options . parent : null ;
if ( options . hasOwnProperty ( 'key' ) ) { this . key = options . key ; }
this . data = options . hasOwnProperty ( 'value' ) ? [ options . value ] : [ ] ;
this . unique = options . unique || false ;
this . compareKeys = options . compareKeys || customUtils . defaultCompareKeysFunction ;
this . checkValueEquality = options . checkValueEquality || customUtils . defaultCheckValueEquality ;
}
/ * *
* Inherit basic functions from the basic binary search tree
* /
util . inherits ( _AVLTree , BinarySearchTree ) ;
/ * *
* Keep a pointer to the internal tree constructor for testing purposes
* /
AVLTree . _AVLTree = _AVLTree ;
/ * *
* Check the recorded height is correct for every node
* Throws if one height doesn ' t match
* /
_AVLTree . prototype . checkHeightCorrect = function ( ) {
var leftH , rightH ;
if ( ! this . hasOwnProperty ( 'key' ) ) { return ; } // Empty tree
if ( this . left && this . left . height === undefined ) { throw "Undefined height for node " + this . left . key ; }
if ( this . right && this . right . height === undefined ) { throw "Undefined height for node " + this . right . key ; }
if ( this . height === undefined ) { throw "Undefined height for node " + this . key ; }
leftH = this . left ? this . left . height : 0 ;
rightH = this . right ? this . right . height : 0 ;
if ( this . height !== 1 + Math . max ( leftH , rightH ) ) { throw "Height constraint failed for node " + this . key ; }
if ( this . left ) { this . left . checkHeightCorrect ( ) ; }
if ( this . right ) { this . right . checkHeightCorrect ( ) ; }
} ;
/ * *
* Return the balance factor
* /
_AVLTree . prototype . balanceFactor = function ( ) {
var leftH = this . left ? this . left . height : 0
, rightH = this . right ? this . right . height : 0
;
return leftH - rightH ;
} ;
/ * *
* Check that the balance factors are all between - 1 and 1
* /
_AVLTree . prototype . checkBalanceFactors = function ( ) {
if ( Math . abs ( this . balanceFactor ( ) ) > 1 ) { throw 'Tree is unbalanced at node ' + this . key ; }
if ( this . left ) { this . left . checkBalanceFactors ( ) ; }
if ( this . right ) { this . right . checkBalanceFactors ( ) ; }
} ;
/ * *
* When checking if the BST conditions are met , also check that the heights are correct
* and the tree is balanced
* /
_AVLTree . prototype . checkIsAVLT = function ( ) {
_AVLTree . super _ . prototype . checkIsBST . call ( this ) ;
this . checkHeightCorrect ( ) ;
this . checkBalanceFactors ( ) ;
} ;
AVLTree . prototype . checkIsAVLT = function ( ) { this . tree . checkIsAVLT ( ) ; } ;
/ * *
* Perform a right rotation of the tree if possible
* and return the root of the resulting tree
* The resulting tree 's nodes' heights are also updated
* /
_AVLTree . prototype . rightRotation = function ( ) {
var q = this
, p = this . left
, b
, ah , bh , ch ;
if ( ! p ) { return this ; } // No change
b = p . right ;
// Alter tree structure
if ( q . parent ) {
p . parent = q . parent ;
if ( q . parent . left === q ) { q . parent . left = p ; } else { q . parent . right = p ; }
} else {
p . parent = null ;
}
p . right = q ;
q . parent = p ;
q . left = b ;
if ( b ) { b . parent = q ; }
// Update heights
ah = p . left ? p . left . height : 0 ;
bh = b ? b . height : 0 ;
ch = q . right ? q . right . height : 0 ;
q . height = Math . max ( bh , ch ) + 1 ;
p . height = Math . max ( ah , q . height ) + 1 ;
return p ;
} ;
/ * *
* Perform a left rotation of the tree if possible
* and return the root of the resulting tree
* The resulting tree 's nodes' heights are also updated
* /
_AVLTree . prototype . leftRotation = function ( ) {
var p = this
, q = this . right
, b
, ah , bh , ch ;
if ( ! q ) { return this ; } // No change
b = q . left ;
// Alter tree structure
if ( p . parent ) {
q . parent = p . parent ;
if ( p . parent . left === p ) { p . parent . left = q ; } else { p . parent . right = q ; }
} else {
q . parent = null ;
}
q . left = p ;
p . parent = q ;
p . right = b ;
if ( b ) { b . parent = p ; }
// Update heights
ah = p . left ? p . left . height : 0 ;
bh = b ? b . height : 0 ;
ch = q . right ? q . right . height : 0 ;
p . height = Math . max ( ah , bh ) + 1 ;
q . height = Math . max ( ch , p . height ) + 1 ;
return q ;
} ;
/ * *
* Modify the tree if its right subtree is too small compared to the left
* Return the new root if any
* /
_AVLTree . prototype . rightTooSmall = function ( ) {
if ( this . balanceFactor ( ) <= 1 ) { return this ; } // Right is not too small, don't change
if ( this . left . balanceFactor ( ) < 0 ) {
this . left . leftRotation ( ) ;
}
return this . rightRotation ( ) ;
} ;
/ * *
* Modify the tree if its left subtree is too small compared to the right
* Return the new root if any
* /
_AVLTree . prototype . leftTooSmall = function ( ) {
if ( this . balanceFactor ( ) >= - 1 ) { return this ; } // Left is not too small, don't change
if ( this . right . balanceFactor ( ) > 0 ) {
this . right . rightRotation ( ) ;
}
return this . leftRotation ( ) ;
} ;
/ * *
* Rebalance the tree along the given path . The path is given reversed ( as he was calculated
* in the insert and delete functions ) .
* Returns the new root of the tree
* Of course , the first element of the path must be the root of the tree
* /
_AVLTree . prototype . rebalanceAlongPath = function ( path ) {
var newRoot = this
, rotated
, i ;
if ( ! this . hasOwnProperty ( 'key' ) ) { delete this . height ; return this ; } // Empty tree
// Rebalance the tree and update all heights
for ( i = path . length - 1 ; i >= 0 ; i -= 1 ) {
path [ i ] . height = 1 + Math . max ( path [ i ] . left ? path [ i ] . left . height : 0 , path [ i ] . right ? path [ i ] . right . height : 0 ) ;
if ( path [ i ] . balanceFactor ( ) > 1 ) {
rotated = path [ i ] . rightTooSmall ( ) ;
if ( i === 0 ) { newRoot = rotated ; }
}
if ( path [ i ] . balanceFactor ( ) < - 1 ) {
rotated = path [ i ] . leftTooSmall ( ) ;
if ( i === 0 ) { newRoot = rotated ; }
}
}
return newRoot ;
} ;
/ * *
* Insert a key , value pair in the tree while maintaining the AVL tree height constraint
* Return a pointer to the root node , which may have changed
* /
_AVLTree . prototype . insert = function ( key , value ) {
var insertPath = [ ]
, currentNode = this
;
// Empty tree, insert as root
if ( ! this . hasOwnProperty ( 'key' ) ) {
this . key = key ;
this . data . push ( value ) ;
this . height = 1 ;
return this ;
}
// Insert new leaf at the right place
while ( true ) {
// Same key: no change in the tree structure
if ( currentNode . compareKeys ( currentNode . key , key ) === 0 ) {
if ( currentNode . unique ) {
throw { message : "Can't insert key " + key + ", it violates the unique constraint"
, key : key
, errorType : 'uniqueViolated'
} ;
} else {
currentNode . data . push ( value ) ;
}
return this ;
}
insertPath . push ( currentNode ) ;
if ( currentNode . compareKeys ( key , currentNode . key ) < 0 ) {
if ( ! currentNode . left ) {
insertPath . push ( currentNode . createLeftChild ( { key : key , value : value } ) ) ;
break ;
} else {
currentNode = currentNode . left ;
}
} else {
if ( ! currentNode . right ) {
insertPath . push ( currentNode . createRightChild ( { key : key , value : value } ) ) ;
break ;
} else {
currentNode = currentNode . right ;
}
}
}
return this . rebalanceAlongPath ( insertPath ) ;
} ;
// Insert in the internal tree, update the pointer to the root if needed
AVLTree . prototype . insert = function ( key , value ) {
var newTree = this . tree . insert ( key , value ) ;
// If newTree is undefined, that means its structure was not modified
if ( newTree ) { this . tree = newTree ; }
} ;
/ * *
* Delete a key or just a value and return the new root of the tree
* @ param { Key } key
* @ param { Value } value Optional . If not set , the whole key is deleted . If set , only this value is deleted
* /
_AVLTree . prototype . delete = function ( key , value ) {
var newData = [ ] , replaceWith
, self = this
, currentNode = this
, deletePath = [ ]
;
if ( ! this . hasOwnProperty ( 'key' ) ) { return this ; } // Empty tree
// Either no match is found and the function will return from within the loop
// Or a match is found and deletePath will contain the path from the root to the node to delete after the loop
while ( true ) {
if ( currentNode . compareKeys ( key , currentNode . key ) === 0 ) { break ; }
deletePath . push ( currentNode ) ;
if ( currentNode . compareKeys ( key , currentNode . key ) < 0 ) {
if ( currentNode . left ) {
currentNode = currentNode . left ;
} else {
return this ; // Key not found, no modification
}
} else {
// currentNode.compareKeys(key, currentNode.key) is > 0
if ( currentNode . right ) {
currentNode = currentNode . right ;
} else {
return this ; // Key not found, no modification
}
}
}
// Delete only a value (no tree modification)
if ( currentNode . data . length > 1 && value ) {
currentNode . data . forEach ( function ( d ) {
if ( ! currentNode . checkValueEquality ( d , value ) ) { newData . push ( d ) ; }
} ) ;
currentNode . data = newData ;
return this ;
}
// Delete a whole node
// Leaf
if ( ! currentNode . left && ! currentNode . right ) {
if ( currentNode === this ) { // This leaf is also the root
delete currentNode . key ;
currentNode . data = [ ] ;
delete currentNode . height ;
return this ;
} else {
if ( currentNode . parent . left === currentNode ) {
currentNode . parent . left = null ;
} else {
currentNode . parent . right = null ;
}
return this . rebalanceAlongPath ( deletePath ) ;
}
}
// Node with only one child
if ( ! currentNode . left || ! currentNode . right ) {
replaceWith = currentNode . left ? currentNode . left : currentNode . right ;
if ( currentNode === this ) { // This node is also the root
replaceWith . parent = null ;
return replaceWith ; // height of replaceWith is necessarily 1 because the tree was balanced before deletion
} else {
if ( currentNode . parent . left === currentNode ) {
currentNode . parent . left = replaceWith ;
replaceWith . parent = currentNode . parent ;
} else {
currentNode . parent . right = replaceWith ;
replaceWith . parent = currentNode . parent ;
}
return this . rebalanceAlongPath ( deletePath ) ;
}
}
// Node with two children
// Use the in-order predecessor (no need to randomize since we actively rebalance)
deletePath . push ( currentNode ) ;
replaceWith = currentNode . left ;
// Special case: the in-order predecessor is right below the node to delete
if ( ! replaceWith . right ) {
currentNode . key = replaceWith . key ;
currentNode . data = replaceWith . data ;
currentNode . left = replaceWith . left ;
if ( replaceWith . left ) { replaceWith . left . parent = currentNode ; }
return this . rebalanceAlongPath ( deletePath ) ;
}
// After this loop, replaceWith is the right-most leaf in the left subtree
// and deletePath the path from the root (inclusive) to replaceWith (exclusive)
while ( true ) {
if ( replaceWith . right ) {
deletePath . push ( replaceWith ) ;
replaceWith = replaceWith . right ;
} else {
break ;
}
}
currentNode . key = replaceWith . key ;
currentNode . data = replaceWith . data ;
replaceWith . parent . right = replaceWith . left ;
if ( replaceWith . left ) { replaceWith . left . parent = replaceWith . parent ; }
return this . rebalanceAlongPath ( deletePath ) ;
} ;
// Delete a value
AVLTree . prototype . delete = function ( key , value ) {
var newTree = this . tree . delete ( key , value ) ;
// If newTree is undefined, that means its structure was not modified
if ( newTree ) { this . tree = newTree ; }
} ;
/ * *
* Other functions we want to use on an AVLTree as if it were the internal _AVLTree
* /
[ 'getNumberOfKeys' , 'search' , 'betweenBounds' , 'prettyPrint' , 'executeOnEveryNode' ] . forEach ( function ( fn ) {
AVLTree . prototype [ fn ] = function ( ) {
return this . tree [ fn ] . apply ( this . tree , arguments ) ;
} ;
} ) ;
// Interface
module . exports = AVLTree ;
} , { "./bst" : 14 , "./customUtils" : 15 , "underscore" : 16 , "util" : 2 } ] , 14 : [ function ( require , module , exports ) {
/ * *
* Simple binary search tree
* /
var customUtils = require ( './customUtils' ) ;
/ * *
* Constructor
* @ param { Object } options Optional
* @ param { Boolean } options . unique Whether to enforce a 'unique' constraint on the key or not
* @ param { Key } options . key Initialize this BST ' s key with key
* @ param { Value } options . value Initialize this BST ' s data with [ value ]
* @ param { Function } options . compareKeys Initialize this BST ' s compareKeys
* /
function BinarySearchTree ( options ) {
options = options || { } ;
this . left = null ;
this . right = null ;
this . parent = options . parent !== undefined ? options . parent : null ;
if ( options . hasOwnProperty ( 'key' ) ) { this . key = options . key ; }
this . data = options . hasOwnProperty ( 'value' ) ? [ options . value ] : [ ] ;
this . unique = options . unique || false ;
this . compareKeys = options . compareKeys || customUtils . defaultCompareKeysFunction ;
this . checkValueEquality = options . checkValueEquality || customUtils . defaultCheckValueEquality ;
}
// ================================
// Methods used to test the tree
// ================================
/ * *
* Get the descendant with max key
* /
BinarySearchTree . prototype . getMaxKeyDescendant = function ( ) {
if ( this . right ) {
return this . right . getMaxKeyDescendant ( ) ;
} else {
return this ;
}
} ;
/ * *
* Get the maximum key
* /
BinarySearchTree . prototype . getMaxKey = function ( ) {
return this . getMaxKeyDescendant ( ) . key ;
} ;
/ * *
* Get the descendant with min key
* /
BinarySearchTree . prototype . getMinKeyDescendant = function ( ) {
if ( this . left ) {
return this . left . getMinKeyDescendant ( )
} else {
return this ;
}
} ;
/ * *
* Get the minimum key
* /
BinarySearchTree . prototype . getMinKey = function ( ) {
return this . getMinKeyDescendant ( ) . key ;
} ;
/ * *
* Check that all nodes ( incl . leaves ) fullfil condition given by fn
* test is a function passed every ( key , data ) and which throws if the condition is not met
* /
BinarySearchTree . prototype . checkAllNodesFullfillCondition = function ( test ) {
if ( ! this . hasOwnProperty ( 'key' ) ) { return ; }
test ( this . key , this . data ) ;
if ( this . left ) { this . left . checkAllNodesFullfillCondition ( test ) ; }
if ( this . right ) { this . right . checkAllNodesFullfillCondition ( test ) ; }
} ;
/ * *
* Check that the core BST properties on node ordering are verified
* Throw if they aren ' t
* /
BinarySearchTree . prototype . checkNodeOrdering = function ( ) {
var self = this ;
if ( ! this . hasOwnProperty ( 'key' ) ) { return ; }
if ( this . left ) {
this . left . checkAllNodesFullfillCondition ( function ( k ) {
if ( self . compareKeys ( k , self . key ) >= 0 ) {
throw 'Tree with root ' + self . key + ' is not a binary search tree' ;
}
} ) ;
this . left . checkNodeOrdering ( ) ;
}
if ( this . right ) {
this . right . checkAllNodesFullfillCondition ( function ( k ) {
if ( self . compareKeys ( k , self . key ) <= 0 ) {
throw 'Tree with root ' + self . key + ' is not a binary search tree' ;
}
} ) ;
this . right . checkNodeOrdering ( ) ;
}
} ;
/ * *
* Check that all pointers are coherent in this tree
* /
BinarySearchTree . prototype . checkInternalPointers = function ( ) {
if ( this . left ) {
if ( this . left . parent !== this ) { throw 'Parent pointer broken for key ' + this . key ; }
this . left . checkInternalPointers ( ) ;
}
if ( this . right ) {
if ( this . right . parent !== this ) { throw 'Parent pointer broken for key ' + this . key ; }
this . right . checkInternalPointers ( ) ;
}
} ;
/ * *
* Check that a tree is a BST as defined here ( node ordering and pointer references )
* /
BinarySearchTree . prototype . checkIsBST = function ( ) {
this . checkNodeOrdering ( ) ;
this . checkInternalPointers ( ) ;
if ( this . parent ) { throw "The root shouldn't have a parent" ; }
} ;
/ * *
* Get number of keys inserted
* /
BinarySearchTree . prototype . getNumberOfKeys = function ( ) {
var res ;
if ( ! this . hasOwnProperty ( 'key' ) ) { return 0 ; }
res = 1 ;
if ( this . left ) { res += this . left . getNumberOfKeys ( ) ; }
if ( this . right ) { res += this . right . getNumberOfKeys ( ) ; }
return res ;
} ;
// ============================================
// Methods used to actually work on the tree
// ============================================
/ * *
* Create a BST similar ( i . e . same options except for key and value ) to the current one
* Use the same constructor ( i . e . BinarySearchTree , AVLTree etc )
* @ param { Object } options see constructor
* /
BinarySearchTree . prototype . createSimilar = function ( options ) {
options = options || { } ;
options . unique = this . unique ;
options . compareKeys = this . compareKeys ;
options . checkValueEquality = this . checkValueEquality ;
return new this . constructor ( options ) ;
} ;
/ * *
* Create the left child of this BST and return it
* /
BinarySearchTree . prototype . createLeftChild = function ( options ) {
var leftChild = this . createSimilar ( options ) ;
leftChild . parent = this ;
this . left = leftChild ;
return leftChild ;
} ;
/ * *
* Create the right child of this BST and return it
* /
BinarySearchTree . prototype . createRightChild = function ( options ) {
var rightChild = this . createSimilar ( options ) ;
rightChild . parent = this ;
this . right = rightChild ;
return rightChild ;
} ;
/ * *
* Insert a new element
* /
BinarySearchTree . prototype . insert = function ( key , value ) {
// Empty tree, insert as root
if ( ! this . hasOwnProperty ( 'key' ) ) {
this . key = key ;
this . data . push ( value ) ;
return ;
}
// Same key as root
if ( this . compareKeys ( this . key , key ) === 0 ) {
if ( this . unique ) {
throw { message : "Can't insert key " + key + ", it violates the unique constraint"
, key : key
, errorType : 'uniqueViolated'
} ;
} else {
this . data . push ( value ) ;
}
return ;
}
if ( this . compareKeys ( key , this . key ) < 0 ) {
// Insert in left subtree
if ( this . left ) {
this . left . insert ( key , value ) ;
} else {
this . createLeftChild ( { key : key , value : value } ) ;
}
} else {
// Insert in right subtree
if ( this . right ) {
this . right . insert ( key , value ) ;
} else {
this . createRightChild ( { key : key , value : value } ) ;
}
}
} ;
/ * *
* Search for all data corresponding to a key
* /
BinarySearchTree . prototype . search = function ( key ) {
if ( ! this . hasOwnProperty ( 'key' ) ) { return [ ] ; }
if ( this . compareKeys ( this . key , key ) === 0 ) { return this . data ; }
if ( this . compareKeys ( key , this . key ) < 0 ) {
if ( this . left ) {
return this . left . search ( key ) ;
} else {
return [ ] ;
}
} else {
if ( this . right ) {
return this . right . search ( key ) ;
} else {
return [ ] ;
}
}
} ;
/ * *
* Return a function that tells whether a given key matches a lower bound
* /
BinarySearchTree . prototype . getLowerBoundMatcher = function ( query ) {
var self = this ;
// No lower bound
if ( ! query . hasOwnProperty ( '$gt' ) && ! query . hasOwnProperty ( '$gte' ) ) {
return function ( ) { return true ; } ;
}
if ( query . hasOwnProperty ( '$gt' ) && query . hasOwnProperty ( '$gte' ) ) {
if ( self . compareKeys ( query . $gte , query . $gt ) === 0 ) {
return function ( key ) { return self . compareKeys ( key , query . $gt ) > 0 ; } ;
}
if ( self . compareKeys ( query . $gte , query . $gt ) > 0 ) {
return function ( key ) { return self . compareKeys ( key , query . $gte ) >= 0 ; } ;
} else {
return function ( key ) { return self . compareKeys ( key , query . $gt ) > 0 ; } ;
}
}
if ( query . hasOwnProperty ( '$gt' ) ) {
return function ( key ) { return self . compareKeys ( key , query . $gt ) > 0 ; } ;
} else {
return function ( key ) { return self . compareKeys ( key , query . $gte ) >= 0 ; } ;
}
} ;
/ * *
* Return a function that tells whether a given key matches an upper bound
* /
BinarySearchTree . prototype . getUpperBoundMatcher = function ( query ) {
var self = this ;
// No lower bound
if ( ! query . hasOwnProperty ( '$lt' ) && ! query . hasOwnProperty ( '$lte' ) ) {
return function ( ) { return true ; } ;
}
if ( query . hasOwnProperty ( '$lt' ) && query . hasOwnProperty ( '$lte' ) ) {
if ( self . compareKeys ( query . $lte , query . $lt ) === 0 ) {
return function ( key ) { return self . compareKeys ( key , query . $lt ) < 0 ; } ;
}
if ( self . compareKeys ( query . $lte , query . $lt ) < 0 ) {
return function ( key ) { return self . compareKeys ( key , query . $lte ) <= 0 ; } ;
} else {
return function ( key ) { return self . compareKeys ( key , query . $lt ) < 0 ; } ;
}
}
if ( query . hasOwnProperty ( '$lt' ) ) {
return function ( key ) { return self . compareKeys ( key , query . $lt ) < 0 ; } ;
} else {
return function ( key ) { return self . compareKeys ( key , query . $lte ) <= 0 ; } ;
}
} ;
// Append all elements in toAppend to array
function append ( array , toAppend ) {
var i ;
for ( i = 0 ; i < toAppend . length ; i += 1 ) {
array . push ( toAppend [ i ] ) ;
}
}
/ * *
* Get all data for a key between bounds
* Return it in key order
* @ param { Object } query Mongo - style query where keys are $lt , $lte , $gt or $gte ( other keys are not considered )
* @ param { Functions } lbm / ubm matching functions calculated at the first recursive step
* /
BinarySearchTree . prototype . betweenBounds = function ( query , lbm , ubm ) {
var res = [ ] ;
if ( ! this . hasOwnProperty ( 'key' ) ) { return [ ] ; } // Empty tree
lbm = lbm || this . getLowerBoundMatcher ( query ) ;
ubm = ubm || this . getUpperBoundMatcher ( query ) ;
if ( lbm ( this . key ) && this . left ) { append ( res , this . left . betweenBounds ( query , lbm , ubm ) ) ; }
if ( lbm ( this . key ) && ubm ( this . key ) ) { append ( res , this . data ) ; }
if ( ubm ( this . key ) && this . right ) { append ( res , this . right . betweenBounds ( query , lbm , ubm ) ) ; }
return res ;
} ;
/ * *
* Delete the current node if it is a leaf
* Return true if it was deleted
* /
BinarySearchTree . prototype . deleteIfLeaf = function ( ) {
if ( this . left || this . right ) { return false ; }
// The leaf is itself a root
if ( ! this . parent ) {
delete this . key ;
this . data = [ ] ;
return true ;
}
if ( this . parent . left === this ) {
this . parent . left = null ;
} else {
this . parent . right = null ;
}
return true ;
} ;
/ * *
* Delete the current node if it has only one child
* Return true if it was deleted
* /
BinarySearchTree . prototype . deleteIfOnlyOneChild = function ( ) {
var child ;
if ( this . left && ! this . right ) { child = this . left ; }
if ( ! this . left && this . right ) { child = this . right ; }
if ( ! child ) { return false ; }
// Root
if ( ! this . parent ) {
this . key = child . key ;
this . data = child . data ;
this . left = null ;
if ( child . left ) {
this . left = child . left ;
child . left . parent = this ;
}
this . right = null ;
if ( child . right ) {
this . right = child . right ;
child . right . parent = this ;
}
return true ;
}
if ( this . parent . left === this ) {
this . parent . left = child ;
child . parent = this . parent ;
} else {
this . parent . right = child ;
child . parent = this . parent ;
}
return true ;
} ;
/ * *
* Delete a key or just a value
* @ param { Key } key
* @ param { Value } value Optional . If not set , the whole key is deleted . If set , only this value is deleted
* /
BinarySearchTree . prototype . delete = function ( key , value ) {
var newData = [ ] , replaceWith
, self = this
;
if ( ! this . hasOwnProperty ( 'key' ) ) { return ; }
if ( this . compareKeys ( key , this . key ) < 0 ) {
if ( this . left ) { this . left . delete ( key , value ) ; }
return ;
}
if ( this . compareKeys ( key , this . key ) > 0 ) {
if ( this . right ) { this . right . delete ( key , value ) ; }
return ;
}
if ( ! this . compareKeys ( key , this . key ) === 0 ) { return ; }
// Delete only a value
if ( this . data . length > 1 && value !== undefined ) {
this . data . forEach ( function ( d ) {
if ( ! self . checkValueEquality ( d , value ) ) { newData . push ( d ) ; }
} ) ;
self . data = newData ;
return ;
}
// Delete the whole node
if ( this . deleteIfLeaf ( ) ) {
return ;
}
if ( this . deleteIfOnlyOneChild ( ) ) {
return ;
}
// We are in the case where the node to delete has two children
if ( Math . random ( ) >= 0.5 ) { // Randomize replacement to avoid unbalancing the tree too much
// Use the in-order predecessor
replaceWith = this . left . getMaxKeyDescendant ( ) ;
this . key = replaceWith . key ;
this . data = replaceWith . data ;
if ( this === replaceWith . parent ) { // Special case
this . left = replaceWith . left ;
if ( replaceWith . left ) { replaceWith . left . parent = replaceWith . parent ; }
} else {
replaceWith . parent . right = replaceWith . left ;
if ( replaceWith . left ) { replaceWith . left . parent = replaceWith . parent ; }
}
} else {
// Use the in-order successor
replaceWith = this . right . getMinKeyDescendant ( ) ;
this . key = replaceWith . key ;
this . data = replaceWith . data ;
if ( this === replaceWith . parent ) { // Special case
this . right = replaceWith . right ;
if ( replaceWith . right ) { replaceWith . right . parent = replaceWith . parent ; }
} else {
replaceWith . parent . left = replaceWith . right ;
if ( replaceWith . right ) { replaceWith . right . parent = replaceWith . parent ; }
}
}
} ;
/ * *
* Execute a function on every node of the tree , in key order
* @ param { Function } fn Signature : node . Most useful will probably be node . key and node . data
* /
BinarySearchTree . prototype . executeOnEveryNode = function ( fn ) {
if ( this . left ) { this . left . executeOnEveryNode ( fn ) ; }
fn ( this ) ;
if ( this . right ) { this . right . executeOnEveryNode ( fn ) ; }
} ;
/ * *
* Pretty print a tree
* @ param { Boolean } printData To print the nodes ' data along with the key
* /
BinarySearchTree . prototype . prettyPrint = function ( printData , spacing ) {
spacing = spacing || "" ;
console . log ( spacing + "* " + this . key ) ;
if ( printData ) { console . log ( spacing + "* " + this . data ) ; }
if ( ! this . left && ! this . right ) { return ; }
if ( this . left ) {
this . left . prettyPrint ( printData , spacing + " " ) ;
} else {
console . log ( spacing + " *" ) ;
}
if ( this . right ) {
this . right . prettyPrint ( printData , spacing + " " ) ;
} else {
console . log ( spacing + " *" ) ;
}
} ;
// Interface
module . exports = BinarySearchTree ;
} , { "./customUtils" : 15 } ] , 15 : [ function ( require , module , exports ) {
/ * *
* Return an array with the numbers from 0 to n - 1 , in a random order
* /
function getRandomArray ( n ) {
var res , next ;
if ( n === 0 ) { return [ ] ; }
if ( n === 1 ) { return [ 0 ] ; }
res = getRandomArray ( n - 1 ) ;
next = Math . floor ( Math . random ( ) * n ) ;
res . splice ( next , 0 , n - 1 ) ; // Add n-1 at a random position in the array
return res ;
} ;
module . exports . getRandomArray = getRandomArray ;
/ *
* Default compareKeys function will work for numbers , strings and dates
* /
function defaultCompareKeysFunction ( a , b ) {
if ( a < b ) { return - 1 ; }
if ( a > b ) { return 1 ; }
if ( a === b ) { return 0 ; }
throw { message : "Couldn't compare elements" , a : a , b : b } ;
}
module . exports . defaultCompareKeysFunction = defaultCompareKeysFunction ;
/ * *
* Check whether two values are equal ( used in non - unique deletion )
* /
function defaultCheckValueEquality ( a , b ) {
return a === b ;
}
module . exports . defaultCheckValueEquality = defaultCheckValueEquality ;
} , { } ] , 16 : [ function ( require , module , exports ) {
// Underscore.js 1.4.4
// http://underscorejs.org
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
( function ( ) {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this ;
// Save the previous value of the `_` variable.
var previousUnderscore = root . _ ;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = { } ;
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array . prototype , ObjProto = Object . prototype , FuncProto = Function . prototype ;
// Create quick reference variables for speed access to core prototypes.
var push = ArrayProto . push ,
slice = ArrayProto . slice ,
concat = ArrayProto . concat ,
toString = ObjProto . toString ,
hasOwnProperty = ObjProto . hasOwnProperty ;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto . forEach ,
nativeMap = ArrayProto . map ,
nativeReduce = ArrayProto . reduce ,
nativeReduceRight = ArrayProto . reduceRight ,
nativeFilter = ArrayProto . filter ,
nativeEvery = ArrayProto . every ,
nativeSome = ArrayProto . some ,
nativeIndexOf = ArrayProto . indexOf ,
nativeLastIndexOf = ArrayProto . lastIndexOf ,
nativeIsArray = Array . isArray ,
nativeKeys = Object . keys ,
nativeBind = FuncProto . bind ;
// Create a safe reference to the Underscore object for use below.
var _ = function ( obj ) {
if ( obj instanceof _ ) return obj ;
if ( ! ( this instanceof _ ) ) return new _ ( obj ) ;
this . _wrapped = obj ;
} ;
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if ( typeof exports !== 'undefined' ) {
if ( typeof module !== 'undefined' && module . exports ) {
exports = module . exports = _ ;
}
exports . _ = _ ;
} else {
root . _ = _ ;
}
// Current version.
_ . VERSION = '1.4.4' ;
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _ . each = _ . forEach = function ( obj , iterator , context ) {
if ( obj == null ) return ;
if ( nativeForEach && obj . forEach === nativeForEach ) {
obj . forEach ( iterator , context ) ;
} else if ( obj . length === + obj . length ) {
for ( var i = 0 , l = obj . length ; i < l ; i ++ ) {
if ( iterator . call ( context , obj [ i ] , i , obj ) === breaker ) return ;
}
} else {
for ( var key in obj ) {
if ( _ . has ( obj , key ) ) {
if ( iterator . call ( context , obj [ key ] , key , obj ) === breaker ) return ;
}
}
}
} ;
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_ . map = _ . collect = function ( obj , iterator , context ) {
var results = [ ] ;
if ( obj == null ) return results ;
if ( nativeMap && obj . map === nativeMap ) return obj . map ( iterator , context ) ;
each ( obj , function ( value , index , list ) {
results [ results . length ] = iterator . call ( context , value , index , list ) ;
} ) ;
return results ;
} ;
var reduceError = 'Reduce of empty array with no initial value' ;
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_ . reduce = _ . foldl = _ . inject = function ( obj , iterator , memo , context ) {
var initial = arguments . length > 2 ;
if ( obj == null ) obj = [ ] ;
if ( nativeReduce && obj . reduce === nativeReduce ) {
if ( context ) iterator = _ . bind ( iterator , context ) ;
return initial ? obj . reduce ( iterator , memo ) : obj . reduce ( iterator ) ;
}
each ( obj , function ( value , index , list ) {
if ( ! initial ) {
memo = value ;
initial = true ;
} else {
memo = iterator . call ( context , memo , value , index , list ) ;
}
} ) ;
if ( ! initial ) throw new TypeError ( reduceError ) ;
return memo ;
} ;
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_ . reduceRight = _ . foldr = function ( obj , iterator , memo , context ) {
var initial = arguments . length > 2 ;
if ( obj == null ) obj = [ ] ;
if ( nativeReduceRight && obj . reduceRight === nativeReduceRight ) {
if ( context ) iterator = _ . bind ( iterator , context ) ;
return initial ? obj . reduceRight ( iterator , memo ) : obj . reduceRight ( iterator ) ;
}
var length = obj . length ;
if ( length !== + length ) {
var keys = _ . keys ( obj ) ;
length = keys . length ;
}
each ( obj , function ( value , index , list ) {
index = keys ? keys [ -- length ] : -- length ;
if ( ! initial ) {
memo = obj [ index ] ;
initial = true ;
} else {
memo = iterator . call ( context , memo , obj [ index ] , index , list ) ;
}
} ) ;
if ( ! initial ) throw new TypeError ( reduceError ) ;
return memo ;
} ;
// Return the first value which passes a truth test. Aliased as `detect`.
_ . find = _ . detect = function ( obj , iterator , context ) {
var result ;
any ( obj , function ( value , index , list ) {
if ( iterator . call ( context , value , index , list ) ) {
result = value ;
return true ;
}
} ) ;
return result ;
} ;
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_ . filter = _ . select = function ( obj , iterator , context ) {
var results = [ ] ;
if ( obj == null ) return results ;
if ( nativeFilter && obj . filter === nativeFilter ) return obj . filter ( iterator , context ) ;
each ( obj , function ( value , index , list ) {
if ( iterator . call ( context , value , index , list ) ) results [ results . length ] = value ;
} ) ;
return results ;
} ;
// Return all the elements for which a truth test fails.
_ . reject = function ( obj , iterator , context ) {
return _ . filter ( obj , function ( value , index , list ) {
return ! iterator . call ( context , value , index , list ) ;
} , context ) ;
} ;
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_ . every = _ . all = function ( obj , iterator , context ) {
iterator || ( iterator = _ . identity ) ;
var result = true ;
if ( obj == null ) return result ;
if ( nativeEvery && obj . every === nativeEvery ) return obj . every ( iterator , context ) ;
each ( obj , function ( value , index , list ) {
if ( ! ( result = result && iterator . call ( context , value , index , list ) ) ) return breaker ;
} ) ;
return ! ! result ;
} ;
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _ . some = _ . any = function ( obj , iterator , context ) {
iterator || ( iterator = _ . identity ) ;
var result = false ;
if ( obj == null ) return result ;
if ( nativeSome && obj . some === nativeSome ) return obj . some ( iterator , context ) ;
each ( obj , function ( value , index , list ) {
if ( result || ( result = iterator . call ( context , value , index , list ) ) ) return breaker ;
} ) ;
return ! ! result ;
} ;
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_ . contains = _ . include = function ( obj , target ) {
if ( obj == null ) return false ;
if ( nativeIndexOf && obj . indexOf === nativeIndexOf ) return obj . indexOf ( target ) != - 1 ;
return any ( obj , function ( value ) {
return value === target ;
} ) ;
} ;
// Invoke a method (with arguments) on every item in a collection.
_ . invoke = function ( obj , method ) {
var args = slice . call ( arguments , 2 ) ;
var isFunc = _ . isFunction ( method ) ;
return _ . map ( obj , function ( value ) {
return ( isFunc ? method : value [ method ] ) . apply ( value , args ) ;
} ) ;
} ;
// Convenience version of a common use case of `map`: fetching a property.
_ . pluck = function ( obj , key ) {
return _ . map ( obj , function ( value ) { return value [ key ] ; } ) ;
} ;
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_ . where = function ( obj , attrs , first ) {
if ( _ . isEmpty ( attrs ) ) return first ? null : [ ] ;
return _ [ first ? 'find' : 'filter' ] ( obj , function ( value ) {
for ( var key in attrs ) {
if ( attrs [ key ] !== value [ key ] ) return false ;
}
return true ;
} ) ;
} ;
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_ . findWhere = function ( obj , attrs ) {
return _ . where ( obj , attrs , true ) ;
} ;
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
_ . max = function ( obj , iterator , context ) {
if ( ! iterator && _ . isArray ( obj ) && obj [ 0 ] === + obj [ 0 ] && obj . length < 65535 ) {
return Math . max . apply ( Math , obj ) ;
}
if ( ! iterator && _ . isEmpty ( obj ) ) return - Infinity ;
var result = { computed : - Infinity , value : - Infinity } ;
each ( obj , function ( value , index , list ) {
var computed = iterator ? iterator . call ( context , value , index , list ) : value ;
computed >= result . computed && ( result = { value : value , computed : computed } ) ;
} ) ;
return result . value ;
} ;
// Return the minimum element (or element-based computation).
_ . min = function ( obj , iterator , context ) {
if ( ! iterator && _ . isArray ( obj ) && obj [ 0 ] === + obj [ 0 ] && obj . length < 65535 ) {
return Math . min . apply ( Math , obj ) ;
}
if ( ! iterator && _ . isEmpty ( obj ) ) return Infinity ;
var result = { computed : Infinity , value : Infinity } ;
each ( obj , function ( value , index , list ) {
var computed = iterator ? iterator . call ( context , value , index , list ) : value ;
computed < result . computed && ( result = { value : value , computed : computed } ) ;
} ) ;
return result . value ;
} ;
// Shuffle an array.
_ . shuffle = function ( obj ) {
var rand ;
var index = 0 ;
var shuffled = [ ] ;
each ( obj , function ( value ) {
rand = _ . random ( index ++ ) ;
shuffled [ index - 1 ] = shuffled [ rand ] ;
shuffled [ rand ] = value ;
} ) ;
return shuffled ;
} ;
// An internal function to generate lookup iterators.
var lookupIterator = function ( value ) {
return _ . isFunction ( value ) ? value : function ( obj ) { return obj [ value ] ; } ;
} ;
// Sort the object's values by a criterion produced by an iterator.
_ . sortBy = function ( obj , value , context ) {
var iterator = lookupIterator ( value ) ;
return _ . pluck ( _ . map ( obj , function ( value , index , list ) {
return {
value : value ,
index : index ,
criteria : iterator . call ( context , value , index , list )
} ;
} ) . sort ( function ( left , right ) {
var a = left . criteria ;
var b = right . criteria ;
if ( a !== b ) {
if ( a > b || a === void 0 ) return 1 ;
if ( a < b || b === void 0 ) return - 1 ;
}
return left . index < right . index ? - 1 : 1 ;
} ) , 'value' ) ;
} ;
// An internal function used for aggregate "group by" operations.
var group = function ( obj , value , context , behavior ) {
var result = { } ;
var iterator = lookupIterator ( value || _ . identity ) ;
each ( obj , function ( value , index ) {
var key = iterator . call ( context , value , index , obj ) ;
behavior ( result , key , value ) ;
} ) ;
return result ;
} ;
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_ . groupBy = function ( obj , value , context ) {
return group ( obj , value , context , function ( result , key , value ) {
( _ . has ( result , key ) ? result [ key ] : ( result [ key ] = [ ] ) ) . push ( value ) ;
} ) ;
} ;
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_ . countBy = function ( obj , value , context ) {
return group ( obj , value , context , function ( result , key ) {
if ( ! _ . has ( result , key ) ) result [ key ] = 0 ;
result [ key ] ++ ;
} ) ;
} ;
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_ . sortedIndex = function ( array , obj , iterator , context ) {
iterator = iterator == null ? _ . identity : lookupIterator ( iterator ) ;
var value = iterator . call ( context , obj ) ;
var low = 0 , high = array . length ;
while ( low < high ) {
var mid = ( low + high ) >>> 1 ;
iterator . call ( context , array [ mid ] ) < value ? low = mid + 1 : high = mid ;
}
return low ;
} ;
// Safely convert anything iterable into a real, live array.
_ . toArray = function ( obj ) {
if ( ! obj ) return [ ] ;
if ( _ . isArray ( obj ) ) return slice . call ( obj ) ;
if ( obj . length === + obj . length ) return _ . map ( obj , _ . identity ) ;
return _ . values ( obj ) ;
} ;
// Return the number of elements in an object.
_ . size = function ( obj ) {
if ( obj == null ) return 0 ;
return ( obj . length === + obj . length ) ? obj . length : _ . keys ( obj ) . length ;
} ;
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_ . first = _ . head = _ . take = function ( array , n , guard ) {
if ( array == null ) return void 0 ;
return ( n != null ) && ! guard ? slice . call ( array , 0 , n ) : array [ 0 ] ;
} ;
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_ . initial = function ( array , n , guard ) {
return slice . call ( array , 0 , array . length - ( ( n == null ) || guard ? 1 : n ) ) ;
} ;
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_ . last = function ( array , n , guard ) {
if ( array == null ) return void 0 ;
if ( ( n != null ) && ! guard ) {
return slice . call ( array , Math . max ( array . length - n , 0 ) ) ;
} else {
return array [ array . length - 1 ] ;
}
} ;
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_ . rest = _ . tail = _ . drop = function ( array , n , guard ) {
return slice . call ( array , ( n == null ) || guard ? 1 : n ) ;
} ;
// Trim out all falsy values from an array.
_ . compact = function ( array ) {
return _ . filter ( array , _ . identity ) ;
} ;
// Internal implementation of a recursive `flatten` function.
var flatten = function ( input , shallow , output ) {
each ( input , function ( value ) {
if ( _ . isArray ( value ) ) {
shallow ? push . apply ( output , value ) : flatten ( value , shallow , output ) ;
} else {
output . push ( value ) ;
}
} ) ;
return output ;
} ;
// Return a completely flattened version of an array.
_ . flatten = function ( array , shallow ) {
return flatten ( array , shallow , [ ] ) ;
} ;
// Return a version of the array that does not contain the specified value(s).
_ . without = function ( array ) {
return _ . difference ( array , slice . call ( arguments , 1 ) ) ;
} ;
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_ . uniq = _ . unique = function ( array , isSorted , iterator , context ) {
if ( _ . isFunction ( isSorted ) ) {
context = iterator ;
iterator = isSorted ;
isSorted = false ;
}
var initial = iterator ? _ . map ( array , iterator , context ) : array ;
var results = [ ] ;
var seen = [ ] ;
each ( initial , function ( value , index ) {
if ( isSorted ? ( ! index || seen [ seen . length - 1 ] !== value ) : ! _ . contains ( seen , value ) ) {
seen . push ( value ) ;
results . push ( array [ index ] ) ;
}
} ) ;
return results ;
} ;
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_ . union = function ( ) {
return _ . uniq ( concat . apply ( ArrayProto , arguments ) ) ;
} ;
// Produce an array that contains every item shared between all the
// passed-in arrays.
_ . intersection = function ( array ) {
var rest = slice . call ( arguments , 1 ) ;
return _ . filter ( _ . uniq ( array ) , function ( item ) {
return _ . every ( rest , function ( other ) {
return _ . indexOf ( other , item ) >= 0 ;
} ) ;
} ) ;
} ;
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_ . difference = function ( array ) {
var rest = concat . apply ( ArrayProto , slice . call ( arguments , 1 ) ) ;
return _ . filter ( array , function ( value ) { return ! _ . contains ( rest , value ) ; } ) ;
} ;
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_ . zip = function ( ) {
var args = slice . call ( arguments ) ;
var length = _ . max ( _ . pluck ( args , 'length' ) ) ;
var results = new Array ( length ) ;
for ( var i = 0 ; i < length ; i ++ ) {
results [ i ] = _ . pluck ( args , "" + i ) ;
}
return results ;
} ;
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_ . object = function ( list , values ) {
if ( list == null ) return { } ;
var result = { } ;
for ( var i = 0 , l = list . length ; i < l ; i ++ ) {
if ( values ) {
result [ list [ i ] ] = values [ i ] ;
} else {
result [ list [ i ] [ 0 ] ] = list [ i ] [ 1 ] ;
}
}
return result ;
} ;
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_ . indexOf = function ( array , item , isSorted ) {
if ( array == null ) return - 1 ;
var i = 0 , l = array . length ;
if ( isSorted ) {
if ( typeof isSorted == 'number' ) {
i = ( isSorted < 0 ? Math . max ( 0 , l + isSorted ) : isSorted ) ;
} else {
i = _ . sortedIndex ( array , item ) ;
return array [ i ] === item ? i : - 1 ;
}
}
if ( nativeIndexOf && array . indexOf === nativeIndexOf ) return array . indexOf ( item , isSorted ) ;
for ( ; i < l ; i ++ ) if ( array [ i ] === item ) return i ;
return - 1 ;
} ;
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_ . lastIndexOf = function ( array , item , from ) {
if ( array == null ) return - 1 ;
var hasIndex = from != null ;
if ( nativeLastIndexOf && array . lastIndexOf === nativeLastIndexOf ) {
return hasIndex ? array . lastIndexOf ( item , from ) : array . lastIndexOf ( item ) ;
}
var i = ( hasIndex ? from : array . length ) ;
while ( i -- ) if ( array [ i ] === item ) return i ;
return - 1 ;
} ;
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_ . range = function ( start , stop , step ) {
if ( arguments . length <= 1 ) {
stop = start || 0 ;
start = 0 ;
}
step = arguments [ 2 ] || 1 ;
var len = Math . max ( Math . ceil ( ( stop - start ) / step ) , 0 ) ;
var idx = 0 ;
var range = new Array ( len ) ;
while ( idx < len ) {
range [ idx ++ ] = start ;
start += step ;
}
return range ;
} ;
// Function (ahem) Functions
// ------------------
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_ . bind = function ( func , context ) {
if ( func . bind === nativeBind && nativeBind ) return nativeBind . apply ( func , slice . call ( arguments , 1 ) ) ;
var args = slice . call ( arguments , 2 ) ;
return function ( ) {
return func . apply ( context , args . concat ( slice . call ( arguments ) ) ) ;
} ;
} ;
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
_ . partial = function ( func ) {
var args = slice . call ( arguments , 1 ) ;
return function ( ) {
return func . apply ( this , args . concat ( slice . call ( arguments ) ) ) ;
} ;
} ;
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_ . bindAll = function ( obj ) {
var funcs = slice . call ( arguments , 1 ) ;
if ( funcs . length === 0 ) funcs = _ . functions ( obj ) ;
each ( funcs , function ( f ) { obj [ f ] = _ . bind ( obj [ f ] , obj ) ; } ) ;
return obj ;
} ;
// Memoize an expensive function by storing its results.
_ . memoize = function ( func , hasher ) {
var memo = { } ;
hasher || ( hasher = _ . identity ) ;
return function ( ) {
var key = hasher . apply ( this , arguments ) ;
return _ . has ( memo , key ) ? memo [ key ] : ( memo [ key ] = func . apply ( this , arguments ) ) ;
} ;
} ;
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_ . delay = function ( func , wait ) {
var args = slice . call ( arguments , 2 ) ;
return setTimeout ( function ( ) { return func . apply ( null , args ) ; } , wait ) ;
} ;
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_ . defer = function ( func ) {
return _ . delay . apply ( _ , [ func , 1 ] . concat ( slice . call ( arguments , 1 ) ) ) ;
} ;
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_ . throttle = function ( func , wait ) {
var context , args , timeout , result ;
var previous = 0 ;
var later = function ( ) {
previous = new Date ;
timeout = null ;
result = func . apply ( context , args ) ;
} ;
return function ( ) {
var now = new Date ;
var remaining = wait - ( now - previous ) ;
context = this ;
args = arguments ;
if ( remaining <= 0 ) {
clearTimeout ( timeout ) ;
timeout = null ;
previous = now ;
result = func . apply ( context , args ) ;
} else if ( ! timeout ) {
timeout = setTimeout ( later , remaining ) ;
}
return result ;
} ;
} ;
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_ . debounce = function ( func , wait , immediate ) {
var timeout , result ;
return function ( ) {
var context = this , args = arguments ;
var later = function ( ) {
timeout = null ;
if ( ! immediate ) result = func . apply ( context , args ) ;
} ;
var callNow = immediate && ! timeout ;
clearTimeout ( timeout ) ;
timeout = setTimeout ( later , wait ) ;
if ( callNow ) result = func . apply ( context , args ) ;
return result ;
} ;
} ;
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_ . once = function ( func ) {
var ran = false , memo ;
return function ( ) {
if ( ran ) return memo ;
ran = true ;
memo = func . apply ( this , arguments ) ;
func = null ;
return memo ;
} ;
} ;
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_ . wrap = function ( func , wrapper ) {
return function ( ) {
var args = [ func ] ;
push . apply ( args , arguments ) ;
return wrapper . apply ( this , args ) ;
} ;
} ;
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_ . compose = function ( ) {
var funcs = arguments ;
return function ( ) {
var args = arguments ;
for ( var i = funcs . length - 1 ; i >= 0 ; i -- ) {
args = [ funcs [ i ] . apply ( this , args ) ] ;
}
return args [ 0 ] ;
} ;
} ;
// Returns a function that will only be executed after being called N times.
_ . after = function ( times , func ) {
if ( times <= 0 ) return func ( ) ;
return function ( ) {
if ( -- times < 1 ) {
return func . apply ( this , arguments ) ;
}
} ;
} ;
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_ . keys = nativeKeys || function ( obj ) {
if ( obj !== Object ( obj ) ) throw new TypeError ( 'Invalid object' ) ;
var keys = [ ] ;
for ( var key in obj ) if ( _ . has ( obj , key ) ) keys [ keys . length ] = key ;
return keys ;
} ;
// Retrieve the values of an object's properties.
_ . values = function ( obj ) {
var values = [ ] ;
for ( var key in obj ) if ( _ . has ( obj , key ) ) values . push ( obj [ key ] ) ;
return values ;
} ;
// Convert an object into a list of `[key, value]` pairs.
_ . pairs = function ( obj ) {
var pairs = [ ] ;
for ( var key in obj ) if ( _ . has ( obj , key ) ) pairs . push ( [ key , obj [ key ] ] ) ;
return pairs ;
} ;
// Invert the keys and values of an object. The values must be serializable.
_ . invert = function ( obj ) {
var result = { } ;
for ( var key in obj ) if ( _ . has ( obj , key ) ) result [ obj [ key ] ] = key ;
return result ;
} ;
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_ . functions = _ . methods = function ( obj ) {
var names = [ ] ;
for ( var key in obj ) {
if ( _ . isFunction ( obj [ key ] ) ) names . push ( key ) ;
}
return names . sort ( ) ;
} ;
// Extend a given object with all the properties in passed-in object(s).
_ . extend = function ( obj ) {
each ( slice . call ( arguments , 1 ) , function ( source ) {
if ( source ) {
for ( var prop in source ) {
obj [ prop ] = source [ prop ] ;
}
}
} ) ;
return obj ;
} ;
// Return a copy of the object only containing the whitelisted properties.
_ . pick = function ( obj ) {
var copy = { } ;
var keys = concat . apply ( ArrayProto , slice . call ( arguments , 1 ) ) ;
each ( keys , function ( key ) {
if ( key in obj ) copy [ key ] = obj [ key ] ;
} ) ;
return copy ;
} ;
// Return a copy of the object without the blacklisted properties.
_ . omit = function ( obj ) {
var copy = { } ;
var keys = concat . apply ( ArrayProto , slice . call ( arguments , 1 ) ) ;
for ( var key in obj ) {
if ( ! _ . contains ( keys , key ) ) copy [ key ] = obj [ key ] ;
}
return copy ;
} ;
// Fill in a given object with default properties.
_ . defaults = function ( obj ) {
each ( slice . call ( arguments , 1 ) , function ( source ) {
if ( source ) {
for ( var prop in source ) {
if ( obj [ prop ] == null ) obj [ prop ] = source [ prop ] ;
}
}
} ) ;
return obj ;
} ;
// Create a (shallow-cloned) duplicate of an object.
_ . clone = function ( obj ) {
if ( ! _ . isObject ( obj ) ) return obj ;
return _ . isArray ( obj ) ? obj . slice ( ) : _ . extend ( { } , obj ) ;
} ;
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_ . tap = function ( obj , interceptor ) {
interceptor ( obj ) ;
return obj ;
} ;
// Internal recursive comparison function for `isEqual`.
var eq = function ( a , b , aStack , bStack ) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if ( a === b ) return a !== 0 || 1 / a == 1 / b ;
// A strict comparison is necessary because `null == undefined`.
if ( a == null || b == null ) return a === b ;
// Unwrap any wrapped objects.
if ( a instanceof _ ) a = a . _wrapped ;
if ( b instanceof _ ) b = b . _wrapped ;
// Compare `[[Class]]` names.
var className = toString . call ( a ) ;
if ( className != toString . call ( b ) ) return false ;
switch ( className ) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]' :
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String ( b ) ;
case '[object Number]' :
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != + a ? b != + b : ( a == 0 ? 1 / a == 1 / b : a == + b ) ;
case '[object Date]' :
case '[object Boolean]' :
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return + a == + b ;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]' :
return a . source == b . source &&
a . global == b . global &&
a . multiline == b . multiline &&
a . ignoreCase == b . ignoreCase ;
}
if ( typeof a != 'object' || typeof b != 'object' ) return false ;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack . length ;
while ( length -- ) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if ( aStack [ length ] == a ) return bStack [ length ] == b ;
}
// Add the first object to the stack of traversed objects.
aStack . push ( a ) ;
bStack . push ( b ) ;
var size = 0 , result = true ;
// Recursively compare objects and arrays.
if ( className == '[object Array]' ) {
// Compare array lengths to determine if a deep comparison is necessary.
size = a . length ;
result = size == b . length ;
if ( result ) {
// Deep compare the contents, ignoring non-numeric properties.
while ( size -- ) {
if ( ! ( result = eq ( a [ size ] , b [ size ] , aStack , bStack ) ) ) break ;
}
}
} else {
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a . constructor , bCtor = b . constructor ;
if ( aCtor !== bCtor && ! ( _ . isFunction ( aCtor ) && ( aCtor instanceof aCtor ) &&
_ . isFunction ( bCtor ) && ( bCtor instanceof bCtor ) ) ) {
return false ;
}
// Deep compare objects.
for ( var key in a ) {
if ( _ . has ( a , key ) ) {
// Count the expected number of properties.
size ++ ;
// Deep compare each member.
if ( ! ( result = _ . has ( b , key ) && eq ( a [ key ] , b [ key ] , aStack , bStack ) ) ) break ;
}
}
// Ensure that both objects contain the same number of properties.
if ( result ) {
for ( key in b ) {
if ( _ . has ( b , key ) && ! ( size -- ) ) break ;
}
result = ! size ;
}
}
// Remove the first object from the stack of traversed objects.
aStack . pop ( ) ;
bStack . pop ( ) ;
return result ;
} ;
// Perform a deep comparison to check if two objects are equal.
_ . isEqual = function ( a , b ) {
return eq ( a , b , [ ] , [ ] ) ;
} ;
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_ . isEmpty = function ( obj ) {
if ( obj == null ) return true ;
if ( _ . isArray ( obj ) || _ . isString ( obj ) ) return obj . length === 0 ;
for ( var key in obj ) if ( _ . has ( obj , key ) ) return false ;
return true ;
} ;
// Is a given value a DOM element?
_ . isElement = function ( obj ) {
return ! ! ( obj && obj . nodeType === 1 ) ;
} ;
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_ . isArray = nativeIsArray || function ( obj ) {
return toString . call ( obj ) == '[object Array]' ;
} ;
// Is a given variable an object?
_ . isObject = function ( obj ) {
return obj === Object ( obj ) ;
} ;
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each ( [ 'Arguments' , 'Function' , 'String' , 'Number' , 'Date' , 'RegExp' ] , function ( name ) {
_ [ 'is' + name ] = function ( obj ) {
return toString . call ( obj ) == '[object ' + name + ']' ;
} ;
} ) ;
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if ( ! _ . isArguments ( arguments ) ) {
_ . isArguments = function ( obj ) {
return ! ! ( obj && _ . has ( obj , 'callee' ) ) ;
} ;
}
// Optimize `isFunction` if appropriate.
if ( typeof ( /./ ) !== 'function' ) {
_ . isFunction = function ( obj ) {
return typeof obj === 'function' ;
} ;
}
// Is a given object a finite number?
_ . isFinite = function ( obj ) {
return isFinite ( obj ) && ! isNaN ( parseFloat ( obj ) ) ;
} ;
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_ . isNaN = function ( obj ) {
return _ . isNumber ( obj ) && obj != + obj ;
} ;
// Is a given value a boolean?
_ . isBoolean = function ( obj ) {
return obj === true || obj === false || toString . call ( obj ) == '[object Boolean]' ;
} ;
// Is a given value equal to null?
_ . isNull = function ( obj ) {
return obj === null ;
} ;
// Is a given variable undefined?
_ . isUndefined = function ( obj ) {
return obj === void 0 ;
} ;
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_ . has = function ( obj , key ) {
return hasOwnProperty . call ( obj , key ) ;
} ;
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_ . noConflict = function ( ) {
root . _ = previousUnderscore ;
return this ;
} ;
// Keep the identity function around for default iterators.
_ . identity = function ( value ) {
return value ;
} ;
// Run a function **n** times.
_ . times = function ( n , iterator , context ) {
var accum = Array ( n ) ;
for ( var i = 0 ; i < n ; i ++ ) accum [ i ] = iterator . call ( context , i ) ;
return accum ;
} ;
// Return a random integer between min and max (inclusive).
_ . random = function ( min , max ) {
if ( max == null ) {
max = min ;
min = 0 ;
}
return min + Math . floor ( Math . random ( ) * ( max - min + 1 ) ) ;
} ;
// List of HTML entities for escaping.
var entityMap = {
escape : {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : ''' ,
'/' : '/'
}
} ;
entityMap . unescape = _ . invert ( entityMap . escape ) ;
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape : new RegExp ( '[' + _ . keys ( entityMap . escape ) . join ( '' ) + ']' , 'g' ) ,
unescape : new RegExp ( '(' + _ . keys ( entityMap . unescape ) . join ( '|' ) + ')' , 'g' )
} ;
// Functions for escaping and unescaping strings to/from HTML interpolation.
_ . each ( [ 'escape' , 'unescape' ] , function ( method ) {
_ [ method ] = function ( string ) {
if ( string == null ) return '' ;
return ( '' + string ) . replace ( entityRegexes [ method ] , function ( match ) {
return entityMap [ method ] [ match ] ;
} ) ;
} ;
} ) ;
// If the value of the named property is a function then invoke it;
// otherwise, return it.
_ . result = function ( object , property ) {
if ( object == null ) return null ;
var value = object [ property ] ;
return _ . isFunction ( value ) ? value . call ( object ) : value ;
} ;
// Add your own custom functions to the Underscore object.
_ . mixin = function ( obj ) {
each ( _ . functions ( obj ) , function ( name ) {
var func = _ [ name ] = obj [ name ] ;
_ . prototype [ name ] = function ( ) {
var args = [ this . _wrapped ] ;
push . apply ( args , arguments ) ;
return result . call ( this , func . apply ( _ , args ) ) ;
} ;
} ) ;
} ;
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0 ;
_ . uniqueId = function ( prefix ) {
var id = ++ idCounter + '' ;
return prefix ? prefix + id : id ;
} ;
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_ . templateSettings = {
evaluate : /<%([\s\S]+?)%>/g ,
interpolate : /<%=([\s\S]+?)%>/g ,
escape : /<%-([\s\S]+?)%>/g
} ;
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/ ;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'" : "'" ,
'\\' : '\\' ,
'\r' : 'r' ,
'\n' : 'n' ,
'\t' : 't' ,
'\u2028' : 'u2028' ,
'\u2029' : 'u2029'
} ;
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g ;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_ . template = function ( text , data , settings ) {
var render ;
settings = _ . defaults ( { } , settings , _ . templateSettings ) ;
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp ( [
( settings . escape || noMatch ) . source ,
( settings . interpolate || noMatch ) . source ,
( settings . evaluate || noMatch ) . source
] . join ( '|' ) + '|$' , 'g' ) ;
// Compile the template source, escaping string literals appropriately.
var index = 0 ;
var source = "__p+='" ;
text . replace ( matcher , function ( match , escape , interpolate , evaluate , offset ) {
source += text . slice ( index , offset )
. replace ( escaper , function ( match ) { return '\\' + escapes [ match ] ; } ) ;
if ( escape ) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" ;
}
if ( interpolate ) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" ;
}
if ( evaluate ) {
source += "';\n" + evaluate + "\n__p+='" ;
}
index = offset + match . length ;
return match ;
} ) ;
source += "';\n" ;
// If a variable is not specified, place data values in local scope.
if ( ! settings . variable ) source = 'with(obj||{}){\n' + source + '}\n' ;
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n" ;
try {
render = new Function ( settings . variable || 'obj' , '_' , source ) ;
} catch ( e ) {
e . source = source ;
throw e ;
}
if ( data ) return render ( data , _ ) ;
var template = function ( data ) {
return render . call ( this , data , _ ) ;
} ;
// Provide the compiled function source as a convenience for precompilation.
template . source = 'function(' + ( settings . variable || 'obj' ) + '){\n' + source + '}' ;
return template ;
} ;
// Add a "chain" function, which will delegate to the wrapper.
_ . chain = function ( obj ) {
return _ ( obj ) . chain ( ) ;
} ;
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function ( obj ) {
return this . _chain ? _ ( obj ) . chain ( ) : obj ;
} ;
// Add all of the Underscore functions to the wrapper object.
_ . mixin ( _ ) ;
// Add all mutator Array functions to the wrapper.
each ( [ 'pop' , 'push' , 'reverse' , 'shift' , 'sort' , 'splice' , 'unshift' ] , function ( name ) {
var method = ArrayProto [ name ] ;
_ . prototype [ name ] = function ( ) {
var obj = this . _wrapped ;
method . apply ( obj , arguments ) ;
if ( ( name == 'shift' || name == 'splice' ) && obj . length === 0 ) delete obj [ 0 ] ;
return result . call ( this , obj ) ;
} ;
} ) ;
// Add all accessor Array functions to the wrapper.
each ( [ 'concat' , 'join' , 'slice' ] , function ( name ) {
var method = ArrayProto [ name ] ;
_ . prototype [ name ] = function ( ) {
return result . call ( this , method . apply ( this . _wrapped , arguments ) ) ;
} ;
} ) ;
_ . extend ( _ . prototype , {
// Start chaining a wrapped Underscore object.
chain : function ( ) {
this . _chain = true ;
return this ;
} ,
// Extracts the result from a wrapped and chained object.
value : function ( ) {
return this . _wrapped ;
}
} ) ;
} ) . call ( this ) ;
} , { } ] } , { } , [ 6 ] ) ( 6 )
} ) ;
;