| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | define(function(require){ var module = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //var DEBUG = DEBUG != null ? DEBUG : true
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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(){ | 
					
						
							|  |  |  | 	return this[0].toUpperCase() + this.slice(1) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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-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
										 |  |  | 
 | 
					
						
							|  |  |  | // 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-19 09:34:22 +03:00
										 |  |  | // convert JS arguments to Array...
 | 
					
						
							|  |  |  | var args2array = | 
					
						
							|  |  |  | module.args2array = | 
					
						
							|  |  |  | function(args){ | 
					
						
							|  |  |  | 	//return Array.apply(null, args)
 | 
					
						
							|  |  |  | 	return [].slice.call(args) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 09:14:59 +03:00
										 |  |  | // Quote a string and convert to RegExp to match self literally.
 | 
					
						
							|  |  |  | var quoteRegExp = | 
					
						
							|  |  |  | 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...
 | 
					
						
							|  |  |  | 			.replace(/(\/)/g, '/') | 
					
						
							|  |  |  | 			// 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		multiline: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		reset_on_abort: true,
 | 
					
						
							|  |  |  | 	// 		clear_on_edit: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		abort_keys: [
 | 
					
						
							|  |  |  | 	// 			'Esc',
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		],
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 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){ | 
					
						
							|  |  |  | 		options = options || {} | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var original = this.text() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(options.clear_on_edit == null || options.clear_on_edit){ | 
					
						
							|  |  |  | 			this.text('') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this | 
					
						
							|  |  |  | 			.prop('contenteditable', true) | 
					
						
							|  |  |  | 			// make the element focusable and selectable...
 | 
					
						
							|  |  |  | 			.attr('tabindex', '0') | 
					
						
							| 
									
										
										
										
											2016-04-19 02:42:21 +03:00
										 |  |  | 			.addClass('editable-field') | 
					
						
							| 
									
										
										
										
											2016-04-17 00:58:21 +03:00
										 |  |  | 			// NOTE: this will also focus the element...
 | 
					
						
							|  |  |  | 			.selectText() | 
					
						
							|  |  |  | 			.keydown(function(){  | 
					
						
							|  |  |  | 				if(!that.prop('contenteditable')){ | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				event.stopPropagation()  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var n = keyboard.toKeyName(event.keyCode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// abort...
 | 
					
						
							|  |  |  | 				if((options.abort_keys || ['Esc']).indexOf(n) >= 0){ | 
					
						
							|  |  |  | 					// reset original value...
 | 
					
						
							|  |  |  | 					(options.reset_on_abort == null || options.reset_on_abort)  | 
					
						
							|  |  |  | 						&& that.text(original) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					that | 
					
						
							|  |  |  | 						.trigger('edit-aborted', original) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// done -- single line...
 | 
					
						
							|  |  |  | 				} else if(n == 'Enter'  | 
					
						
							|  |  |  | 						&& !options.multiline){ | 
					
						
							|  |  |  | 					event.preventDefault() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					that.trigger('edit-done', that.text()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// done -- multiline...
 | 
					
						
							|  |  |  | 				} else if(n == 'Enter'  | 
					
						
							|  |  |  | 						&& (event.ctrlKey || event.metaKey)  | 
					
						
							|  |  |  | 						&& options.multiline){ | 
					
						
							|  |  |  | 					event.preventDefault() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					that.trigger('edit-done', that.text()) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                                                */ | 
					
						
							|  |  |  | return module }) |