| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | String.prototype.capitalize = function(){ | 
					
						
							| 
									
										
										
										
											2017-01-08 03:32:49 +03:00
										 |  |  | 	return this == '' ?  | 
					
						
							|  |  |  | 		this  | 
					
						
							|  |  |  | 		: this[0].toUpperCase() + this.slice(1) } | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX not sure if this has to be a utility or a method...
 | 
					
						
							|  |  |  | Object.get = function(obj, name, dfl){ | 
					
						
							|  |  |  | 	var val = obj[name] | 
					
						
							|  |  |  | 	if(val === undefined && dfl != null){ | 
					
						
							|  |  |  | 		return dfl | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Compact a sparse array...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will not compact in-place.
 | 
					
						
							|  |  |  | Array.prototype.compact = function(){ | 
					
						
							|  |  |  | 	return this.filter(function(){ return true }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-12 19:01:40 +03:00
										 |  |  | /* | 
					
						
							|  |  |  | Array.prototype.compact = function(){ | 
					
						
							|  |  |  | 	var res = [] | 
					
						
							|  |  |  | 	for(var i in res){ | 
					
						
							|  |  |  | 		res.push(this[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // return an array with duplicate elements removed...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:37:58 +03:00
										 |  |  | Array.prototype.unique = function(normalize){ | 
					
						
							|  |  |  | 	if(normalize){ | 
					
						
							| 
									
										
										
										
											2016-04-16 01:36:55 +03:00
										 |  |  | 		var cache = this.map(function(e){ return normalize(e) }) | 
					
						
							| 
									
										
										
										
											2016-04-14 16:37:58 +03:00
										 |  |  | 		return this.filter(function(e, i, a){ return cache.indexOf(cache[i]) == i }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return this.filter(function(e, i, a){ return a.indexOf(e) == i }) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Compare two Arrays as sets...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will ignore order
 | 
					
						
							|  |  |  | Array.prototype.setCmp = function(other){ | 
					
						
							|  |  |  | 	return this === other  | 
					
						
							|  |  |  | 		|| this.unique().sort().cmp(other.unique().sort()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-28 18:31:42 +03:00
										 |  |  | var args2array = | 
					
						
							|  |  |  | module.args2array = | 
					
						
							|  |  |  | Array.fromArgs =  | 
					
						
							|  |  |  | 	function(args){ return [].slice.call(args) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-18 20:23:31 +03:00
										 |  |  | // Get all the accessible keys...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is different to Object.keys(..) in that this will return keys
 | 
					
						
							|  |  |  | // from all the prototypes while .keys(..) will only return the keys
 | 
					
						
							|  |  |  | // defined in the last layer.
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | // like .length but for sparse arrays will return the element count...
 | 
					
						
							|  |  |  | // XXX make this a prop...
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | Array.prototype.len = function(){ | 
					
						
							|  |  |  | 	//return this.compact().length
 | 
					
						
							|  |  |  | 	return Object.keys(this).length | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Object.defineProperty(Array.prototype, 'len', { | 
					
						
							|  |  |  | 	get : function () { | 
					
						
							|  |  |  | 		return Object.keys(this).length | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set : function(val){}, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | // Quote a string and convert to RegExp to match self literally.
 | 
					
						
							|  |  |  | var quoteRegExp = | 
					
						
							| 
									
										
										
										
											2016-05-28 02:47:52 +03:00
										 |  |  | RegExp.quoteRegExp = | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | module.quoteRegExp = | 
					
						
							|  |  |  | function(str){ | 
					
						
							|  |  |  | 	return str.replace(/([\.\\\/\(\)\[\]\$\*\+\-\{\}\@\^\&\?\<\>])/g, '\\$1') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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...
 | 
					
						
							|  |  |  | 	if(/^(http|https|file|[\w-]*):[\\\/]{2}/.test(path)){ | 
					
						
							|  |  |  | 		return path | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// skip encoding windows drives...
 | 
					
						
							|  |  |  | 	var drive = path.split(/^([a-z]:[\\\/])/i) | 
					
						
							|  |  |  | 	path = drive.pop() | 
					
						
							|  |  |  | 	drive = drive.pop() || '' | 
					
						
							|  |  |  | 	return drive + (path | 
					
						
							|  |  |  | 		.split(/[\\\/]/g) | 
					
						
							|  |  |  | 		// XXX these are too aggressive...
 | 
					
						
							|  |  |  | 		//.map(encodeURI)
 | 
					
						
							|  |  |  | 		//.map(encodeURIComponent)
 | 
					
						
							|  |  |  | 		.join('/') | 
					
						
							|  |  |  | 		// NOTE: keep '%' the first...
 | 
					
						
							| 
									
										
										
										
											2015-12-18 07:55:14 +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
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.selectElemText = function(elem){ | 
					
						
							|  |  |  | 	var range = document.createRange() | 
					
						
							|  |  |  | 	range.selectNodeContents(elem) | 
					
						
							|  |  |  | 	var sel = window.getSelection() | 
					
						
							|  |  |  | 	sel.removeAllRanges() | 
					
						
							|  |  |  | 	sel.addRange(range) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-28 02:47:52 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | // 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() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// set multi line edit mode...
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// 		multiline: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 06:03:16 +03:00
										 |  |  | 	// 		// clear element value on edit...
 | 
					
						
							|  |  |  | 	// 		clear_on_edit: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 	// 		// reset value on abort...
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	// 		reset_on_abort: true,
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 	// 		// blur element on abort/commit...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	// 		blur_on_abort: false,
 | 
					
						
							|  |  |  | 	// 		blur_on_commit: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 	// 		// restore focus before disabling the editor...
 | 
					
						
							|  |  |  | 	// 		keep_focus_on_parent: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:28:56 +03:00
										 |  |  | 	// 		// clear selection on abort/commit...
 | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 	// 		clear_selection_on_abort: true,
 | 
					
						
							|  |  |  | 	// 		clear_selection_on_commit: 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:
 | 
					
						
							|  |  |  | 	// 	'commit'		- will commit changes and fire 'edit-done' with
 | 
					
						
							|  |  |  | 	// 						field text.
 | 
					
						
							|  |  |  | 	// 	'abort'			- will reset field and trigger 'edit-aborted'
 | 
					
						
							|  |  |  | 	// 						with original (before edit started) field text
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											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...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											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...
 | 
					
						
							| 
									
										
										
										
											2016-05-08 18:51:26 +03:00
										 |  |  | 	// XXX should this reset field to it's original state after 
 | 
					
						
							|  |  |  | 	// 		commit/abort???
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 		options = options || {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var original = this.text() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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  | 
					
						
							|  |  |  | 			&& 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') | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 				.keydown(events.keydown = function(evt){  | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					if(!that.prop('contenteditable')){ | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					event.stopPropagation()  | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 02:09:59 +03:00
										 |  |  | 					var n = keyboard.code2key(event.keyCode) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// abort...
 | 
					
						
							|  |  |  | 					if((options.abort_keys || ['Esc']).indexOf(n) >= 0){ | 
					
						
							|  |  |  | 						that.trigger('abort') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// done -- single line...
 | 
					
						
							|  |  |  | 					} else if(n == 'Enter'  | 
					
						
							|  |  |  | 							&& !options.multiline){ | 
					
						
							|  |  |  | 						event.preventDefault() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						that.trigger('commit') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// done -- multiline...
 | 
					
						
							|  |  |  | 					} else if(n == 'Enter'  | 
					
						
							|  |  |  | 							&& (event.ctrlKey || event.metaKey)  | 
					
						
							|  |  |  | 							&& options.multiline){ | 
					
						
							|  |  |  | 						event.preventDefault() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						that.trigger('commit') | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// continue handling...
 | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						$(this).parent().trigger(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-18 20:48:30 +03:00
										 |  |  | 				.on('abort', events.abort = function(){ | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					that.trigger('edit-aborted', original) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					options.clear_selection_on_abort !== false  | 
					
						
							|  |  |  | 						&& window.getSelection().removeAllRanges() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 					// reset original value...
 | 
					
						
							|  |  |  | 					options.reset_on_abort !== false | 
					
						
							|  |  |  | 						&& that.text(original) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					options.blur_on_abort !== false  | 
					
						
							|  |  |  | 						&& this.blur()  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// restore focus on parent...
 | 
					
						
							|  |  |  | 					options.keep_focus_on_parent !== false | 
					
						
							|  |  |  | 						&& that.parents('[tabindex]').first().focus() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					that.makeEditable(false) | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 				.on('commit', events.commit = function(){ | 
					
						
							| 
									
										
										
										
											2016-06-07 04:03:56 +03:00
										 |  |  | 					that.trigger('edit-done', that.text()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					options.clear_selection_on_commit !== false  | 
					
						
							|  |  |  | 						&& window.getSelection().removeAllRanges() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 20:48:30 +03:00
										 |  |  | 					// reset original value...
 | 
					
						
							|  |  |  | 					options.reset_on_commit !== false | 
					
						
							|  |  |  | 						&& that.text(original) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					options.blur_on_commit !== false  | 
					
						
							|  |  |  | 						&& this.blur()  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// restore focus on parent...
 | 
					
						
							|  |  |  | 					options.keep_focus_on_parent !== false | 
					
						
							|  |  |  | 						&& that.parents('[tabindex]').first().focus() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					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 }) |