| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							| 
									
										
										
										
											2016-08-21 02:19:24 +03:00
										 |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							| 
									
										
										
										
											2016-08-20 22:49:36 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Object...
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | // Run a function in the context of an object...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-08-18 04:15:42 +03:00
										 |  |  | Object.defineProperty(Object.prototype, 'run', { | 
					
						
							|  |  |  | 	enumerable: false, | 
					
						
							|  |  |  | 	value: function(func){ | 
					
						
							|  |  |  | 		var res = func ? func.call(this) : undefined | 
					
						
							|  |  |  | 		return res === undefined ? this : res | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | // Get all the accessible keys...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is different to Object.keys(..) in that this will return keys
 | 
					
						
							|  |  |  | // from all the prototypes in the inheritance chain while .keys(..) will 
 | 
					
						
							|  |  |  | // only return the keys defined in the current object only.
 | 
					
						
							|  |  |  | Object.deepKeys = function(obj){ | 
					
						
							|  |  |  | 	var res = [] | 
					
						
							|  |  |  | 	while(obj != null){ | 
					
						
							|  |  |  | 		res = res.concat(Object.keys(obj)) | 
					
						
							|  |  |  | 		obj = obj.__proto__ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return res.unique() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Make a full key set copy of an object...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will not deep-copy the values...
 | 
					
						
							|  |  |  | Object.flatCopy = function(obj){ | 
					
						
							|  |  |  | 	var res = {} | 
					
						
							|  |  |  | 	Object.deepKeys(obj).forEach(function(key){ | 
					
						
							|  |  |  | 		res[key] = obj[key] | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Array...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 15:18:06 +03:00
										 |  |  | // Array.prototype.flat polyfill...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: .flat(..) is not yet supported in IE/Edge...
 | 
					
						
							|  |  |  | Array.prototype.flat | 
					
						
							|  |  |  | 	|| (Array.prototype.flat = function(depth){ | 
					
						
							|  |  |  | 		depth = typeof(depth) == typeof(123) ? depth : 1 | 
					
						
							|  |  |  | 		return this.reduce(function(res, e){  | 
					
						
							|  |  |  | 			return res.concat(e instanceof Array && depth > 0 ?  | 
					
						
							|  |  |  | 				e.flat(depth-1)  | 
					
						
							|  |  |  | 				: [e]) }, []) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | // Array.prototype.includes polyfill...
 | 
					
						
							| 
									
										
										
										
											2018-08-21 14:31:19 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | Array.prototype.includes | 
					
						
							|  |  |  | 	|| (Array.prototype.includes = function(value){ | 
					
						
							|  |  |  | 		return this.indexOf(value) >= 0 })  | 
					
						
							| 
									
										
										
										
											2018-08-21 14:31:19 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | // Compact a sparse array...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will not compact in-place.
 | 
					
						
							|  |  |  | Array.prototype.compact = function(){ | 
					
						
							| 
									
										
										
										
											2018-04-03 00:07:38 +03:00
										 |  |  | 	return this.filter(function(){ return true }) } | 
					
						
							| 
									
										
										
										
											2018-03-23 00:47:39 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 15:18:06 +03:00
										 |  |  | // like .length but for sparse arrays will return the element count...
 | 
					
						
							| 
									
										
										
										
											2018-12-14 16:31:34 +03:00
										 |  |  | 'len' in Array.prototype | 
					
						
							|  |  |  | 	|| Object.defineProperty(Array.prototype, 'len', { | 
					
						
							|  |  |  | 		get : function () { | 
					
						
							|  |  |  | 			return Object.keys(this).length | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set : function(val){}, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2018-11-14 15:18:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-23 00:47:39 +03:00
										 |  |  | // 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...
 | 
					
						
							|  |  |  | Array.prototype.toKeys = function(normalize){ | 
					
						
							| 
									
										
										
										
											2018-03-23 02:22:42 +03:00
										 |  |  | 	return normalize ?  | 
					
						
							|  |  |  | 		this.reduce(function(r, e, i){ | 
					
						
							|  |  |  | 			r[normalize(e)] = i | 
					
						
							|  |  |  | 			return r | 
					
						
							|  |  |  | 		}, {}) | 
					
						
							|  |  |  | 		: this.reduce(function(r, e, i){ | 
					
						
							|  |  |  | 			r[e] = i | 
					
						
							|  |  |  | 			return r | 
					
						
							| 
									
										
										
										
											2018-04-03 00:07:38 +03:00
										 |  |  | 		}, {}) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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...
 | 
					
						
							|  |  |  | Array.prototype.toMap = function(normalize){ | 
					
						
							|  |  |  | 	return normalize ?  | 
					
						
							| 
									
										
										
										
											2018-08-08 03:55:38 +03:00
										 |  |  | 		this | 
					
						
							|  |  |  | 			.reduce(function(m, e, i){ | 
					
						
							|  |  |  | 				m.set(normalize(e), i) | 
					
						
							|  |  |  | 				return m | 
					
						
							|  |  |  | 			}, new Map()) | 
					
						
							|  |  |  | 		: this | 
					
						
							|  |  |  | 			.reduce(function(m, e, i){ | 
					
						
							|  |  |  | 				m.set(e, i) | 
					
						
							|  |  |  | 				return m | 
					
						
							|  |  |  | 			}, new Map()) } | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-23 00:47:39 +03:00
										 |  |  | // Return an array with duplicate elements removed...
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2017-06-06 21:36:17 +03:00
										 |  |  | // NOTE: we are not using an Object as an index here as an Array can 
 | 
					
						
							|  |  |  | // 		contain any type of item while Object keys can only be strings...
 | 
					
						
							| 
									
										
										
										
											2018-03-23 02:22:42 +03:00
										 |  |  | // NOTE: for an array containing only strings use a much faster .uniqueStrings(..)
 | 
					
						
							| 
									
										
										
										
											2018-04-03 00:07:38 +03:00
										 |  |  | // NOTE: this may not work on IE...
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:37:58 +03:00
										 |  |  | Array.prototype.unique = function(normalize){ | 
					
						
							| 
									
										
										
										
											2018-08-08 03:55:38 +03:00
										 |  |  | 	return normalize ?  | 
					
						
							|  |  |  | 		[...new Map(this.map(function(e){ return [normalize(e), e] })).values()] | 
					
						
							|  |  |  | 		: [...(new Set(this))] } | 
					
						
							| 
									
										
										
										
											2018-03-23 00:47:39 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | // Compare two arrays...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | Array.prototype.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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | // Compare two Arrays as sets...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will ignore order
 | 
					
						
							| 
									
										
										
										
											2018-04-03 00:07:38 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // XXX should we use Set(..) here???
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | Array.prototype.setCmp = function(other){ | 
					
						
							|  |  |  | 	return this === other  | 
					
						
							| 
									
										
										
										
											2018-03-23 02:22:42 +03:00
										 |  |  | 		|| this | 
					
						
							|  |  |  | 			.unique() | 
					
						
							|  |  |  | 			.sort() | 
					
						
							|  |  |  | 			.cmp(other | 
					
						
							|  |  |  | 				.unique() | 
					
						
							|  |  |  | 				.sort()) } | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-01 19:25:58 +03:00
										 |  |  | Array.prototype.sortAs = function(other){ | 
					
						
							|  |  |  | 	return this.sort(function(a, b){ | 
					
						
							|  |  |  | 		var i = other.indexOf(a) | 
					
						
							|  |  |  | 		var j = other.indexOf(b) | 
					
						
							|  |  |  | 		return i < 0 && j < 0 ? 0 | 
					
						
							|  |  |  | 			: i < 0 ? 1 | 
					
						
							|  |  |  | 			: j < 0 ? -1 | 
					
						
							|  |  |  | 			: i - j | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Set...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-29 04:59:50 +03:00
										 |  |  | // Set set operation shorthands...
 | 
					
						
							| 
									
										
										
										
											2018-11-30 17:12:27 +03:00
										 |  |  | // XXX should these accept lists of sets???
 | 
					
						
							| 
									
										
										
										
											2018-11-29 04:59:50 +03:00
										 |  |  | Set.prototype.unite = function(other){  | 
					
						
							|  |  |  | 	return new Set([...this, ...other]) } | 
					
						
							|  |  |  | Set.prototype.intersect = function(other){ | 
					
						
							|  |  |  | 	var test = other.has ?  'has' : 'includes' | 
					
						
							|  |  |  | 	return new Set([...this] | 
					
						
							|  |  |  | 		.filter(function(e){ return other[test](e) })) } | 
					
						
							|  |  |  | Set.prototype.subtract = function(other){ | 
					
						
							|  |  |  | 	var test = other.has ?  'has' : 'includes' | 
					
						
							|  |  |  | 	return new Set([...this] | 
					
						
							|  |  |  | 		.filter(function(e){ return !other[test](e) })) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // RegExp...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Quote a string and convert to RegExp to match self literally.
 | 
					
						
							|  |  |  | var quoteRegExp = | 
					
						
							|  |  |  | RegExp.quoteRegExp = | 
					
						
							|  |  |  | module.quoteRegExp = | 
					
						
							|  |  |  | function(str){ | 
					
						
							|  |  |  | 	return str.replace(/([\.\\\/\(\)\[\]\$\*\+\-\{\}\@\^\&\?\<\>])/g, '\\$1') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // String...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String.prototype.capitalize = function(){ | 
					
						
							|  |  |  | 	return this == '' ?  | 
					
						
							|  |  |  | 		this  | 
					
						
							|  |  |  | 		: this[0].toUpperCase() + this.slice(1) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Date...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NOTE: repatching a date should not lead to any side effects as this
 | 
					
						
							|  |  |  | // 		does not add any state...
 | 
					
						
							|  |  |  | var patchDate = | 
					
						
							|  |  |  | module.patchDate = function(date){ | 
					
						
							|  |  |  | 	date = date || Date | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	date.prototype.toShortDate = function(){ | 
					
						
							|  |  |  | 		var y = this.getFullYear() | 
					
						
							|  |  |  | 		var M = this.getMonth()+1 | 
					
						
							|  |  |  | 		M = M < 10 ? '0'+M : M | 
					
						
							|  |  |  | 		var D = this.getDate() | 
					
						
							|  |  |  | 		D = D < 10 ? '0'+D : D | 
					
						
							|  |  |  | 		var H = this.getHours() | 
					
						
							|  |  |  | 		H = H < 10 ? '0'+H : H | 
					
						
							|  |  |  | 		var m = this.getMinutes() | 
					
						
							|  |  |  | 		m = m < 10 ? '0'+m : m | 
					
						
							|  |  |  | 		var s = this.getSeconds() | 
					
						
							|  |  |  | 		s = s < 10 ? '0'+s : s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return ''+y+'-'+M+'-'+D+' '+H+':'+m+':'+s | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	date.prototype.getTimeStamp = function(no_seconds){ | 
					
						
							|  |  |  | 		var y = this.getFullYear() | 
					
						
							|  |  |  | 		var M = this.getMonth()+1 | 
					
						
							|  |  |  | 		M = M < 10 ? '0'+M : M | 
					
						
							|  |  |  | 		var D = this.getDate() | 
					
						
							|  |  |  | 		D = D < 10 ? '0'+D : D | 
					
						
							|  |  |  | 		var H = this.getHours() | 
					
						
							|  |  |  | 		H = H < 10 ? '0'+H : H | 
					
						
							|  |  |  | 		var m = this.getMinutes() | 
					
						
							|  |  |  | 		m = m < 10 ? '0'+m : m | 
					
						
							|  |  |  | 		var s = this.getSeconds() | 
					
						
							|  |  |  | 		s = s < 10 ? '0'+s : s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return ''+y+M+D+H+m+s | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	date.prototype.setTimeStamp = function(ts){ | 
					
						
							|  |  |  | 		ts = ts.replace(/[^0-9]*/g, '') | 
					
						
							|  |  |  | 		this.setFullYear(ts.slice(0, 4)) | 
					
						
							|  |  |  | 		this.setMonth(ts.slice(4, 6)*1-1) | 
					
						
							|  |  |  | 		this.setDate(ts.slice(6, 8)) | 
					
						
							|  |  |  | 		this.setHours(ts.slice(8, 10)) | 
					
						
							|  |  |  | 		this.setMinutes(ts.slice(10, 12)) | 
					
						
							|  |  |  | 		this.setSeconds(ts.slice(12, 14)) | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	date.timeStamp = function(){ | 
					
						
							|  |  |  | 		return (new this()).getTimeStamp() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	date.fromTimeStamp = function(ts){ | 
					
						
							|  |  |  | 		return (new this()).setTimeStamp(ts) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// convert string time period to milliseconds...
 | 
					
						
							|  |  |  | 	date.str2ms = function(str, dfl){ | 
					
						
							|  |  |  | 		dfl = dfl || 'ms' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(typeof(str) == typeof(123)){ | 
					
						
							|  |  |  | 			var val = str | 
					
						
							|  |  |  | 			str = dfl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			var val = parseFloat(str) | 
					
						
							|  |  |  | 			str = str.trim() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// check if a unit is given...
 | 
					
						
							|  |  |  | 			str = str == val ? dfl : str | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		var c = /(m(illi)?(-)?s(ec(ond(s)?)?)?)$/i.test(str) ? 1 | 
					
						
							|  |  |  | 			: /s(ec(ond(s)?)?)?$/i.test(str) ? 1000 | 
					
						
							|  |  |  | 			: /m(in(ute(s)?)?)?$/i.test(str) ? 1000*60 | 
					
						
							|  |  |  | 			: /h(our(s)?)?$/i.test(str) ? 1000*60*60 | 
					
						
							|  |  |  | 			: /d(ay(s)?)?$/i.test(str) ? 1000*60*60*24 | 
					
						
							|  |  |  | 			: null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return c ? val * c : NaN | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return date | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | // patch the root date...
 | 
					
						
							|  |  |  | patchDate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Misc...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-20 16:17:12 +03:00
										 |  |  | module.chainCmp = function(cmp_chain){ | 
					
						
							|  |  |  | 	return function(a, b, get, data){ | 
					
						
							|  |  |  | 		var res | 
					
						
							|  |  |  | 		for(var i=0; i < cmp_chain.length; i++){ | 
					
						
							|  |  |  | 			res = cmp_chain[i](a, b, get, data) | 
					
						
							|  |  |  | 			if(res != 0){ | 
					
						
							|  |  |  | 				return res | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }  | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | // XXX do we need to quote anything else???
 | 
					
						
							|  |  |  | var path2url = | 
					
						
							|  |  |  | module.path2url = | 
					
						
							|  |  |  | function(path){ | 
					
						
							|  |  |  | 	// test if we have a schema, and if yes return as-is...
 | 
					
						
							| 
									
										
										
										
											2017-06-16 19:41:27 +03:00
										 |  |  | 	if(/^(data|http|https|file|[\w-]*):[\\\/]{2}/.test(path)){ | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | 		return path | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// skip encoding windows drives...
 | 
					
						
							| 
									
										
										
										
											2017-06-17 06:33:47 +03:00
										 |  |  | 	path = path | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | 		.split(/[\\\/]/g) | 
					
						
							| 
									
										
										
										
											2017-06-17 06:33:47 +03:00
										 |  |  | 	drive = path[0].endsWith(':') ? | 
					
						
							|  |  |  | 		path.shift() + '/' | 
					
						
							|  |  |  | 		: '' | 
					
						
							|  |  |  | 	return drive + (path | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | 		// XXX these are too aggressive...
 | 
					
						
							|  |  |  | 		//.map(encodeURI)
 | 
					
						
							|  |  |  | 		//.map(encodeURIComponent)
 | 
					
						
							|  |  |  | 		.join('/') | 
					
						
							|  |  |  | 		// NOTE: keep '%' the first...
 | 
					
						
							| 
									
										
										
										
											2017-11-17 02:45:44 +03:00
										 |  |  | 		.replace(/%/g, '%25') | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | 		.replace(/#/g, '%23') | 
					
						
							|  |  |  | 		.replace(/&/g, '%26')) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | // NOTE: we are not using node's path module as we need this to work in
 | 
					
						
							|  |  |  | // 		all contexts, not only node... (???)
 | 
					
						
							|  |  |  | var normalizePath =  | 
					
						
							|  |  |  | module.normalizePath = | 
					
						
							|  |  |  | function(path){ | 
					
						
							|  |  |  | 	return typeof(path) == typeof('str') ? path | 
					
						
							|  |  |  | 			// normalize the slashes...
 | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 			.replace(/\\/g, '/') | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			// remove duplicate '/'
 | 
					
						
							|  |  |  | 			.replace(/(\/)\1+/g, '/') | 
					
						
							|  |  |  | 			// remove trailing '/'
 | 
					
						
							|  |  |  | 			.replace(/\/+$/, '') | 
					
						
							|  |  |  | 			// take care of .
 | 
					
						
							|  |  |  | 			.replace(/\/\.\//g, '/') | 
					
						
							|  |  |  | 			.replace(/\/\.$/, '') | 
					
						
							|  |  |  | 			// take care of ..
 | 
					
						
							|  |  |  | 			.replace(/\/[^\/]+\/\.\.\//g, '/') | 
					
						
							|  |  |  | 			.replace(/\/[^\/]+\/\.\.$/, '') | 
					
						
							|  |  |  | 		: path | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | // HTML/DOM/jQuery...
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-30 14:24:05 +03:00
										 |  |  | var selectElemText = | 
					
						
							|  |  |  | module.selectElemText =  | 
					
						
							|  |  |  | function(elem){ | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 	var range = document.createRange() | 
					
						
							|  |  |  | 	range.selectNodeContents(elem) | 
					
						
							|  |  |  | 	var sel = window.getSelection() | 
					
						
							|  |  |  | 	sel.removeAllRanges() | 
					
						
							|  |  |  | 	sel.addRange(range) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-30 14:24:05 +03:00
										 |  |  | // XXX make this global...
 | 
					
						
							|  |  |  | var getCaretOffset =  | 
					
						
							|  |  |  | module.getCaretOffset = | 
					
						
							|  |  |  | function(elem){ | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 	var s = window.getSelection() | 
					
						
							|  |  |  | 	if(s.rangeCount == 0){ | 
					
						
							| 
									
										
										
										
											2018-03-30 14:24:05 +03:00
										 |  |  | 		return -1 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 	var r = s.getRangeAt(0) | 
					
						
							|  |  |  | 	var pre = r.cloneRange() | 
					
						
							|  |  |  | 	pre.selectNodeContents(elem) | 
					
						
							|  |  |  | 	pre.setEnd(r.endContainer, r.endOffset) | 
					
						
							|  |  |  | 	return pre.toString().length || 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var selectionCollapsed = | 
					
						
							|  |  |  | module.selectionCollapsed = | 
					
						
							|  |  |  | function(elem){ | 
					
						
							|  |  |  | 		var s = window.getSelection() | 
					
						
							|  |  |  | 		if(s.rangeCount == 0){ | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return s.getRangeAt(0).cloneRange().collapsed | 
					
						
							| 
									
										
										
										
											2018-03-30 14:24:05 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 16:13:32 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2015-12-23 07:09:43 +03:00
										 |  |  | // XXX experiment
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | if(typeof(jQuery) != typeof(undefined)){ | 
					
						
							|  |  |  | 	jQuery.fn._drag = function(){ | 
					
						
							|  |  |  | 		var dragging = false | 
					
						
							|  |  |  | 		var s,  | 
					
						
							|  |  |  | 			px, py | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var elem = $(this) | 
					
						
							|  |  |  | 			.on('mousedown touchstart', function(evt){ | 
					
						
							|  |  |  | 				dragging = true | 
					
						
							|  |  |  | 				px = evt.clientX | 
					
						
							|  |  |  | 				px = evt.clientY | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				s = elem.rscale() | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.on('mousemove touchmove', function(evt){ | 
					
						
							|  |  |  | 				if(!dragging){ | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var x = evt.clientX  | 
					
						
							|  |  |  | 				var dx = px - x | 
					
						
							|  |  |  | 				px = x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var y = evt.clientY  | 
					
						
							|  |  |  | 				var dy = py - y | 
					
						
							|  |  |  | 				py = y | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				elem | 
					
						
							|  |  |  | 					.velocity('stop') | 
					
						
							|  |  |  | 					.velocity({ | 
					
						
							|  |  |  | 						translateX: '-=' + (dx / s), | 
					
						
							|  |  |  | 						translateY: '-=' + (dy / s), | 
					
						
							|  |  |  | 					}, 0) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.on('mouseup touchend', function(evt){ | 
					
						
							|  |  |  | 				dragging = false | 
					
						
							|  |  |  | 				elem.velocity('stop') | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 	jQuery.fn.selectText = function(){ | 
					
						
							|  |  |  | 		var range = document.createRange() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.each(function(){ | 
					
						
							|  |  |  | 			range.selectNodeContents(this) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var sel = window.getSelection() | 
					
						
							|  |  |  | 		sel.removeAllRanges() | 
					
						
							|  |  |  | 		sel.addRange(range) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-30 14:24:05 +03:00
										 |  |  | 	jQuery.fn.caretOffset = function(){ return getCaretOffset(this) } | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 	jQuery.fn.selectionCollapsed = function(){ return selectionCollapsed(this) } | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	var keyboard = require('lib/keyboard') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make element editable...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Options format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2017-01-20 23:03:09 +03:00
										 |  |  | 	// 		// activate (focus) element...
 | 
					
						
							|  |  |  | 	// 		//
 | 
					
						
							|  |  |  | 	// 		// NOTE: this will also select the element text...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 	// 		activate: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 	// 		// set multi-line edit mode...
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// 		multiline: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 	// 		// if true in multi-line mode, accept filed on Enter, while
 | 
					
						
							|  |  |  | 	// 		// ctrl-Enter / meta-Enter insert a new line; otherwise
 | 
					
						
							|  |  |  | 	// 		// ctrl-Enter / meta-Enter will accept the edit.
 | 
					
						
							|  |  |  | 	// 		accept_on_enter: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 06:03:16 +03:00
										 |  |  | 	// 		// clear element value on edit...
 | 
					
						
							|  |  |  | 	// 		clear_on_edit: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-02-02 07:26:39 +03:00
										 |  |  | 	// 		// reset value on commit/abort...
 | 
					
						
							|  |  |  | 	// 		// XXX revise default...
 | 
					
						
							|  |  |  | 	// 		reset_on_commit: true,
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// 		reset_on_abort: true,
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-02-02 07:26:39 +03:00
										 |  |  | 	// 		// blur element on commit/abort...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	// 		blur_on_commit: false,
 | 
					
						
							| 
									
										
										
										
											2017-02-02 07:26:39 +03:00
										 |  |  | 	// 		blur_on_abort: false,
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 	// 		// restore focus before disabling the editor...
 | 
					
						
							|  |  |  | 	// 		keep_focus_on_parent: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-02-02 07:26:39 +03:00
										 |  |  | 	// 		// clear selection on commit/abort...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	// 		clear_selection_on_commit: true,
 | 
					
						
							| 
									
										
										
										
											2017-02-02 07:26:39 +03:00
										 |  |  | 	// 		clear_selection_on_abort: true,
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-24 06:16:20 +03:00
										 |  |  | 	// 		// If false unhandled key events will not be propagated to 
 | 
					
						
							|  |  |  | 	// 		// parents...
 | 
					
						
							| 
									
										
										
										
											2017-01-24 06:14:38 +03:00
										 |  |  | 	// 		propagate_unhandled_keys: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-24 06:16:20 +03:00
										 |  |  | 	// 		// If false the element editable state will not be reset to
 | 
					
						
							|  |  |  | 	// 		// the original when edit is done...
 | 
					
						
							| 
									
										
										
										
											2017-01-24 06:14:38 +03:00
										 |  |  | 	// 		reset_on_done: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 	// 		// Keys that will abort the edit...
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// 		abort_keys: [
 | 
					
						
							|  |  |  | 	// 			'Esc',
 | 
					
						
							|  |  |  | 	// 		],
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-05-08 18:51:26 +03:00
										 |  |  | 	// This listens to these events triggerable by user:
 | 
					
						
							| 
									
										
										
										
											2017-01-21 05:21:41 +03:00
										 |  |  | 	// 	'edit-commit'		- will commit changes, this is passed the 
 | 
					
						
							|  |  |  | 	// 							new text just edited.
 | 
					
						
							|  |  |  | 	// 	'edit-abort'		- will reset field, this is passed the 
 | 
					
						
							|  |  |  | 	// 							original text before the edit.
 | 
					
						
							| 
									
										
										
										
											2016-05-08 18:51:26 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 	// These events get passed the relevant text, but the element is 
 | 
					
						
							|  |  |  | 	// likely to be already reset to a different state, to get the 
 | 
					
						
							|  |  |  | 	// element before any state change is started use one of the 
 | 
					
						
							|  |  |  | 	// following variants:
 | 
					
						
							|  |  |  | 	// 	'edit-committing'	- triggered within 'edit-commit' but before
 | 
					
						
							|  |  |  | 	// 							anything is changed, gets passed the final
 | 
					
						
							|  |  |  | 	// 							text (same as 'edit-commit')
 | 
					
						
							|  |  |  | 	// 	'edit-aborting'		- triggered within 'edit-abort' but before 
 | 
					
						
							|  |  |  | 	// 							anything is changed, gets passed the 
 | 
					
						
							|  |  |  | 	// 							original text value (same as 'edit-abort')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-09-24 02:17:44 +03:00
										 |  |  | 	// This will try and preserve element content DOM when resetting.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: removing tabindex will reset focus, so this will attempt to 
 | 
					
						
							|  |  |  | 	// 		focus the first [tabindex] element up the tree...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-21 05:21:41 +03:00
										 |  |  | 	// XXX add option to select the element on start or just focus it...
 | 
					
						
							| 
									
										
										
										
											2017-01-21 05:27:10 +03:00
										 |  |  | 	// 		.activate: 'select' | true | false
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// XXX should we just use form elements???
 | 
					
						
							|  |  |  | 	// 		...it's a trade-off, here we add editing functionality and fight
 | 
					
						
							|  |  |  | 	// 		a bit the original function, in an input we'll need to fight part
 | 
					
						
							|  |  |  | 	// 		of the editing functionality and add our own navigation...
 | 
					
						
							|  |  |  | 	// XXX move this to a more generic spot...
 | 
					
						
							|  |  |  | 	jQuery.fn.makeEditable = function(options){ | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 		if(options == false){ | 
					
						
							|  |  |  | 			this | 
					
						
							| 
									
										
										
										
											2017-01-21 04:36:21 +03:00
										 |  |  | 				.removeAttr('contenteditable') | 
					
						
							|  |  |  | 				.removeAttr('tabindex') | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 				.removeClass('editable-field') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var events = this.data('editable-field-events') | 
					
						
							|  |  |  | 			for(var e in events){ | 
					
						
							|  |  |  | 				this.off(e, events[e]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.removeData('editable-field-events') | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return this | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 		options = Object.assign({ | 
					
						
							|  |  |  | 			// defaults...
 | 
					
						
							|  |  |  | 			activate: false, | 
					
						
							|  |  |  | 			multiline: false, | 
					
						
							|  |  |  | 			accept_on_enter: true, | 
					
						
							|  |  |  | 			clear_on_edit: false, | 
					
						
							|  |  |  | 			reset_on_commit: true, | 
					
						
							|  |  |  | 			reset_on_abort: true, | 
					
						
							|  |  |  | 			blur_on_commit: false, | 
					
						
							|  |  |  | 			blur_on_abort: false, | 
					
						
							|  |  |  | 			keep_focus_on_parent: true, | 
					
						
							|  |  |  | 			clear_selection_on_commit: true, | 
					
						
							|  |  |  | 			clear_selection_on_abort: true, | 
					
						
							|  |  |  | 			propagate_unhandled_keys: true, | 
					
						
							|  |  |  | 			reset_on_done: true, | 
					
						
							|  |  |  | 			abort_keys: ['Esc'], | 
					
						
							|  |  |  | 		}, options || {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var original_text = this[0].innerText | 
					
						
							| 
									
										
										
										
											2017-09-24 02:17:44 +03:00
										 |  |  | 		var original_dom = document.createDocumentFragment() | 
					
						
							|  |  |  | 		this[0].childNodes | 
					
						
							|  |  |  | 			.forEach(function(node){  | 
					
						
							|  |  |  | 				original_dom.appendChild(node.cloneNode(true)) }) | 
					
						
							|  |  |  | 		var resetOriginal = function(){ | 
					
						
							|  |  |  | 			//that.text(original_text)
 | 
					
						
							|  |  |  | 			that[0].innerHTML = '' | 
					
						
							|  |  |  | 			that[0].appendChild(original_dom.cloneNode(true)) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 		this.prop('contenteditable', true) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-07 06:03:16 +03:00
										 |  |  | 		options.activate  | 
					
						
							|  |  |  | 			&& options.clear_on_edit  | 
					
						
							| 
									
										
										
										
											2017-02-01 22:50:01 +03:00
										 |  |  | 			// XXX this for some reason breaks on click...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 06:03:16 +03:00
										 |  |  | 			&& this.text('') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 		// NOTE: this will also focus the element...
 | 
					
						
							| 
									
										
										
										
											2017-01-20 23:03:09 +03:00
										 |  |  | 		options.activate  | 
					
						
							|  |  |  | 			&& this.selectText() | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 		// do not setup handlers more than once...
 | 
					
						
							|  |  |  | 		if(!this.hasClass('editable-field')){ | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 			var events = {} | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 			this | 
					
						
							|  |  |  | 				// make the element focusable and selectable...
 | 
					
						
							|  |  |  | 				.attr('tabindex', '0') | 
					
						
							|  |  |  | 				.addClass('editable-field') | 
					
						
							| 
									
										
										
										
											2018-03-29 14:48:30 +03:00
										 |  |  | 				.keydown(events.keydown = function(in_evt){  | 
					
						
							| 
									
										
										
										
											2018-03-26 18:05:53 +03:00
										 |  |  | 					var evt = window.event || in_evt | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					if(!that.prop('contenteditable')){ | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 					evt.stopPropagation()  | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					var c = getCaretOffset(this) | 
					
						
							|  |  |  | 					var collapsed = selectionCollapsed(this)  | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 					var n = keyboard.code2key(evt.keyCode) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// abort...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					if((options.abort_keys || []).indexOf(n) >= 0){ | 
					
						
							| 
									
										
										
										
											2017-09-24 02:17:44 +03:00
										 |  |  | 						that.trigger('edit-abort', original_text) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// done -- single line...
 | 
					
						
							|  |  |  | 					} else if(n == 'Enter'  | 
					
						
							|  |  |  | 							&& !options.multiline){ | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 						evt.preventDefault() | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 						that.trigger('edit-commit', that[0].innerText) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 					// done -- multi-line...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(options.multiline  | 
					
						
							|  |  |  | 							&& n == 'Enter'  | 
					
						
							|  |  |  | 							&& (options.accept_on_enter ? | 
					
						
							|  |  |  | 								!(evt.ctrlKey || evt.shiftKey || evt.metaKey)  | 
					
						
							|  |  |  | 								: (evt.ctrlKey || evt.shiftKey || evt.metaKey)) ){ | 
					
						
							| 
									
										
										
										
											2018-03-19 01:42:36 +03:00
										 |  |  | 						evt.preventDefault() | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 						that.trigger('edit-commit', that[0].innerText) | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 					// multi-line keep keys...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(options.multiline  | 
					
						
							|  |  |  | 							&& options.accept_on_enter ?  | 
					
						
							|  |  |  | 								(n == 'Enter'  | 
					
						
							|  |  |  | 								 	&& (evt.ctrlKey || evt.shiftKey || evt.metaKey)) | 
					
						
							|  |  |  | 								: n == 'Enter'){ | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 						return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// multi-line arrow keys -- keep key iff not at first/last position...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(options.multiline | 
					
						
							|  |  |  | 							&& n == 'Up' | 
					
						
							|  |  |  | 							&& (c > 0 || !collapsed)){ | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 						return | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(options.multiline | 
					
						
							|  |  |  | 							&& n == 'Down' | 
					
						
							|  |  |  | 							&& (c < $(this).text().length || !collapsed)){ | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 						return | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(n == 'Up' || n == 'Down'){ | 
					
						
							|  |  |  | 						evt.preventDefault() | 
					
						
							|  |  |  | 						that.trigger('edit-commit', that[0].innerText) | 
					
						
							| 
									
										
										
										
											2018-03-30 12:58:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 					// continue handling...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					} else if(options.propagate_unhandled_keys){ | 
					
						
							| 
									
										
										
										
											2018-03-26 19:54:02 +03:00
										 |  |  | 						// NOTE: jQuery can't reuse browser events, this 
 | 
					
						
							|  |  |  | 						// 		we need to pass a jq event/proxy here...
 | 
					
						
							| 
									
										
										
										
											2018-03-29 14:48:30 +03:00
										 |  |  | 						$(this).parent().trigger(in_evt || evt) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 				.blur(events.blur = function(){ | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					window.getSelection().removeAllRanges() | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 				.on('focus click', events['focus click'] = function(evt){ | 
					
						
							| 
									
										
										
										
											2016-06-07 06:03:16 +03:00
										 |  |  | 					evt.stopPropagation() | 
					
						
							|  |  |  | 					options.clear_on_edit  | 
					
						
							|  |  |  | 						&& $(this) | 
					
						
							|  |  |  | 							.text('') | 
					
						
							|  |  |  | 							.selectText() | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 				// user triggerable events...
 | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 				.on('edit-abort', events['edit-abort'] = function(evt, text){ | 
					
						
							|  |  |  | 					that.trigger('edit-aborting', text) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.clear_selection_on_abort | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 						&& window.getSelection().removeAllRanges() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 					// reset original value...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.reset_on_abort | 
					
						
							| 
									
										
										
										
											2017-09-24 02:17:44 +03:00
										 |  |  | 						&& resetOriginal()  | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.blur_on_abort | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 						&& this.blur()  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// restore focus on parent...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.keep_focus_on_parent | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 						&& that.parents('[tabindex]').first().focus() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.reset_on_done | 
					
						
							| 
									
										
										
										
											2017-01-24 06:14:38 +03:00
										 |  |  | 						&& that.makeEditable(false) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-01-21 23:39:41 +03:00
										 |  |  | 				.on('edit-commit', events['edit-commit'] = function(evt, text){ | 
					
						
							|  |  |  | 					that.trigger('edit-committing', text) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.clear_selection_on_commit | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 						&& window.getSelection().removeAllRanges() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 					// reset original value...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.reset_on_commit | 
					
						
							| 
									
										
										
										
											2017-09-24 02:17:44 +03:00
										 |  |  | 						&& resetOriginal()  | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.blur_on_commit | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 						&& this.blur()  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// restore focus on parent...
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.keep_focus_on_parent | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 						&& that.parents('[tabindex]').first().focus() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-31 15:24:42 +03:00
										 |  |  | 					options.reset_on_done | 
					
						
							| 
									
										
										
										
											2017-01-24 06:14:38 +03:00
										 |  |  | 						&& that.makeEditable(false) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			this.data('editable-field-events', events) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-23 07:09:43 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2016-08-20 22:49:36 +03:00
										 |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |