mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-10-31 02:50:08 +00:00 
			
		
		
		
	
		
			
	
	
		
			547 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			547 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | /********************************************************************** | ||
|  | *  | ||
|  | * | ||
|  | * | ||
|  | * XXX move .zip(..) here from diff.js | ||
|  | * XXX do we need .at(..) / .to(..) methods here and in Map/Set/...??? | ||
|  | * | ||
|  | * | ||
|  | **********************************************/  /* c8 ignore next 2 */ | ||
|  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | ||
|  | (function(require){ var module={} // make module AMD/node compatible...
 | ||
|  | /*********************************************************************/ | ||
|  | 
 | ||
|  | var object = require('ig-object') | ||
|  | var stoppable = require('ig-stoppable') | ||
|  | 
 | ||
|  | var generator = require('./generator') | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /*********************************************************************/ | ||
|  | 
 | ||
|  | // NOTE: this is used in a similar fashion to Python's StopIteration...
 | ||
|  | var STOP = | ||
|  | module.STOP = | ||
|  | 	stoppable.STOP | ||
|  | 
 | ||
|  | 
 | ||
|  | //---------------------------------------------------------------------
 | ||
|  | // Mixins...
 | ||
|  | 
 | ||
|  | // Wrap .map(..) / .filter(..) / .reduce(..) / .. to support STOP...
 | ||
|  | // 
 | ||
|  | // NOTE: internally these are implemented as for-of loops (./generator.js)
 | ||
|  | var stoppableList = function(iter){ | ||
|  | 	return function(func){ | ||
|  | 		return [...this.iter()[iter](...arguments)] } } | ||
|  | var stoppableValue = function(iter, no_return=false){ | ||
|  | 	return function(func){ | ||
|  | 		var res = this.iter()[iter](...arguments)  | ||
|  | 		return no_return ? | ||
|  | 			undefined | ||
|  | 			: res } } | ||
|  | 
 | ||
|  | 
 | ||
|  | // Equivalent to .map(..) / .filter(..) / .reduce(..) that process the 
 | ||
|  | // contents in chunks asynchronously...
 | ||
|  | //
 | ||
|  | //	.mapChunks(func)
 | ||
|  | //	.mapChunks(chunk_size, func)
 | ||
|  | //	.mapChunks([item_handler, chunk_handler])
 | ||
|  | //	.mapChunks(chunk_size, [item_handler, chunk_handler])
 | ||
|  | //		-> promise(list)
 | ||
|  | //	
 | ||
|  | //	.filterChunks(func)
 | ||
|  | //	.filterChunks(chunk_size, func)
 | ||
|  | //	.filterChunks([item_handler, chunk_handler])
 | ||
|  | //	.filterChunks(chunk_size, [item_handler, chunk_handler])
 | ||
|  | //		-> promise(list)
 | ||
|  | //	
 | ||
|  | //	.reduceChunks(func, res)
 | ||
|  | //	.reduceChunks(chunk_size, func, res)
 | ||
|  | //	.reduceChunks([item_handler, chunk_handler], res)
 | ||
|  | //	.reduceChunks(chunk_size, [item_handler, chunk_handler], res)
 | ||
|  | //		-> promise(res)
 | ||
|  | //
 | ||
|  | //
 | ||
|  | //	chunk_handler(chunk, result, offset)
 | ||
|  | //
 | ||
|  | //
 | ||
|  | // chunk_size can be:
 | ||
|  | // 	20			- chunk size
 | ||
|  | // 	'20'		- chunk size
 | ||
|  | // 	'20C'		- number of chunks
 | ||
|  | //
 | ||
|  | //
 | ||
|  | // STOP can be thrown in func or chunk_handler at any time to 
 | ||
|  | // abort iteration, this will resolve the promise.
 | ||
|  | //	
 | ||
|  | //
 | ||
|  | // The main goal of this is to not block the runtime while processing a 
 | ||
|  | // very long array by interrupting the processing with a timeout...
 | ||
|  | //
 | ||
|  | // XXX should these return a partial result on STOP?
 | ||
|  | // XXX add generators:
 | ||
|  | // 			.map(..) / .filter(..) / .reduce(..)
 | ||
|  | // 		...the basis here should be the chunks, i.e. each cycle should
 | ||
|  | // 		go through a chunk...
 | ||
|  | //		...the mixin can be generic, i.e. applicable to Array, and 
 | ||
|  | //		other stuff...
 | ||
|  | // XXX add time-based chunk iteration...
 | ||
|  | var makeChunkIter = function(iter, wrapper){ | ||
|  | 	wrapper = wrapper | ||
|  | 		|| function(res, func, array, e){ | ||
|  | 			return func.call(this, e[1], e[0], array) } | ||
|  | 	return function(size, func, ...rest){ | ||
|  | 		var that = this | ||
|  | 		var args = [...arguments] | ||
|  | 		size = (args[0] instanceof Function  | ||
|  | 				|| args[0] instanceof Array) ?  | ||
|  | 			(this.CHUNK_SIZE || 50) | ||
|  | 			: args.shift() | ||
|  | 		size = typeof(size) == typeof('str') ? | ||
|  | 				// number of chunks...
 | ||
|  | 				(size.trim().endsWith('c') || size.trim().endsWith('C') ? | ||
|  | 				 	Math.round(this.length / (parseInt(size) || 1)) || 1 | ||
|  | 				: parseInt(size)) | ||
|  | 			: size | ||
|  | 		var postChunk | ||
|  | 		func = args.shift() | ||
|  | 		;[func, postChunk] = func instanceof Array ? func : [func] | ||
|  | 		rest = args | ||
|  | 
 | ||
|  | 		// special case...
 | ||
|  | 		// no need to setTimeout(..) if smaller than size...
 | ||
|  | 		if(this.length <= size){ | ||
|  | 			try { | ||
|  | 				// handle iteration...
 | ||
|  | 				var res = this[iter](func, ...rest) | ||
|  | 				// handle chunk...
 | ||
|  | 				postChunk | ||
|  | 					&& postChunk.call(this, this, res, 0)  | ||
|  | 				return Promise.all(res)  | ||
|  | 			// handle STOP...
 | ||
|  | 			} catch(err){ | ||
|  | 				if(err === STOP){ | ||
|  | 					return Promise.resolve()  | ||
|  | 				} else if( err instanceof STOP){ | ||
|  | 					return Promise.resolve(err.value) } | ||
|  | 				throw err } } | ||
|  | 
 | ||
|  | 		var res = [] | ||
|  | 		var _wrapper = wrapper.bind(this, res, func, this) | ||
|  | 
 | ||
|  | 		return new Promise(function(resolve, reject){ | ||
|  | 				var next = function(chunks){ | ||
|  | 					setTimeout(function(){ | ||
|  | 						var chunk, val | ||
|  | 						try { | ||
|  | 							// handle iteration...
 | ||
|  | 							res.push( | ||
|  | 								val = (chunk = chunks.shift()) | ||
|  | 									[iter](_wrapper, ...rest)) | ||
|  | 							// handle chunk...
 | ||
|  | 							postChunk | ||
|  | 								&& postChunk.call(that,  | ||
|  | 									chunk.map(function([i, v]){ return v }),  | ||
|  | 									val, | ||
|  | 									chunk[0][0]) | ||
|  | 						// handle STOP...
 | ||
|  | 						} catch(err){ | ||
|  | 							if(err === STOP){ | ||
|  | 								return resolve()  | ||
|  | 							} else if( err instanceof STOP){ | ||
|  | 								return resolve(err.value) } | ||
|  | 							throw err } | ||
|  | 
 | ||
|  | 						// stop condition...
 | ||
|  | 						chunks.length == 0 ? | ||
|  | 							resolve(res.flat(2)) | ||
|  | 							: next(chunks) }, 0) } | ||
|  | 				next(that | ||
|  | 					// split the array into chunks...
 | ||
|  | 					.reduce(function(res, e, i){ | ||
|  | 						var c = res.slice(-1)[0] | ||
|  | 						c.length >= size ? | ||
|  | 							// initial element in chunk...
 | ||
|  | 							res.push([[i, e]]) | ||
|  | 							// rest...
 | ||
|  | 							: c.push([i, e]) | ||
|  | 						return res }, [[]])) }) } } | ||
|  | 
 | ||
|  | 
 | ||
|  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | ||
|  | 
 | ||
|  | var ArrayMixin = | ||
|  | module.ArrayMixin = | ||
|  | object.Mixin('ArrayMixin', 'soft', { | ||
|  | 	STOP: object.STOP, | ||
|  | 
 | ||
|  | 	// 	zip(array, array, ...)
 | ||
|  | 	// 		-> [[item, item, ...], ...]
 | ||
|  | 	//
 | ||
|  | 	// 	zip(func, array, array, ...)
 | ||
|  | 	// 		-> [func(i, [item, item, ...]), ...]
 | ||
|  | 	//
 | ||
|  | 	zip: function(func, ...arrays){ | ||
|  | 		var i = arrays[0] instanceof Array ?  | ||
|  | 			0  | ||
|  | 			: arrays.shift() | ||
|  | 		if(func instanceof Array){ | ||
|  | 			arrays.splice(0, 0, func) | ||
|  | 			func = null } | ||
|  | 		// build the zip item...
 | ||
|  | 		// NOTE: this is done this way to preserve array sparseness...
 | ||
|  | 		var s = arrays | ||
|  | 			.reduce( | ||
|  | 				function(res, a, j){ | ||
|  | 					//a.length > i
 | ||
|  | 					i in a | ||
|  | 						&& (res[j] = a[i]) | ||
|  | 					return res },  | ||
|  | 				new Array(arrays.length)) | ||
|  | 		return arrays | ||
|  | 				// check that at least one array is longer than i...
 | ||
|  | 				.reduce(function(res, a){  | ||
|  | 					return Math.max(res, i, a.length) }, 0) > i ? | ||
|  | 			// collect zip item...
 | ||
|  | 			[func ? func(i, s) : s] | ||
|  | 				// get next...
 | ||
|  | 				.concat(this.zip(func, i+1, ...arrays)) | ||
|  | 			// done...
 | ||
|  | 			: [] }, | ||
|  | 
 | ||
|  | 	// XXX not sure about the handler API here yet... 
 | ||
|  | 	iter: function*(lst=[], handler=undefined){ | ||
|  | 		yield* lst.iter( | ||
|  | 			...(handler ?  | ||
|  | 				[handler]  | ||
|  | 				: [])) }, | ||
|  | }) | ||
|  | 
 | ||
|  | 
 | ||
|  | var ArrayProtoMixin = | ||
|  | module.ArrayProtoMixin = | ||
|  | object.Mixin('ArrayProtoMixin', 'soft', { | ||
|  | 
 | ||
|  | 	// A faster version of .indexOf(..)
 | ||
|  | 	//
 | ||
|  | 	// NOTE: this is not faster when looking for an item not in this,
 | ||
|  | 	// 		for some reason the native .includes(..) and .indexOf(..)
 | ||
|  | 	// 		search for non existant elements about an order of magnirude 
 | ||
|  | 	// 		faster than if it existed...
 | ||
|  | 	// 		...the funny thing is that at least on Crome .lastIndexOf(..)
 | ||
|  | 	// 		is about as fast as this for an item in about the same relative 
 | ||
|  | 	// 		location...
 | ||
|  | 	// NOTE: this will get depricated as soon as JS redoes its .indexOf(..)
 | ||
|  | 	index: function(value){ | ||
|  | 		for(var i = 0; i < this.length && this[i] !== value; i++){} | ||
|  | 		return i == this.length ? -1 : i }, | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// first/last element access short-hands...
 | ||
|  | 	//
 | ||
|  | 	//	.first()
 | ||
|  | 	//	.last()
 | ||
|  | 	//		-> elem
 | ||
|  | 	//
 | ||
|  | 	//	.first(value)
 | ||
|  | 	//	.last(value)
 | ||
|  | 	//		-> array
 | ||
|  | 	//
 | ||
|  | 	// NOTE: setting a value will overwrite an existing first/last value.
 | ||
|  | 	// NOTE: for an empty array both .first(..)/.last(..) will return undefined 
 | ||
|  | 	// 		when getting a value and set the 0'th value when setting...
 | ||
|  | 	// NOTE: decided to keep these as methods and not props because methods
 | ||
|  | 	// 		have one advantage: they can be chained
 | ||
|  | 	// 		...while you can't chain assignment unless you wrap it in .run(..)
 | ||
|  | 	first: function(value){ | ||
|  | 		return arguments.length > 0 ? | ||
|  | 			((this[0] = value), this) | ||
|  | 			: this[0]}, | ||
|  | 	last: function(value){ | ||
|  | 		return arguments.length > 0 ? | ||
|  | 			((this[Math.max(this.length - 1, 0)] = value), this) | ||
|  | 			: this[this.length - 1]}, | ||
|  | 
 | ||
|  | 	// Roll left/right (in-place)...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: to .rol(..) left just pass a negative n value...
 | ||
|  | 	// NOTE: we can't use ...[..] for sparse arrays as the will expand undefined
 | ||
|  | 	// 		inplace of empty positions, this is thereason the .splice(..) 
 | ||
|  | 	// 		implementation was replaced by a less clear (but faster)
 | ||
|  | 	// 		.copyWithin(..) version...
 | ||
|  | 	rol: function(n=1){ | ||
|  | 		var l = this.length | ||
|  | 		n = (n >= 0 ?  | ||
|  | 				n  | ||
|  | 				: l - n) | ||
|  | 			% l | ||
|  | 		if(n != 0){ | ||
|  | 			this.length += n | ||
|  | 			this.copyWithin(l, 0, n) | ||
|  | 			this.splice(0, n) } | ||
|  | 		return this }, | ||
|  | 
 | ||
|  | 	// Compact a sparse array...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: this will not compact in-place.
 | ||
|  | 	compact: function(){ | ||
|  | 		return this | ||
|  | 			.filter(function(){ return true }) }, | ||
|  | 
 | ||
|  | 	// Remove sprse slots form start/end/both ends of array...
 | ||
|  | 	//
 | ||
|  | 	trim: function(){ | ||
|  | 		var l = this.length | ||
|  | 		var i = 0 | ||
|  | 		while(!(i in this) && i < l){ i++ } | ||
|  | 		var j = 0 | ||
|  | 		while(!(l-j-1 in this) && j < l){ j++ } | ||
|  | 		return this.slice(i, j == 0 ? l : -j) }, | ||
|  | 	trimStart: function(){ | ||
|  | 		var l = this.length | ||
|  | 		var i = 0 | ||
|  | 		while(!(i in this) && i < l){ i++ } | ||
|  | 		return this.slice(i) }, | ||
|  | 	trimEnd: function(){ | ||
|  | 		var l = this.length | ||
|  | 		var j = 0 | ||
|  | 		while(!(l-j-1 in this) && j < l){ j++ } | ||
|  | 		return this.slice(0, j == 0 ? l : -j) }, | ||
|  | 
 | ||
|  | 	// like .length but for sparse arrays will return the element count...
 | ||
|  | 	get len(){ | ||
|  | 		// NOTE: if we don't do .slice() here this can count array 
 | ||
|  | 		// 		instance attributes...
 | ||
|  | 		// NOTE: .slice() has an added menifit here of removing any 
 | ||
|  | 		// 		attributes from the count...
 | ||
|  | 		return Object.keys(this.slice()).length }, | ||
|  | 
 | ||
|  | 	// Return a new array with duplicate elements removed...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: order is preserved... 
 | ||
|  | 	unique: function(normalize){ | ||
|  | 		return normalize ?  | ||
|  | 			[...new Map(this | ||
|  | 					.map(function(e){  | ||
|  | 						return [normalize(e), e] })) | ||
|  | 				.values()] | ||
|  | 			// NOTE: we are calling .compact() here to avoid creating 
 | ||
|  | 			// 		undefined items from empty slots in sparse arrays...
 | ||
|  | 			: [...new Set(this.compact())] }, | ||
|  | 	tailUnique: function(normalize){ | ||
|  | 		return this | ||
|  | 			.slice() | ||
|  | 			.reverse() | ||
|  | 			.unique(normalize) | ||
|  | 			.reverse() }, | ||
|  | 
 | ||
|  | 	// Compare two arrays...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: this is diffectent from Object.match(..) in that this compares 
 | ||
|  | 	// 		self to other (internal) while match compares two entities 
 | ||
|  | 	// 		externally.
 | ||
|  | 	// 		XXX not sure if we need the destinction in name, will have to 
 | ||
|  | 	// 			come back to this when refactoring diff.js -- all three 
 | ||
|  | 	// 			have to be similar...
 | ||
|  | 	cmp: function(other){ | ||
|  | 		if(this === other){ | ||
|  | 			return true } | ||
|  | 		if(this.length != other.length){ | ||
|  | 			return false } | ||
|  | 		for(var i=0; i<this.length; i++){ | ||
|  | 			if(this[i] != other[i]){ | ||
|  | 				return false } } | ||
|  | 		return true }, | ||
|  | 
 | ||
|  | 	// Compare two Arrays as sets...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: this will ignore order and repeating elments...
 | ||
|  | 	setCmp: function(other){ | ||
|  | 		return this === other  | ||
|  | 			|| (new Set([...this, ...other])).length  | ||
|  | 				== (new Set(this)).length }, | ||
|  | 
 | ||
|  | 	// Sort as the other array...
 | ||
|  | 	//
 | ||
|  | 	// 	Sort as array placing the sorted items at head...
 | ||
|  | 	// 	.sortAs(array)
 | ||
|  | 	// 	.sortAs(array, 'head')
 | ||
|  | 	// 		-> sorted
 | ||
|  | 	//
 | ||
|  | 	// 	Sort as array placing the sorted items at tail...
 | ||
|  | 	// 	.sortAs(array, 'tail')
 | ||
|  | 	// 		-> sorted
 | ||
|  | 	//
 | ||
|  | 	// This will sort the intersecting items in the head keeping the rest 
 | ||
|  | 	// of the items in the same relative order...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: if an item is in the array multiple times only the first 
 | ||
|  | 	// 		index is used...
 | ||
|  | 	//
 | ||
|  | 	// XXX should this extend/patch .sort(..)???
 | ||
|  | 	// 		...currently do not see a clean way to do this without 
 | ||
|  | 	// 		extending and replacing Array or directly re-wrapping 
 | ||
|  | 	// 		.sort(..)...
 | ||
|  | 	sortAs: function(other, place='head'){ | ||
|  | 		place = place == 'tail' ? -1 : 1  | ||
|  | 		// NOTE: the memory overhead here is better than the time overhead 
 | ||
|  | 		// 		when using .indexOf(..)...
 | ||
|  | 		other = other.toMap() | ||
|  | 		var orig = this.toMap() | ||
|  | 		return this.sort(function(a, b){ | ||
|  | 			var i = other.get(a) | ||
|  | 			var j = other.get(b) | ||
|  | 			return i == null && j == null ? | ||
|  | 					orig.get(a) - orig.get(b) | ||
|  | 				: i == null ?  | ||
|  | 					place | ||
|  | 				: j == null ?  | ||
|  | 					-place | ||
|  | 				: i - j }) }, | ||
|  | 
 | ||
|  | 	/*/ XXX EXPERIMENTAL... | ||
|  | 	//		...if this is successful then merge this with .sortAs(..)
 | ||
|  | 	sort: function(cmp){ | ||
|  | 		return (arguments.length == 0 || typeof(cmp) == 'function') ? | ||
|  | 			object.parentCall(ArrayProtoMixin.data.sort, this, ...arguments) | ||
|  | 			: this.sortAs(...arguments) }, | ||
|  | 	//*/
 | ||
|  | 
 | ||
|  | 	// Same as .sortAs(..) but will not change indexes of items not in other...
 | ||
|  | 	//
 | ||
|  | 	// Example:
 | ||
|  | 	// 		['a', 3, 'b', 1, 2, 'c']
 | ||
|  | 	// 			.inplaceSortAs([1, 2, 3, 3]) // -> ['a', 1, 'b', 2, 3, 'c']
 | ||
|  | 	//
 | ||
|  | 	inplaceSortAs: function(other){ | ||
|  | 		// sort only the intersection...
 | ||
|  | 		var sorted = this | ||
|  | 			.filter(function(e){  | ||
|  | 				return other.includes(e) }) | ||
|  | 			.sortAs(other) | ||
|  | 		// "zip" the sorted items back into this...
 | ||
|  | 		this.forEach(function(e, i, l){ | ||
|  | 			other.includes(e)  | ||
|  | 				&& (l[i] = sorted.shift()) }) | ||
|  | 		return this }, | ||
|  | 
 | ||
|  | 	// Convert an array to object...
 | ||
|  | 	//
 | ||
|  | 	// Format:
 | ||
|  | 	// 	{
 | ||
|  | 	// 		<item>: <index>,
 | ||
|  | 	// 		...
 | ||
|  | 	// 	}
 | ||
|  | 	//
 | ||
|  | 	// NOTE: items should be strings, other types will get converted to 
 | ||
|  | 	// 		strings and thus may mess things up.
 | ||
|  | 	// NOTE: this will forget repeating items...
 | ||
|  | 	// NOTE: normalize will slow things down...
 | ||
|  | 	toKeys: function(normalize){ | ||
|  | 		return normalize ?  | ||
|  | 			this.reduce(function(r, e, i){ | ||
|  | 				r[normalize(e, i)] = i | ||
|  | 				return r }, {}) | ||
|  | 			: this.reduce(function(r, e, i){ | ||
|  | 				r[e] = i | ||
|  | 				return r }, {}) }, | ||
|  | 
 | ||
|  | 	// Convert an array to a map...
 | ||
|  | 	//
 | ||
|  | 	// This is similar to Array.prototype.toKeys(..) but does not 
 | ||
|  | 	// restrict value type to string.
 | ||
|  | 	//
 | ||
|  | 	// Format:
 | ||
|  | 	// 	Map([
 | ||
|  | 	// 		[<item>, <index>],
 | ||
|  | 	// 		...
 | ||
|  | 	// 	])
 | ||
|  | 	//
 | ||
|  | 	// NOTE: this will forget repeating items...
 | ||
|  | 	// NOTE: normalize will slow things down...
 | ||
|  | 	toMap: function(normalize){ | ||
|  | 		return normalize ?  | ||
|  | 			this | ||
|  | 				.reduce(function(m, e, i){ | ||
|  | 					m.set(normalize(e, i), i) | ||
|  | 					return m }, new Map()) | ||
|  | 			: this | ||
|  | 				.reduce(function(m, e, i){ | ||
|  | 					m.set(e, i) | ||
|  | 					return m }, new Map()) }, | ||
|  | 
 | ||
|  | 	// XXX would be nice for this to use the instance .zip(..) in recursion...
 | ||
|  | 	// 		...this might be done by reversign the current implementation, i.e.
 | ||
|  | 	// 		for instance .zip(..) to be the main implementation and for 
 | ||
|  | 	// 		Array.zip(..) to be a proxy to that...
 | ||
|  | 	zip: function(func, ...arrays){ | ||
|  | 		return func instanceof Array ? | ||
|  | 			this.constructor.zip(this, func, ...arrays) | ||
|  | 			: this.constructor.zip(func, this, ...arrays) }, | ||
|  | 
 | ||
|  | 	// get iterator over array...
 | ||
|  | 	//
 | ||
|  | 	//	Array.iter()
 | ||
|  | 	//	Array.iter([ .. ])
 | ||
|  | 	//		-> iterator
 | ||
|  | 	//
 | ||
|  | 	//	array.iter()
 | ||
|  | 	//		-> iterator
 | ||
|  | 	//
 | ||
|  | 	// XXX should this take an argument and be like map??
 | ||
|  | 	// XXX this should handle throwing STOP!!!
 | ||
|  | 	// 		...might also ne a good idea to isolate the STOP mechanics 
 | ||
|  | 	// 		into a spearate module/package...
 | ||
|  | 	iter: stoppable(function*(handler=undefined){ | ||
|  | 		if(handler){ | ||
|  | 			var i = 0 | ||
|  | 			for(var e of this){ | ||
|  | 				var res = handler.call(this, e, i++)  | ||
|  | 				// treat non-iterables as single elements...
 | ||
|  | 				if(typeof(res) == 'object'  | ||
|  | 						&& Symbol.iterator in res){ | ||
|  | 					yield* res | ||
|  | 				} else { | ||
|  | 					yield res } }  | ||
|  | 		} else { | ||
|  | 			yield* this }}), | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// Stoppable iteration...
 | ||
|  | 	//
 | ||
|  | 	// NOTE: internally these are generators...
 | ||
|  | 	smap: stoppableList('map'), | ||
|  | 	sfilter: stoppableList('filter'), | ||
|  | 	sreduce: stoppableValue('reduce'), | ||
|  | 	sreduceRight: stoppableValue('reduceRight'), | ||
|  | 	sforEach: stoppableValue('map', true), | ||
|  | 
 | ||
|  | 	// Chunk iteration...
 | ||
|  | 	//
 | ||
|  | 	CHUNK_SIZE: 50, | ||
|  | 	mapChunks: makeChunkIter('map'), | ||
|  | 	filterChunks: makeChunkIter('map',  | ||
|  | 		function(res, func, array, e){ | ||
|  | 			return !!func.call(this, e[1], e[0], array) ? [e[1]] : [] }), | ||
|  | 	reduceChunks: makeChunkIter('reduce', | ||
|  | 		function(total, func, array, res, e){ | ||
|  | 			return func.call(this,  | ||
|  | 				total.length > 0 ?  | ||
|  | 					total.pop()  | ||
|  | 					: res,  | ||
|  | 				e[1], e[0], array) }), | ||
|  | }) | ||
|  | 
 | ||
|  | 
 | ||
|  | ArrayMixin(Array) | ||
|  | ArrayProtoMixin(Array.prototype) | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /********************************************************************** | ||
|  | * vim:set ts=4 sw=4 :                               */ return module }) |