mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-11-04 04:50:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			212 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**********************************************************************
 | 
						|
* 
 | 
						|
*
 | 
						|
*
 | 
						|
***********************************************/ /* c8 ignore next 2 */
 | 
						|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | 
						|
(function(require){ var module={} // make module AMD/node compatible...
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
require('./Map')
 | 
						|
 | 
						|
var object = require('ig-object')
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
var UniqueKeyMap = 
 | 
						|
module.UniqueKeyMap = 
 | 
						|
object.Constructor('UniqueKeyMap', Map, {
 | 
						|
 | 
						|
	// Format:
 | 
						|
	// 	Map([
 | 
						|
	// 		[ <elem>, Set([
 | 
						|
	// 				<original-name>, 
 | 
						|
	// 				...
 | 
						|
	// 			]) ],
 | 
						|
	// 		...
 | 
						|
	// 	])
 | 
						|
	//
 | 
						|
	__keys_index: null,
 | 
						|
	get __keys(){
 | 
						|
		return (this.__keys_index = 
 | 
						|
			this.__keys_index || new Map()) },
 | 
						|
 | 
						|
	// Format:
 | 
						|
	// 	Map([
 | 
						|
	// 		[<unique-key>, <orig-key>],
 | 
						|
	// 		...
 | 
						|
	// 	])
 | 
						|
	//
 | 
						|
	__reverse_index: null,
 | 
						|
	get __reverse(){
 | 
						|
		return (this.__reverse_index = 
 | 
						|
			this.__reverse_index || new Map()) },
 | 
						|
 | 
						|
 | 
						|
	// Pattern to be used to generate unique key...
 | 
						|
	//
 | 
						|
	__key_pattern__: '$KEY ($COUNT)',
 | 
						|
 | 
						|
	// If true then a value can not be stored under the same key more 
 | 
						|
	// than once...
 | 
						|
	//
 | 
						|
	// Example:
 | 
						|
	// 	var u = UniqueKeyMap()
 | 
						|
	// 	u.set('x', 123)
 | 
						|
	// 	// if .__unique_key_value__ is true this will have no effect, 
 | 
						|
	// 	// otherwise 123 will be stored under 'x (1)'
 | 
						|
	// 	u.set('x', 123)
 | 
						|
	//
 | 
						|
	__unique_key_value__: false,
 | 
						|
 | 
						|
 | 
						|
 | 
						|
	// helpers...
 | 
						|
	//
 | 
						|
	originalKey: function(key){
 | 
						|
		return this.__reverse.get(key) },
 | 
						|
	uniqieKey: function(key){
 | 
						|
		var n = key
 | 
						|
		var i = 0
 | 
						|
		while(this.has(n)){
 | 
						|
			i++
 | 
						|
			n = this.__key_pattern__
 | 
						|
				.replace(/\$KEY/, key)
 | 
						|
				.replace(/\$COUNT/, i) }
 | 
						|
		return n },
 | 
						|
	keysOf: function(elem, mode='original'){
 | 
						|
		// get unique keys...
 | 
						|
		if(mode == 'unique'){
 | 
						|
			return this
 | 
						|
				.entries()
 | 
						|
				.reduce(function(res, [k, e]){
 | 
						|
					e === elem
 | 
						|
						&& res.push(k)
 | 
						|
					return res }, []) }
 | 
						|
		// get keys used to set the values...
 | 
						|
		return [...(this.__keys.get(elem) || [])] },
 | 
						|
 | 
						|
	// NOTE: this will never overwrite a key's value, to overwrite use .reset(..)
 | 
						|
	set: function(key, elem, return_key=false){
 | 
						|
		// index...
 | 
						|
		var names
 | 
						|
		this.__keys.set(elem, 
 | 
						|
			names = this.__keys.get(elem) || new Set())
 | 
						|
		// key/elem already exists...
 | 
						|
		if(this.__unique_key_value__ 
 | 
						|
				&& names.has(key)){
 | 
						|
			return return_key ?
 | 
						|
				key
 | 
						|
				: this }
 | 
						|
		names.add(key)
 | 
						|
		// add the elem with the unique name...
 | 
						|
		var n
 | 
						|
		var res = object.parentCall(
 | 
						|
			UniqueKeyMap.prototype, 
 | 
						|
			'set', 
 | 
						|
			this, 
 | 
						|
			n = this.uniqieKey(key), 
 | 
						|
			elem) 
 | 
						|
		// reverse index...
 | 
						|
		this.__reverse.set(n, key)
 | 
						|
		return return_key ?
 | 
						|
			n
 | 
						|
			: res },
 | 
						|
	// NOTE: this will never generate a new name...
 | 
						|
	reset: function(key, elem){
 | 
						|
		// rewrite...
 | 
						|
		if(this.has(key)){
 | 
						|
			// remove old elem/key from .__keys...
 | 
						|
			var o = this.originalKey(key)
 | 
						|
			var s = this.__keys.get(this.get(key))
 | 
						|
			s.delete(o)
 | 
						|
			s.size == 0
 | 
						|
				&& this.__keys.delete(this.get(key))
 | 
						|
			// add new elem/key to .__keys...
 | 
						|
			var n
 | 
						|
			this.__keys.set(elem, (n = this.__keys.get(elem) || new Set()))
 | 
						|
			n.add(o)
 | 
						|
			
 | 
						|
			return object.parentCall(UniqueKeyMap.prototype, 'set', this, key, elem) 
 | 
						|
		// add...
 | 
						|
		} else {
 | 
						|
			return this.set(key, elem) } },
 | 
						|
	delete: function(key){
 | 
						|
		var s = this.__keys.get(this.get(key))
 | 
						|
		if(s){
 | 
						|
			// XXX will this delete if key is with an index???
 | 
						|
			//s.delete(key)
 | 
						|
			s.delete(this.originalKey(key))
 | 
						|
			this.__reverse.delete(key)
 | 
						|
			s.size == 0
 | 
						|
				&& this.__keys.delete(this.get(key)) }
 | 
						|
		return object.parentCall(UniqueKeyMap.prototype, 'delete', this, key) },
 | 
						|
 | 
						|
	// NOTE: this maintains the item order. This is done by rewriting 
 | 
						|
	// 		items in sequence, this may be slow and trigger lots of write 
 | 
						|
	// 		observer callbacks. to avoid this use .unOrderedRename(..)
 | 
						|
	// XXX do not see how can we avoid rewriting the map if we want to 
 | 
						|
	// 		keep the order...
 | 
						|
	orderedRename: function(from, to, return_key=false){
 | 
						|
		var keys = [...this.keys()]
 | 
						|
		// rename the element...
 | 
						|
		var e = this.get(from)
 | 
						|
		this.delete(from)
 | 
						|
		var n = this.set(to, e, true) 
 | 
						|
		// keep order...
 | 
						|
		keys.splice(keys.indexOf(from), 1, n)
 | 
						|
		this.sort(keys)
 | 
						|
		return return_key ?
 | 
						|
   			n
 | 
						|
			: this },
 | 
						|
	// NOTE: the renamed item is appended to the map...
 | 
						|
	unorderedRename: function(from, to, return_key=false){
 | 
						|
		var e = this.get(from)
 | 
						|
		this.delete(from)
 | 
						|
		return this.set(to, e, return_key) },
 | 
						|
 | 
						|
	__unorderd_rename__: false,
 | 
						|
	rename: function(from, to, return_key=false){
 | 
						|
		return this.__unorderd_writes__ ?
 | 
						|
			this.unorderedRename(...arguments)
 | 
						|
			: this.orderedRename(...arguments) },
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
// XXX should this be a map???
 | 
						|
// XXX make two variants: ordered and unordered...
 | 
						|
// 		...the ordered variant should have the same API as an array...
 | 
						|
var ObjectWithAutoKeys = 
 | 
						|
module.ObjectWithAutoKeys = 
 | 
						|
object.Constructor('ObjectWithAutoKeys', {
 | 
						|
 | 
						|
	__last_key: null,
 | 
						|
	__new_key__: function(value){
 | 
						|
		return (this.__last_key = (this.__last_key || -1) + 1) },
 | 
						|
 | 
						|
	// 
 | 
						|
	// 	.push(value)
 | 
						|
	// 		-> key
 | 
						|
	//
 | 
						|
	// 	.push(value, ..)
 | 
						|
	// 		-> [key, ..]
 | 
						|
	//
 | 
						|
	push: function(...values){
 | 
						|
		var that = this
 | 
						|
		var res = values
 | 
						|
			.map(function(value){
 | 
						|
				that[that.__new_key__(value)] = value })
 | 
						|
		return values.length == 1 ?
 | 
						|
   			res.pop()
 | 
						|
			: res },
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**********************************************************************
 | 
						|
* vim:set ts=4 sw=4 :                               */ return module })
 |