| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							| 
									
										
										
										
											2017-01-02 01:23:33 +03:00
										 |  |  | * Base features... | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * Features: | 
					
						
							|  |  |  | * 	- base | 
					
						
							|  |  |  | * 		map to data and images | 
					
						
							|  |  |  | * 	- crop | 
					
						
							|  |  |  | * 	- tags | 
					
						
							|  |  |  | * 	- groups | 
					
						
							|  |  |  | * 		XXX experimental... | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * Meta Features: | 
					
						
							|  |  |  | * 	- base-full | 
					
						
							|  |  |  | * 		combines the above features into one | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | var actions = require('lib/actions') | 
					
						
							|  |  |  | var features = require('lib/features') | 
					
						
							| 
									
										
										
										
											2015-12-31 10:37:21 +03:00
										 |  |  | var toggler = require('lib/toggler') | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 16:12:10 +03:00
										 |  |  | var data = require('imagegrid/data') | 
					
						
							|  |  |  | var images = require('imagegrid/images') | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | var core = require('features/core') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-31 19:43:25 +03:00
										 |  |  | // Generate an undo function for shift operations...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: {undo: 'shiftImageDown'}, will not do here because we need to 
 | 
					
						
							|  |  |  | // 		pass an argument to the shift action, as without an argument 
 | 
					
						
							|  |  |  | // 		these actions will shift focus to a different image in the same 
 | 
					
						
							|  |  |  | // 		ribbon...
 | 
					
						
							|  |  |  | // 			.shiftImageDown(x)
 | 
					
						
							|  |  |  | // 				shift image x without changing focus, i.e. the focused
 | 
					
						
							|  |  |  | // 				image before the action will stay focused after.
 | 
					
						
							|  |  |  | // 			.focusImage(x).shiftImageDown()
 | 
					
						
							|  |  |  | // 				focus image x, then shift it down (current image default)
 | 
					
						
							|  |  |  | // 				this will shift focus to .direction of current image.
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | var undoShift = function(undo){ | 
					
						
							|  |  |  | 	return function(a){  | 
					
						
							|  |  |  | 		this[undo](a.args.length == 0 ? a.current : a.args[0]) }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 01:23:33 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // XXX split this into read and write actions...
 | 
					
						
							|  |  |  | var BaseActions =  | 
					
						
							|  |  |  | module.BaseActions =  | 
					
						
							|  |  |  | actions.Actions({ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	config: { | 
					
						
							|  |  |  | 		// XXX should this be here???
 | 
					
						
							|  |  |  | 		version: 'gen4', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 		// Number of steps to change default direction...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		// see .direction for details...
 | 
					
						
							|  |  |  | 		'steps-to-change-direction': 3, | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 		// If true, shift up/down will count as a left/right move...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// see .direction for details...
 | 
					
						
							| 
									
										
										
										
											2016-11-26 14:29:11 +03:00
										 |  |  | 		'shifts-affect-direction': 'on', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Determines the image selection mode when focusing or moving 
 | 
					
						
							|  |  |  | 		// between ribbons...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// supported modes:
 | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		// XXX should this be here???
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		'ribbon-focus-modes': [ | 
					
						
							|  |  |  | 			'order',	// select image closest to current in order
 | 
					
						
							|  |  |  | 			'first',	// select first image
 | 
					
						
							|  |  |  | 			'last',		// select last image
 | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2015-12-20 04:29:44 +03:00
										 |  |  | 		'ribbon-focus-mode': 'order', | 
					
						
							| 
									
										
										
										
											2016-04-20 16:17:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	get version(){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		return this.config.version }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// basic state...
 | 
					
						
							|  |  |  | 	// NOTE: the setters in the following use the appropriate actions
 | 
					
						
							|  |  |  | 	// 		so to avoid recursion do not use these in the specific 
 | 
					
						
							|  |  |  | 	// 		actions...
 | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2016-11-28 20:24:01 +03:00
										 |  |  | 	// Data...
 | 
					
						
							|  |  |  | 	get data(){  | 
					
						
							|  |  |  | 		var d = this.__data = this.__data || data.Data()  | 
					
						
							|  |  |  | 		return d  | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set data(value){  | 
					
						
							|  |  |  | 		this.__data = value }, | 
					
						
							| 
									
										
										
										
											2017-01-18 23:12:27 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	get length(){ | 
					
						
							|  |  |  | 		return this.data.length }, | 
					
						
							| 
									
										
										
										
											2016-11-28 20:24:01 +03:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// Base ribbon...
 | 
					
						
							|  |  |  | 	get base(){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		return this.data.base }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	set base(value){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		this.setBaseRibbon(value) }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Current image...
 | 
					
						
							|  |  |  | 	get current(){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		return this.data.current }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	set current(value){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		this.focusImage(value) }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Current ribbon...
 | 
					
						
							| 
									
										
										
										
											2016-12-03 14:39:04 +03:00
										 |  |  | 	get current_ribbon(){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		return this.data.getRibbon() }, | 
					
						
							| 
									
										
										
										
											2016-12-03 14:39:04 +03:00
										 |  |  | 	set current_ribbon(value){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		this.focusRibbon(value) }, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Default direction...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 	// The system delays inertial direction change -- after >N steps of 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// movement in one direction it takes N steps to reverse the default
 | 
					
						
							|  |  |  | 	// direction.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 	// This can be 'left' or 'right', other values are ignored.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Assigning '!' to this is the same as assigning (repeating) the 
 | 
					
						
							|  |  |  | 	// last assigned value again.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Assigning 'left!' or 'right!' ('!' appended) will reset the counter
 | 
					
						
							|  |  |  | 	// and force direction change.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Configuration (.config):
 | 
					
						
							|  |  |  | 	// 	'steps-to-change-direction' 	
 | 
					
						
							|  |  |  | 	// 		Sets the number of steps to change direction (N)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	'shifts-affect-direction'
 | 
					
						
							| 
									
										
										
										
											2016-11-26 14:29:11 +03:00
										 |  |  | 	//		If 'on', add last direction change before vertical shift to 
 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 	//		direction counter (N)
 | 
					
						
							|  |  |  | 	//		This makes the direction change after several shifts up/down
 | 
					
						
							|  |  |  | 	//		"backwards" a bit faster.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	get direction(){ | 
					
						
							|  |  |  | 		return this._direction >= 0 ? 'right' | 
					
						
							|  |  |  | 			: this._direction < 0 ? 'left' | 
					
						
							|  |  |  | 			: 'right' | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set direction(value){ | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 		// repeat last direction...
 | 
					
						
							|  |  |  | 		if(value == '!'){ | 
					
						
							| 
									
										
										
										
											2016-11-26 15:46:44 +03:00
										 |  |  | 			if(this._direction_last == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.direction = this._direction_last | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		// force direction change...
 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 		} else if(typeof(value) == typeof('str')  | 
					
						
							|  |  |  | 				&& value.slice(-1) == '!'){ | 
					
						
							|  |  |  | 			value = this._direction = value == 'left!' ? -1 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 				: value == 'right!' ? 0 | 
					
						
							|  |  |  | 				: this._direction | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 			this._direction_last = value >= 0 ? 'right' : 'left' | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// 'update' direction...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			value = value == 'left' ? -1  | 
					
						
							|  |  |  | 				: value == 'right' ? 1 | 
					
						
							|  |  |  | 				: 0 | 
					
						
							| 
									
										
										
										
											2016-11-19 19:58:55 +03:00
										 |  |  | 			this._direction_last = value >= 0 ? 'right' : 'left' | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			var d = (this._direction || 0) + value | 
					
						
							|  |  |  | 			var s = this.config['steps-to-change-direction'] | 
					
						
							|  |  |  | 			s = s < 1 ? 1 : s | 
					
						
							|  |  |  | 			// cap the direction value between -s and s-1...
 | 
					
						
							|  |  |  | 			// NOTE: we use s-1 instead of s as 0/null is a positive 
 | 
					
						
							|  |  |  | 			// 		direction...
 | 
					
						
							|  |  |  | 			d = d >= s ? s-1 : d | 
					
						
							|  |  |  | 			d = d < -s ? -s : d | 
					
						
							|  |  |  | 			this._direction = d | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-05 03:06:06 +03:00
										 |  |  | 	toggleRibbonFocusMode : ['Interface/Ribbon focus mode', | 
					
						
							| 
									
										
										
										
											2016-03-28 22:07:38 +03:00
										 |  |  | 		core.makeConfigToggler('ribbon-focus-mode',  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			function(){ return this.config['ribbon-focus-modes'] })], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// basic life-cycle actions...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX do we need to call .syncTags(..) here???
 | 
					
						
							|  |  |  | 	load: ['- File|Interface/', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(d){ | 
					
						
							| 
									
										
										
										
											2015-12-29 01:02:57 +03:00
										 |  |  | 			this.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			this.images = images.Images(d.images) | 
					
						
							|  |  |  | 			this.data = data.Data(d.data) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2017-01-23 20:49:05 +03:00
										 |  |  | 	// XXX should this clear or load empty???
 | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:09 +03:00
										 |  |  | 	clear: ['File/Clear', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ | 
					
						
							| 
									
										
										
										
											2017-01-23 20:49:05 +03:00
										 |  |  | 			//this.data = null
 | 
					
						
							|  |  |  | 			//this.images = null
 | 
					
						
							|  |  |  | 			this.data = new data.DataWithTags() | 
					
						
							|  |  |  | 			this.images = new images.Images()  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: for complete isolation it is best to completely copy the 
 | 
					
						
							|  |  |  | 	// 		.config...
 | 
					
						
							|  |  |  | 	clone: ['- File/', | 
					
						
							|  |  |  | 		function(full){ | 
					
						
							|  |  |  | 			var res = actions.MetaActions.clone.call(this, full) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(this.data){ | 
					
						
							|  |  |  | 				res.data = this.data.clone() | 
					
						
							|  |  |  | 			}  | 
					
						
							|  |  |  | 			if(this.images){ | 
					
						
							|  |  |  | 				res.images = this.images.clone() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return res | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 	dataFromURLs: ['- File/', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(lst, base){ | 
					
						
							| 
									
										
										
										
											2015-12-29 01:02:57 +03:00
										 |  |  | 			var imgs = images.Images.fromArray(lst, base) | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 			return { | 
					
						
							| 
									
										
										
										
											2015-12-29 01:02:57 +03:00
										 |  |  | 				images: imgs, | 
					
						
							|  |  |  | 				data: data.Data.fromArray(imgs.keys()), | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 	// XXX should this be here???
 | 
					
						
							|  |  |  | 	// XXX should this use .load(..)
 | 
					
						
							|  |  |  | 	// 		...note if we use this it breaks, need to rethink...
 | 
					
						
							|  |  |  | 	loadURLs: ['- File/Load a URL list', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2016-05-29 23:49:01 +03:00
										 |  |  | 		function(lst, base){ this.load(this.dataFromURLs(lst, base)) }], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// XXX experimental...
 | 
					
						
							|  |  |  | 	// 		...the bad thing about this is that we can not extend this,
 | 
					
						
							|  |  |  | 	// 		adding new items to the resulting structure...
 | 
					
						
							|  |  |  | 	// XXX is this the correct way to go???
 | 
					
						
							|  |  |  | 	// 		...can we save simple attribute values???
 | 
					
						
							|  |  |  | 	json: ['- File/Dump state as JSON object', | 
					
						
							|  |  |  | 		'This will collect JSON data from every available attribute ' | 
					
						
							|  |  |  | 			+'supporting the .dumpJSON() method.', | 
					
						
							|  |  |  | 		function(mode){ | 
					
						
							|  |  |  | 			var res = {} | 
					
						
							|  |  |  | 			for(var k in this){ | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 				if(this[k] != null && this[k].dumpJSON != null){ | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 					res[k] = this[k].dumpJSON() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return res | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 07:09:06 +03:00
										 |  |  | 	replaceGid: ['- System/Replace image gid', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-28 07:09:06 +03:00
										 |  |  | 		function(from, to){ | 
					
						
							|  |  |  | 			from = this.data.getImage(from) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// data...
 | 
					
						
							|  |  |  | 			var res = this.data.replaceGid(from, to) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(res == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// images...
 | 
					
						
							|  |  |  | 			this.images && this.images.replaceGid(from, to) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// basic navigation...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	focusImage: ['- Navigate/Focus image', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Focus image...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus current image... | 
					
						
							|  |  |  | 			.focusImage() | 
					
						
							|  |  |  | 			.focusImage('current') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus next/prev image in current ribbon... | 
					
						
							|  |  |  | 			.focusImage('next') | 
					
						
							|  |  |  | 			.focusImage('prev') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus next/prev image globally... | 
					
						
							|  |  |  | 			.focusImage('next', 'global') | 
					
						
							|  |  |  | 			.focusImage('prev', 'global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image... | 
					
						
							|  |  |  | 			.focusImage(<image>) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image at <order> in current ribbon... | 
					
						
							|  |  |  | 			.focusImage(<image>, 'ribbon') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image at <order> in specific ribbon... | 
					
						
							|  |  |  | 			.focusImage(<image>, <ribbon>) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image globally... | 
					
						
							|  |  |  | 			.focusImage(<image>, 'global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image from list... | 
					
						
							|  |  |  | 			NOTE: this takes account of list order. | 
					
						
							|  |  |  | 			.focusImage(<image>, [ <gid>, .. ]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		In the above, <image> can be: | 
					
						
							|  |  |  | 			<gid>		- explicit image gid | 
					
						
							|  |  |  | 			<order>		- image order. | 
					
						
							|  |  |  | 			'next'		- next image relative to current | 
					
						
							|  |  |  | 			'prev'		- previous image relative to current | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		<ribbon> can be ribbon gid. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Order can be positive, zero based and counted from the left, or | 
					
						
							|  |  |  | 		negative, -1-based and counted from the right, e.g. 0 is the  | 
					
						
							|  |  |  | 		first image and -1 is the last. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		If given image is not present in the requested context (ribbon, | 
					
						
							|  |  |  | 		global), this will focus on the closest image that is loaded. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Examples: | 
					
						
							|  |  |  | 				// focus second to last image...
 | 
					
						
							|  |  |  | 				.focusImage(-2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// focus first image globally...
 | 
					
						
							|  |  |  | 				.focusImage(0, 'global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// focus next image...
 | 
					
						
							|  |  |  | 				.focusImage('next') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// focus next image globally, i.e. we can jump to other
 | 
					
						
							|  |  |  | 				// ribbons...
 | 
					
						
							|  |  |  | 				.focusImage('next', 'global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this is a simplified version of the doc, for more details see: | 
					
						
							|  |  |  | 			.data.focusImage(..) and .data.getImage(), also note that this  | 
					
						
							|  |  |  | 			has a slightly different signature to the above, this is done | 
					
						
							|  |  |  | 			for simplicity... | 
					
						
							|  |  |  | 		`,
 | 
					
						
							|  |  |  | 		function(img, list){ this.data.focusImage.apply(this.data, arguments) }], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// Focuses a ribbon by selecting an image in it...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// modes supported:
 | 
					
						
							|  |  |  | 	// 	'order'			- focus closest image to current in order
 | 
					
						
							|  |  |  | 	// 	'first'/'last'	- focus first/last image in ribbon
 | 
					
						
							|  |  |  | 	// 	'visual'		- focus visually closest to current image
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: default mode is set in .config.ribbon-focus-mode
 | 
					
						
							| 
									
										
										
										
											2015-12-20 20:59:19 +03:00
										 |  |  | 	// NOTE: this explicitly does nothing if mode is unrecognised, this
 | 
					
						
							|  |  |  | 	// 		is done to add support for other custom modes...
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	focusRibbon: ['- Navigate/Focus Ribbon', | 
					
						
							|  |  |  | 		function(target, mode){ | 
					
						
							|  |  |  | 			var data = this.data | 
					
						
							| 
									
										
										
										
											2016-11-28 20:08:08 +03:00
										 |  |  | 			if(data == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			var r = data.getRibbon(target) | 
					
						
							|  |  |  | 			if(r == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var c = data.getRibbonOrder() | 
					
						
							|  |  |  | 			var i = data.getRibbonOrder(r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			mode = mode || this.config['ribbon-focus-mode'] || 'order' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// NOTE: we are not changing the direction here based on 
 | 
					
						
							|  |  |  | 			// 		this.direction as swap will confuse the user...
 | 
					
						
							|  |  |  | 			var direction = c < i ? 'before' : 'after' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// closest image in order...
 | 
					
						
							|  |  |  | 			if(mode == 'order'){ | 
					
						
							|  |  |  | 				var t = data.getImage(r, direction) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// if there are no images in the requied direction, try the 
 | 
					
						
							|  |  |  | 				// other way...
 | 
					
						
							|  |  |  | 				t = t == null ? data.getImage(r, direction == 'before' ? 'after' : 'before') : t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// first/last image...
 | 
					
						
							|  |  |  | 			} else if(mode == 'first' || mode == 'last'){ | 
					
						
							|  |  |  | 				var t = data.getImage(mode, r) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 20:59:19 +03:00
										 |  |  | 			// unknown mode -- do nothing...
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2015-12-20 04:29:44 +03:00
										 |  |  | 				return | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this.focusImage(t, r) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	// XXX add undo...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	setBaseRibbon: ['Edit|Ribbon/Set base ribbon', { | 
					
						
							|  |  |  | 		journal: true, | 
					
						
							|  |  |  | 		browseMode: function(target){  | 
					
						
							|  |  |  | 			return this.current_ribbon == this.base && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.data.setBase(target) }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// shorthands...
 | 
					
						
							|  |  |  | 	// XXX do we reset direction on these???
 | 
					
						
							|  |  |  | 	firstImage: ['Navigate/First image in current ribbon', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Focus first image
 | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			Focus first image in current ribbon... | 
					
						
							|  |  |  | 			.firstImage() | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 			Focus first image globally... | 
					
						
							|  |  |  | 			.firstImage(true) | 
					
						
							|  |  |  | 			.firstImage('global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Shorthand for: | 
					
						
							|  |  |  | 			.focusImage(0) | 
					
						
							|  |  |  | 			.focusImage(0, 'global') | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(target){  | 
					
						
							|  |  |  | 			return this.data.getImageOrder('ribbon', target) == 0 && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		function(all){ this.focusImage(0, all == null ? 'ribbon' : 'global') }], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	lastImage: ['Navigate/Last image in current ribbon', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Focus last image...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Shorthand for: | 
					
						
							|  |  |  | 			.focusImage(-1) | 
					
						
							|  |  |  | 			.focusImage(-1, 'global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this is symmetrical to .firstImage(..) see docs for that. | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(target){  | 
					
						
							|  |  |  | 			return this.data.getImageOrder('ribbon', target)  | 
					
						
							|  |  |  | 				== this.data.getImageOrder('ribbon', -1) && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		function(all){ this.focusImage(-1, all == null ? 'ribbon' : 'global') }], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// XXX these break if image at first/last position are not loaded (crop, group, ...)
 | 
					
						
							|  |  |  | 	// XXX do we actually need these???
 | 
					
						
							|  |  |  | 	firstGlobalImage: ['Navigate/First image globally', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Get first image globally...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Shorthand for: | 
					
						
							|  |  |  | 			.firstImage('global') | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(){  | 
					
						
							|  |  |  | 			return this.data.getImageOrder() == 0 && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.firstImage(true) }], | 
					
						
							|  |  |  | 	lastGlobalImage: ['Navigate/Last image globally', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Get last image globally...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Shorthand for: | 
					
						
							|  |  |  | 			.lastImage('global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this symmetrical to .firstGlobalImage(..) see docs for that. | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(){  | 
					
						
							|  |  |  | 			return this.data.getImageOrder() == this.data.getImageOrder(-1) && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.lastImage(true) }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX skip unloaded images... (groups?)
 | 
					
						
							|  |  |  | 	// XXX the next two are almost identical...
 | 
					
						
							|  |  |  | 	prevImage: ['Navigate/Previous image', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Focus previous image
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this also modifies .direction | 
					
						
							| 
									
										
										
										
											2017-01-29 00:58:25 +03:00
										 |  |  | 		NOTE: this is .symmetrical to .nextImage(..) see it for docs. | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'firstImage'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(a){  | 
					
						
							|  |  |  | 			// keep track of traverse direction...
 | 
					
						
							|  |  |  | 			this.direction = 'left' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(typeof(a) == typeof(123)){ | 
					
						
							|  |  |  | 				// XXX should this force direction change???
 | 
					
						
							|  |  |  | 				this.focusImage(this.data.getImage('current', -a) | 
					
						
							|  |  |  | 						// go to the first image if it's closer than s...
 | 
					
						
							|  |  |  | 						|| this.data.getImage('first')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.focusImage('prev', a)  | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	nextImage: ['Navigate/Next image', | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		core.doc`Focus next image...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-29 00:58:25 +03:00
										 |  |  | 			Focus next image... | 
					
						
							|  |  |  | 			.nextImage() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus image at <offset> to the right... | 
					
						
							|  |  |  | 			.nextImage(<offset>) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus next image in <ribbon>... | 
					
						
							|  |  |  | 			.nextImage(<ribbon>) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Focus next image globally... | 
					
						
							|  |  |  | 			.nextImage('global') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this also modifies .direction | 
					
						
							| 
									
										
										
										
											2017-01-29 00:51:13 +03:00
										 |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'lastImage'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(a){  | 
					
						
							|  |  |  | 			// keep track of traverse direction...
 | 
					
						
							|  |  |  | 			this.direction = 'right' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(typeof(a) == typeof(123)){ | 
					
						
							|  |  |  | 				// XXX should this force direction change???
 | 
					
						
							|  |  |  | 				this.focusImage(this.data.getImage('current', a) | 
					
						
							|  |  |  | 						// go to the first image if it's closer than s...
 | 
					
						
							|  |  |  | 						|| this.data.getImage('last')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.focusImage('next', a)  | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX skip unloaded images... (groups?)
 | 
					
						
							|  |  |  | 	// XXX the next two are almost identical...
 | 
					
						
							|  |  |  | 	prevImageInOrder: ['Navigate/Previous image in order', | 
					
						
							|  |  |  | 		function(){  | 
					
						
							|  |  |  | 			// NOTE: this used to be algorithmically substantially slower
 | 
					
						
							|  |  |  | 			// 		than the code below but after .makeSparseImages(..)
 | 
					
						
							|  |  |  | 			// 		got updated the difference is far less... 
 | 
					
						
							|  |  |  | 			// 		...since I've already spent the time to write and 
 | 
					
						
							|  |  |  | 			// 		debug the long version and it gives a small advantage
 | 
					
						
							|  |  |  | 			// 		I'll keep it for now...
 | 
					
						
							|  |  |  | 			// 		(~15-20% @ 10K images, e.g 50ms vs 80ms on average)
 | 
					
						
							|  |  |  | 			//this.prevImage(this.data.getImages('loaded')) 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var c = {} | 
					
						
							|  |  |  | 			// get prev images for each ribbon...
 | 
					
						
							|  |  |  | 			for(var r in this.data.ribbons){ | 
					
						
							|  |  |  | 				var i = this.data.getImageOrder('prev', r) | 
					
						
							|  |  |  | 				if(i >= 0){ | 
					
						
							|  |  |  | 					c[i] = r | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.prevImage(c[Math.max.apply(null, Object.keys(c))]) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	nextImageInOrder: ['Navigate/Next image in order', | 
					
						
							|  |  |  | 		function(){  | 
					
						
							|  |  |  | 			// NOTE: this used to be algorithmically substantially slower
 | 
					
						
							|  |  |  | 			// 		than the code below but after .makeSparseImages(..)
 | 
					
						
							|  |  |  | 			// 		got updated the difference is far less... 
 | 
					
						
							|  |  |  | 			// 		...since I've already spent the time to write and 
 | 
					
						
							|  |  |  | 			// 		debug the long version and it gives a small advantage
 | 
					
						
							|  |  |  | 			// 		I'll keep it for now...
 | 
					
						
							|  |  |  | 			// 		(~15-20% @ 10K images)
 | 
					
						
							|  |  |  | 			//this.nextImage(this.data.getImages('loaded')) 
 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 			var c = {} | 
					
						
							|  |  |  | 			// get next images for each ribbon...
 | 
					
						
							|  |  |  | 			for(var r in this.data.ribbons){ | 
					
						
							|  |  |  | 				var i = this.data.getImageOrder('next', r) | 
					
						
							|  |  |  | 				if(i >= 0){ | 
					
						
							|  |  |  | 					c[i] = r | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.nextImage(c[Math.min.apply(null, Object.keys(c))]) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	firstRibbon: ['Navigate/First ribbon', | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(target){  | 
					
						
							|  |  |  | 			return this.data.getRibbonOrder(target) == 0 && 'disabled'}}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.focusRibbon('first') }], | 
					
						
							|  |  |  | 	lastRibbon: ['Navigate/Last ribbon', | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: function(target){  | 
					
						
							|  |  |  | 			return this.data.getRibbonOrder(target)  | 
					
						
							|  |  |  | 				== this.data.getRibbonOrder(-1) && 'disabled'}}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.focusRibbon('last') }], | 
					
						
							|  |  |  | 	prevRibbon: ['Navigate/Previous ribbon', | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'firstRibbon'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.focusRibbon('before') }], | 
					
						
							|  |  |  | 	nextRibbon: ['Navigate/Next ribbon', | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'lastRibbon'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.focusRibbon('after') }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// basic ribbon editing...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: for all of these, current/ribbon image is a default...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-26 15:52:23 +03:00
										 |  |  | 	// NOTE: resetting this option will clear the last direction...
 | 
					
						
							|  |  |  | 	toggleShiftsAffectDirection: ['Interface/Shifts affect direction', | 
					
						
							|  |  |  | 		core.makeConfigToggler('shifts-affect-direction',  | 
					
						
							|  |  |  | 			['off', 'on'], | 
					
						
							|  |  |  | 			function(action){ | 
					
						
							|  |  |  | 				if(action == 'on'){ | 
					
						
							|  |  |  | 					delete this._direction_last | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			})], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-28 20:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: this does not retain direction information, handle individual
 | 
					
						
							|  |  |  | 	// 		actions if that info is needed.
 | 
					
						
							|  |  |  | 	// NOTE: to make things clean, this is triggered in action handlers 
 | 
					
						
							|  |  |  | 	// 		below...
 | 
					
						
							| 
									
										
										
										
											2017-02-15 04:21:40 +03:00
										 |  |  | 	// XXX do we need a vertical shift event??
 | 
					
						
							| 
									
										
										
										
											2016-12-28 20:11:42 +03:00
										 |  |  | 	shiftImage: ['- Interface/Image shift (do not use directly)', | 
					
						
							|  |  |  | 		core.notUserCallable(function(gid){ | 
					
						
							|  |  |  | 			// This is the image shift protocol root function
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// Not for direct use.
 | 
					
						
							|  |  |  | 		})], | 
					
						
							| 
									
										
										
										
											2017-02-15 04:21:40 +03:00
										 |  |  | 	shiftImageOrder: ['- Interface/Image horizontal shift (do not use directly)', | 
					
						
							|  |  |  | 		core.notUserCallable(function(gid){ | 
					
						
							|  |  |  | 			// This is the image shift protocol root function
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// Not for direct use.
 | 
					
						
							|  |  |  | 		})], | 
					
						
							| 
									
										
										
										
											2016-12-28 20:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// XXX to be used for things like mark/place and dragging...
 | 
					
						
							|  |  |  | 	// XXX revise...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 19:43:25 +03:00
										 |  |  | 	// XXX undo...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageTo: ['- Edit|Sort|Image/', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | 		{undo: function(a){ this.shiftImageTo(a.args[1], a.args[0]) }}, | 
					
						
							| 
									
										
										
										
											2017-02-15 04:21:40 +03:00
										 |  |  | 		function(target, to){ this.data.shiftImage(target, to) }], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageUp: ['Edit|Image/Shift image up', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		'If implicitly shifting current image (i.e. no arguments), focus ' | 
					
						
							|  |  |  | 			+'will shift to the next or previous image in the current ' | 
					
						
							|  |  |  | 			+'ribbon depending on current direction.', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | 		{undo: undoShift('shiftImageDown')}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			// by default we need to focus another image in the same ribbon...
 | 
					
						
							|  |  |  | 			if(target == null){ | 
					
						
							|  |  |  | 				var direction = this.direction == 'right' ? 'next' : 'prev' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var cur = this.data.getImage() | 
					
						
							|  |  |  | 				var next = this.data.getImage(direction) | 
					
						
							|  |  |  | 				next = next == null  | 
					
						
							|  |  |  | 					? this.data.getImage(direction == 'next' ? 'prev' : 'next')  | 
					
						
							|  |  |  | 					: next | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				this.data.shiftImageUp(cur) | 
					
						
							|  |  |  | 				this.focusImage(next) | 
					
						
							| 
									
										
										
										
											2016-11-26 14:29:11 +03:00
										 |  |  | 				this.config['shifts-affect-direction'] == 'on' && (this.direction = '!') | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// if a specific target is given, just shift it...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.data.shiftImageUp(target) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageDown: ['Edit|Image/Shift image down', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		'If implicitly shifting current image (i.e. no arguments), focus ' | 
					
						
							|  |  |  | 			+'will shift to the next or previous image in the current ' | 
					
						
							|  |  |  | 			+'ribbon depending on current direction.', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | 		{undo: undoShift('shiftImageUp')}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			// by default we need to focus another image in the same ribbon...
 | 
					
						
							|  |  |  | 			if(target == null){ | 
					
						
							|  |  |  | 				var direction = this.direction == 'right' ? 'next' : 'prev' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var cur = this.data.getImage() | 
					
						
							|  |  |  | 				var next = this.data.getImage(direction) | 
					
						
							|  |  |  | 				next = next == null  | 
					
						
							|  |  |  | 					? this.data.getImage(direction == 'next' ? 'prev' : 'next')  | 
					
						
							|  |  |  | 					: next | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				this.data.shiftImageDown(cur) | 
					
						
							|  |  |  | 				this.focusImage(next) | 
					
						
							| 
									
										
										
										
											2016-11-26 14:29:11 +03:00
										 |  |  | 				this.config['shifts-affect-direction'] == 'on' && (this.direction = '!') | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// if a specific target is given, just shift it...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.data.shiftImageDown(target) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 19:43:25 +03:00
										 |  |  | 	// NOTE: we do not need undo here because it will be handled by 
 | 
					
						
							|  |  |  | 	// 		corresponding normal shift operations...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 19:51:16 +03:00
										 |  |  | 	// XXX .undoLast(..) on these for some reason skips...
 | 
					
						
							|  |  |  | 	// 		...e.g. two shifts are undone with three calls to .undoLast()...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageUpNewRibbon: ['Edit|Ribbon/Shift image up to a new empty ribbon', | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ | 
					
						
							|  |  |  | 			this.data.newRibbon(target) | 
					
						
							|  |  |  | 			this.shiftImageUp(target) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageDownNewRibbon: ['Edit|Ribbon/Shift image down to a new empty ribbon', | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ | 
					
						
							|  |  |  | 			this.data.newRibbon(target, 'below') | 
					
						
							|  |  |  | 			this.shiftImageDown(target) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageLeft: ['Edit|Sort|Image/Shift image left', { | 
					
						
							|  |  |  | 		undo: undoShift('shiftImageRight'), | 
					
						
							|  |  |  | 		browseMode: 'prevImage'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			if(target == null){ | 
					
						
							|  |  |  | 				this.direction = 'left' | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.data.shiftImageLeft(target)  | 
					
						
							|  |  |  | 			this.focusImage() | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftImageRight: ['Edit|Sort|Image/Shift image right', { | 
					
						
							|  |  |  | 		undo: undoShift('shiftImageLeft'), | 
					
						
							|  |  |  | 		browseMode: 'nextImage'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			if(target == null){ | 
					
						
							|  |  |  | 				this.direction = 'right' | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.data.shiftImageRight(target)  | 
					
						
							|  |  |  | 			this.focusImage() | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftRibbonUp: ['Ribbon|Edit|Sort/Shift ribbon up', { | 
					
						
							|  |  |  | 		undo: undoShift('shiftRibbonDown'), | 
					
						
							|  |  |  | 		browseMode: 'prevRibbon'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			this.data.shiftRibbonUp(target)  | 
					
						
							|  |  |  | 			// XXX is this the right way to go/???
 | 
					
						
							|  |  |  | 			this.focusImage() | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 	shiftRibbonDown: ['Ribbon|Edit|Sort/Shift ribbon down', { | 
					
						
							|  |  |  | 		undo: undoShift('shiftRibbonUp'), | 
					
						
							|  |  |  | 		browseMode: 'nextRibbon'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){  | 
					
						
							|  |  |  | 			this.data.shiftRibbonDown(target) | 
					
						
							|  |  |  | 			// XXX is this the right way to go/???
 | 
					
						
							|  |  |  | 			this.focusImage() | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// these operate on the current image...
 | 
					
						
							|  |  |  | 	travelImageUp: ['Edit/Travel with the current image up (Shift up and keep focus)', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | 		{undo: undoShift('travelImageDown')}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ | 
					
						
							|  |  |  | 			target = target || this.current | 
					
						
							|  |  |  | 			this.shiftImageUp(target) | 
					
						
							|  |  |  | 			this.focusImage(target) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	travelImageDown: ['Edit/Travel with the current image down (Shift down and keep focus)', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:46:40 +03:00
										 |  |  | 		{undo: undoShift('travelImageUp')}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ | 
					
						
							|  |  |  | 			target = target || this.current | 
					
						
							|  |  |  | 			this.shiftImageDown(target) | 
					
						
							|  |  |  | 			this.focusImage(target) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	reverseImages: ['Edit|Sort/Reverse image order', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'reverseImages'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.data.reverseImages() }], | 
					
						
							| 
									
										
										
										
											2016-08-07 02:42:25 +03:00
										 |  |  | 	reverseRibbons: ['Ribbon|Edit|Sort/Reverse ribbon order', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'reverseRibbons'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.data.reverseRibbons() }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX align to ribbon...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 	// basic image editing...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 	// Rotate image...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Rotate current image clockwise...
 | 
					
						
							|  |  |  | 	//	.rotate()
 | 
					
						
							|  |  |  | 	//	.rotate('cw')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Rotate current image counterclockwise...
 | 
					
						
							|  |  |  | 	//	.rotate('ccw')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Rotate target image clockwise...
 | 
					
						
							|  |  |  | 	//	.rotate(target)
 | 
					
						
							|  |  |  | 	//	.rotate(target, 'cw')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Rotate target image counterclockwise...
 | 
					
						
							|  |  |  | 	//	.rotate(target, 'ccw')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Flip is similar...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Flip current image ('horizontal' is default)...
 | 
					
						
							|  |  |  | 	//	.flip()
 | 
					
						
							|  |  |  | 	//	.flip('horizontal')
 | 
					
						
							|  |  |  | 	//	.flip('vertical')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Flip target...
 | 
					
						
							|  |  |  | 	//	.flip(target)
 | 
					
						
							|  |  |  | 	//	.flip(target, 'horizontal')
 | 
					
						
							|  |  |  | 	//	.flip(target, 'vertical')
 | 
					
						
							|  |  |  | 	//		-> actions
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: target must be .data.getImage(..) compatible, see it for docs...
 | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 	// XXX correct undo???
 | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 	rotate: ['- Image|Edit/Rotate image', | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 		function(target, direction){ | 
					
						
							| 
									
										
										
										
											2016-11-23 13:16:28 +03:00
										 |  |  | 			if(arguments.length == 0){ | 
					
						
							|  |  |  | 				return this.image && this.image.orientation || 0 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 			if(target == 'cw' || target == 'ccw'){ | 
					
						
							|  |  |  | 				direction = target | 
					
						
							|  |  |  | 				target = null | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 			this.images  | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 				&& this.images.rotateImage(this.data.getImage(target), direction || 'cw') | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-08-07 02:42:25 +03:00
										 |  |  | 	flip: ['- Image|Edit/Flip image', | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 		function(target, direction){ | 
					
						
							|  |  |  | 			if(target == 'vertical' || target == 'horizontal'){ | 
					
						
							|  |  |  | 				direction = target | 
					
						
							|  |  |  | 				target = null | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 			this.images  | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 				&& this.images.flipImage(this.data.getImage(target), direction || 'horizontal') | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// shorthands...
 | 
					
						
							|  |  |  | 	// NOTE: these are here mostly for the menus...
 | 
					
						
							|  |  |  | 	rotateCW: ['Image|Edit/Rotate image clockwise',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'rotateCCW'}, | 
					
						
							| 
									
										
										
										
											2016-06-24 15:09:13 +03:00
										 |  |  | 		function(target){ this.rotate(target, 'cw') }], | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 	rotateCCW: ['Image|Edit/Rotate image counterclockwise',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'rotateCW'}, | 
					
						
							| 
									
										
										
										
											2016-06-24 15:09:13 +03:00
										 |  |  | 		function(target){ this.rotate(target, 'ccw') }], | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 	flipVertical: ['Image|Edit/Flip image vertically', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'flipVertical'}, | 
					
						
							| 
									
										
										
										
											2016-06-24 15:09:13 +03:00
										 |  |  | 		function(target){ this.flip(target, 'vertical') }], | 
					
						
							| 
									
										
										
										
											2016-06-21 06:06:34 +03:00
										 |  |  | 	flipHorizontal: ['Image|Edit/Flip image horizontally', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'flipHorizontal'}, | 
					
						
							| 
									
										
										
										
											2016-06-24 15:09:13 +03:00
										 |  |  | 		function(target){ this.flip(target, 'horizontal') }], | 
					
						
							| 
									
										
										
										
											2016-07-01 03:44:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// complex operations...
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:35:20 +03:00
										 |  |  | 	// XXX need interactive mode for this...
 | 
					
						
							|  |  |  | 	// 		- on init: select start/end/base
 | 
					
						
							|  |  |  | 	// 		- allow user to reset/move
 | 
					
						
							|  |  |  | 	// 		- on accept: run
 | 
					
						
							| 
									
										
										
										
											2016-08-07 02:42:25 +03:00
										 |  |  | 	alignToRibbon: ['Ribbon|Edit/Align top ribbon to base', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2016-07-01 03:44:13 +03:00
										 |  |  | 		function(target, start, end){ | 
					
						
							| 
									
										
										
										
											2017-01-01 07:58:55 +03:00
										 |  |  | 			this.data = this.data.alignToRibbon(target, start, end) }], | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var Base = | 
					
						
							|  |  |  | module.Base = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: 'ImageGrid base', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tag: 'base', | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'changes', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 	/* | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 	suggested: [ | 
					
						
							|  |  |  | 		'tags', | 
					
						
							|  |  |  | 		'sort', | 
					
						
							| 
									
										
										
										
											2016-04-27 08:59:13 +03:00
										 |  |  | 		'tasks', | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 	//*/
 | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	actions: BaseActions, | 
					
						
							| 
									
										
										
										
											2016-12-28 20:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			'shiftImageTo', | 
					
						
							|  |  |  | 			'shiftImageUp', | 
					
						
							|  |  |  | 			'shiftImageDown', | 
					
						
							|  |  |  | 			'shiftImageLeft', | 
					
						
							|  |  |  | 			'shiftImageRight', | 
					
						
							|  |  |  | 		],  | 
					
						
							|  |  |  | 			function(){ this.shiftImage.apply(this, [].slice(arguments, 1))}], | 
					
						
							| 
									
										
										
										
											2017-02-15 04:21:40 +03:00
										 |  |  | 		// horizontal shifting...
 | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			'shiftImageLeft', | 
					
						
							|  |  |  | 			'shiftImageRight', | 
					
						
							|  |  |  | 		],  | 
					
						
							|  |  |  | 			function(){ this.shiftImageOrder.apply(this, [].slice(arguments, 1))}], | 
					
						
							|  |  |  | 		['shiftImageTo.pre', | 
					
						
							|  |  |  | 			function(a){ | 
					
						
							|  |  |  | 				var i = this.data.getImageOrder(a) | 
					
						
							|  |  |  | 				return function(){ | 
					
						
							|  |  |  | 					// only trigger if order changed...
 | 
					
						
							|  |  |  | 					i != this.data.getImageOrder(a) | 
					
						
							|  |  |  | 						&& this.shiftImageOrder.apply(this, [].slice(arguments, 1)) } }], | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// manage changes...
 | 
					
						
							|  |  |  | 		// everything changed...
 | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			'claer', | 
					
						
							|  |  |  | 			'loadURLs',  | 
					
						
							|  |  |  | 		], | 
					
						
							|  |  |  | 			function(){ this.markChanged('all') }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// data...
 | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			//'load',
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'setBaseRibbon', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'shiftImageTo', | 
					
						
							|  |  |  | 			'shiftImageUp', | 
					
						
							|  |  |  | 			'shiftImageDown', | 
					
						
							|  |  |  | 			'shiftImageLeft', | 
					
						
							|  |  |  | 			'shiftImageRight', | 
					
						
							|  |  |  | 			'shiftRibbonUp', | 
					
						
							|  |  |  | 			'shiftRibbonDown', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'reverseImages', | 
					
						
							|  |  |  | 			'reverseRibbons', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'alignToRibbon', | 
					
						
							|  |  |  | 		],  | 
					
						
							|  |  |  | 			function(_, target){ this.markChanged('data') }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// image specific...
 | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			'rotateCW', | 
					
						
							|  |  |  | 			'rotateCCW', | 
					
						
							|  |  |  | 			'flipHorizontal', | 
					
						
							|  |  |  | 			'flipVertical', | 
					
						
							|  |  |  | 		],  | 
					
						
							| 
									
										
										
										
											2017-02-15 03:28:18 +03:00
										 |  |  | 			function(_, target){ this.markChanged('images', [this.data.getImage(target)]) }], | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-28 20:11:42 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Tags...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 01:23:33 +03:00
										 |  |  | // mode can be:
 | 
					
						
							|  |  |  | // 	"ribbon"	- next marked in current ribbon (default)
 | 
					
						
							|  |  |  | // 	"all"		- next marked in sequence
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX add support for tag lists...
 | 
					
						
							|  |  |  | var makeTagWalker = | 
					
						
							|  |  |  | module.makeTagWalker = | 
					
						
							|  |  |  | function(direction, dfl_tag){ | 
					
						
							|  |  |  | 	var meth = direction == 'next' ? 'nextImage' : 'prevImage' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return function(tag, mode){ | 
					
						
							|  |  |  | 		mode = mode == null ? 'all' : mode | 
					
						
							|  |  |  | 		tag = tag || dfl_tag | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// account for no tags or no images tagged...
 | 
					
						
							|  |  |  | 		var lst = this.data.tags != null ? this.data.tags[tag] : [] | 
					
						
							|  |  |  | 		lst = lst || [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(mode == 'ribbon'){ | 
					
						
							|  |  |  | 			this[meth](this.data.getImages(lst, 'current')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this[meth](lst) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | var TagsActions =  | 
					
						
							|  |  |  | module.TagsActions = actions.Actions({ | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// tags...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX mark updated...
 | 
					
						
							|  |  |  | 	tag: ['- Tag/Tag image(s)', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(tags, gids){ | 
					
						
							|  |  |  | 			gids = gids || this.current | 
					
						
							|  |  |  | 			gids = gids.constructor !== Array ? [gids] : gids | 
					
						
							|  |  |  | 			tags = tags.constructor !== Array ? [tags] : tags | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// data...
 | 
					
						
							|  |  |  | 			this.data.tag(tags, gids) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// images...
 | 
					
						
							|  |  |  | 			var images = this.images | 
					
						
							|  |  |  | 			gids.forEach(function(gid){ | 
					
						
							|  |  |  | 				var img = images[gid] | 
					
						
							|  |  |  | 				if(img == null){ | 
					
						
							|  |  |  | 					img = images[gid] = {} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if(img.tags == null){ | 
					
						
							|  |  |  | 					img.tags = [] | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				img.tags = img.tags.concat(tags).unique() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// XXX mark updated...
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	// XXX mark updated...
 | 
					
						
							|  |  |  | 	untag: ['- Tag/Untag image(s)', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(tags, gids){ | 
					
						
							|  |  |  | 			gids = gids || this.current | 
					
						
							|  |  |  | 			gids = gids.constructor !== Array ? [gids] : gids | 
					
						
							|  |  |  | 			tags = tags.constructor !== Array ? [tags] : tags | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// data...
 | 
					
						
							|  |  |  | 			this.data.untag(tags, gids) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// images...
 | 
					
						
							|  |  |  | 			var images = this.images | 
					
						
							|  |  |  | 			gids.forEach(function(gid){ | 
					
						
							|  |  |  | 				var img = images[gid] | 
					
						
							|  |  |  | 				if(img == null || img.tags == null){ | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				img.tags = img.tags.filter(function(tag){ return tags.indexOf(tag) < 0 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(img.tags.length == 0){ | 
					
						
							|  |  |  | 					delete img.tags | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// XXX mark updated...
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	// Sync tags...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Sync both ways...
 | 
					
						
							|  |  |  | 	//	.syncTags()
 | 
					
						
							|  |  |  | 	//	.syncTags('both')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Sync from .data
 | 
					
						
							|  |  |  | 	//	.syncTags('data')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Sync from .images
 | 
					
						
							|  |  |  | 	//	.syncTags('images')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Sync from <images> object
 | 
					
						
							|  |  |  | 	//	.syncTags(<images>)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: mode is data.tagsToImages(..) / data.tagsFromImages(..) 
 | 
					
						
							|  |  |  | 	// 		compatible...
 | 
					
						
							|  |  |  | 	// NOTE: setting source to 'both' and mode to 'reset' is the same as
 | 
					
						
							|  |  |  | 	// 		'images' and 'reset' as all .data tags will be lost on first 
 | 
					
						
							|  |  |  | 	// 		pass...
 | 
					
						
							|  |  |  | 	syncTags: ['Tag/Synchoronize tags between data and images', | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(source, mode){ | 
					
						
							|  |  |  | 			// can't do anything if either .data or .images are not 
 | 
					
						
							|  |  |  | 			// defined...
 | 
					
						
							| 
									
										
										
										
											2016-11-28 20:08:08 +03:00
										 |  |  | 			if(this.images == null){ | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			source = source || 'both' | 
					
						
							|  |  |  | 			mode = mode || 'merge' | 
					
						
							|  |  |  | 			images = this.images | 
					
						
							|  |  |  | 			if(typeof(source) != typeof('str')){ | 
					
						
							|  |  |  | 				images = source | 
					
						
							|  |  |  | 				source = 'images' | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(source == 'data' || source == 'both'){ | 
					
						
							|  |  |  | 				this.data.tagsToImages(images, mode) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(source == 'images' || source == 'both'){ | 
					
						
							|  |  |  | 				this.data.tagsFromImages(images, mode) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2015-12-20 20:59:19 +03:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	prevTagged: ['- Navigate/Previous image tagged with tag', | 
					
						
							|  |  |  | 		makeTagWalker('prev')], | 
					
						
							|  |  |  | 	nextTagged: ['- Navigate/Next image tagged with tag', | 
					
						
							|  |  |  | 		makeTagWalker('next')], | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var Tags = | 
					
						
							|  |  |  | module.Tags = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: '', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tag: 'tags', | 
					
						
							|  |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'base', | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 		'changes', | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	actions: TagsActions, | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							|  |  |  | 		// tags and images...
 | 
					
						
							|  |  |  | 		// NOTE: tags are also stored in images...
 | 
					
						
							|  |  |  | 		['tag untag', | 
					
						
							|  |  |  | 			function(_, tags, gids){ | 
					
						
							|  |  |  | 				var that = this | 
					
						
							|  |  |  | 				var changes = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				gids = gids || [this.data.getImage()] | 
					
						
							|  |  |  | 				gids = gids.constructor !== Array ?  | 
					
						
							|  |  |  | 					[this.data.getImage(gids)]  | 
					
						
							|  |  |  | 					: gids | 
					
						
							|  |  |  | 						.map(function(e){ return that.data.getImage(e) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				tags = tags || [] | 
					
						
							|  |  |  | 				tags = tags.constructor !== Array ? [tags] : tags | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// tags...
 | 
					
						
							|  |  |  | 				if(tags.length > 0){ | 
					
						
							|  |  |  | 					this.markChanged('tags') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					tags.indexOf('selected') >= 0 | 
					
						
							|  |  |  | 						&& this.markChanged('selected') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					tags.indexOf('bookmark') >= 0 | 
					
						
							|  |  |  | 						&& this.markChanged('bookmarked') | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				this.markChanged('images', gids) | 
					
						
							|  |  |  | 			}], | 
					
						
							| 
									
										
										
										
											2017-02-15 05:21:28 +03:00
										 |  |  | 		// XXX
 | 
					
						
							|  |  |  | 		['prepareIndexForWrite',  | 
					
						
							|  |  |  | 			function(res, _, full){ | 
					
						
							|  |  |  | 				// XXX move code here from file.buildIndex(..)
 | 
					
						
							|  |  |  | 				// 		- res.raw.tags -> res.index.tags
 | 
					
						
							|  |  |  | 				// 		- ..tags.selected -> .selected
 | 
					
						
							|  |  |  | 				// 		- ..tags.bookmark -> .bookmarked
 | 
					
						
							|  |  |  | 				// XXX will need a symmetrical action to reverse all of this...
 | 
					
						
							|  |  |  | 			}], | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Crop...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var CropActions = | 
					
						
							|  |  |  | module.CropActions = actions.Actions({ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	crop_stack: null, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	// load the crop stack if present...
 | 
					
						
							|  |  |  | 	load: [function(data){ | 
					
						
							| 
									
										
										
										
											2016-06-08 18:10:39 +03:00
										 |  |  | 		// clear previous crop state...
 | 
					
						
							|  |  |  | 		delete this.crop_stack | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 		if(data.crop_stack){ | 
					
						
							|  |  |  | 			this.crop_stack = data.crop_stack.map(function(j){ | 
					
						
							|  |  |  | 				return data.Data(j) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}], | 
					
						
							| 
									
										
										
										
											2016-11-28 20:18:34 +03:00
										 |  |  | 	clear: [function(){  | 
					
						
							|  |  |  | 		delete this.crop_stack }], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	// store the root crop state instead of the current view...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// modes supported:
 | 
					
						
							|  |  |  | 	// 	- current	- store the current state/view
 | 
					
						
							|  |  |  | 	// 	- base		- store the base state/view
 | 
					
						
							|  |  |  | 	// 	- full		- store the crop stack
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX might need to revise the mode approach...
 | 
					
						
							|  |  |  | 	// XXX add support to loading the states...
 | 
					
						
							|  |  |  | 	json: [function(mode){ | 
					
						
							|  |  |  | 		mode = mode || 'current' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return function(res){ | 
					
						
							|  |  |  | 			if(mode == 'base'  | 
					
						
							|  |  |  | 					&& this.crop_stack  | 
					
						
							|  |  |  | 					&& this.crop_stack.length > 0){ | 
					
						
							|  |  |  | 				res.data = this.crop_stack[0].dumpJSON() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(mode == 'full'  | 
					
						
							|  |  |  | 					&& this.crop_stack  | 
					
						
							|  |  |  | 					&& this.crop_stack.length > 0){ | 
					
						
							|  |  |  | 				res.crop_stack = this.crop_stack.map(function(c){ | 
					
						
							|  |  |  | 					return c.dumpJSON() | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 17:31:11 +03:00
										 |  |  | 	// true if current viewer is cropped...
 | 
					
						
							|  |  |  | 	get cropped(){ | 
					
						
							| 
									
										
										
										
											2016-06-08 18:10:39 +03:00
										 |  |  | 		return this.crop_stack != null }, | 
					
						
							| 
									
										
										
										
											2016-04-05 17:31:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// crop...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	// XXX check undo... do we actually need it???
 | 
					
						
							| 
									
										
										
										
											2016-12-30 04:24:38 +03:00
										 |  |  | 	crop: ['Crop/Crop', | 
					
						
							| 
									
										
										
										
											2017-01-27 06:48:24 +03:00
										 |  |  | 		core.doc`Crop current state and push it to the crop stack
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		A crop is a copy of the data state. When a crop is made the old  | 
					
						
							|  |  |  | 		state is pushed to the crop stack and a new state is set in it  | 
					
						
							|  |  |  | 		its place. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		If true (flatten) is passed as the last argument the crop will  | 
					
						
							|  |  |  | 		be flattened, i.e. ribbons will be merged. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		This is the base crop action/event, so this should be called by | 
					
						
							|  |  |  | 		any action implementing a crop. | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			Make a full crop... | 
					
						
							|  |  |  | 			.crop() | 
					
						
							|  |  |  | 			.crop(true) | 
					
						
							|  |  |  | 				-> this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Make a crop keeping only the list of images... | 
					
						
							|  |  |  | 			.crop(images) | 
					
						
							|  |  |  | 			.crop(images, true) | 
					
						
							|  |  |  | 				-> this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Make a crop and use the given data object... | 
					
						
							|  |  |  | 			NOTE: data must be an instance of data.Data | 
					
						
							|  |  |  | 			.crop(data) | 
					
						
							|  |  |  | 			.crop(data, true) | 
					
						
							|  |  |  | 				-> this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this is used as a basis for all the crop operations, so  | 
					
						
							|  |  |  | 			there is no need to bind to anything but this to handle a  | 
					
						
							|  |  |  | 			crop unless specific action is required for a specific crop | 
					
						
							|  |  |  | 			operation. | 
					
						
							|  |  |  | 		NOTE: this is an in-place operation, to make a crop in a new  | 
					
						
							|  |  |  | 			instance use .clone().crop(..) | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{undo: 'uncrop'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(list, flatten){  | 
					
						
							| 
									
										
										
										
											2016-10-21 18:51:42 +03:00
										 |  |  | 			list = list || this.data.getImages() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			if(this.crop_stack == null){ | 
					
						
							|  |  |  | 				this.crop_stack = [] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			this.crop_stack.push(this.data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(list instanceof data.Data){ | 
					
						
							|  |  |  | 				this.data = list  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.data = this.data.crop(list, flatten) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2016-12-29 23:23:05 +03:00
										 |  |  | 	uncrop: ['Crop/Uncrop', | 
					
						
							| 
									
										
										
										
											2016-12-30 03:21:09 +03:00
										 |  |  | 		{browseMode: function(){  | 
					
						
							|  |  |  | 			return (this.crop_stack && this.crop_stack.length > 0) || 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(level, restore_current, keep_crop_order){ | 
					
						
							|  |  |  | 			level = level || 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var cur = this.current | 
					
						
							|  |  |  | 			var order = this.data.order | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(this.crop_stack == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// uncrop all...
 | 
					
						
							|  |  |  | 			if(level == 'all'){ | 
					
						
							|  |  |  | 				this.data = this.crop_stack[0] | 
					
						
							|  |  |  | 				this.crop_stack = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// get the element at level and drop the tail...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.data = this.crop_stack.splice(-level, this.crop_stack.length)[0] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// by default set the current from the crop...
 | 
					
						
							|  |  |  | 			if(!restore_current){ | 
					
						
							|  |  |  | 				this.data.focusImage(cur) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// restore order from the crop...
 | 
					
						
							|  |  |  | 			if(keep_crop_order){ | 
					
						
							|  |  |  | 				this.data.order = order | 
					
						
							|  |  |  | 				this.data.updateImagePositions() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// purge the stack...
 | 
					
						
							|  |  |  | 			if(this.crop_stack.length == 0){ | 
					
						
							|  |  |  | 				delete this.crop_stack | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	uncropAll: ['Crop/Uncrop all', | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'uncrop'}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(restore_current){ this.uncrop('all', restore_current) }], | 
					
						
							|  |  |  | 	// XXX see if we need to do this on this level??
 | 
					
						
							|  |  |  | 	// 		...might be a good idea to do this in data...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	uncropAndKeepOrder: ['Crop|Edit/Uncrop and keep crop image order', { | 
					
						
							|  |  |  | 		journal: true, | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		browseMode: 'uncrop'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(level, restore_current){ this.uncrop(level, restore_current, true) }], | 
					
						
							|  |  |  | 	// XXX same as uncrop but will also try and merge changes...
 | 
					
						
							|  |  |  | 	// 		- the order is simple and already done above...
 | 
					
						
							|  |  |  | 	// 		- I think that levels should be relative to images, the 
 | 
					
						
							|  |  |  | 	// 		  only problem here is how to deal with new ribbons...
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	mergeCrop: ['- Crop|Edit/Merge crop', { | 
					
						
							|  |  |  | 		journal: true, | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		browseMode: 'uncrop'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX save a crop (catalog)...
 | 
					
						
							|  |  |  | 	// XXX
 | 
					
						
							| 
									
										
										
										
											2016-10-21 18:51:42 +03:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	// XXX not sure if we actually need this...
 | 
					
						
							|  |  |  | 	cropFlatten: ['Crop/Flatten', | 
					
						
							| 
									
										
										
										
											2016-11-28 20:18:34 +03:00
										 |  |  | 		function(list){ this.data.length > 0 && this.crop(list, true) }], | 
					
						
							| 
									
										
										
										
											2017-01-26 21:49:32 +03:00
										 |  |  | 	cropRibbon: ['Crop/Crop ribbon', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(ribbon, flatten){ | 
					
						
							| 
									
										
										
										
											2016-11-28 20:18:34 +03:00
										 |  |  | 			if(this.data.length == 0){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			if(typeof(ribbon) == typeof(true)){ | 
					
						
							|  |  |  | 				flatten = ribbon | 
					
						
							|  |  |  | 				ribbon = null | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ribbon = ribbon || 'current' | 
					
						
							|  |  |  | 			this.crop(this.data.getImages(ribbon), flatten) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2017-01-26 21:49:32 +03:00
										 |  |  | 	cropOutRibbon: ['Crop/Crop ribbon out', | 
					
						
							|  |  |  | 		function(ribbon, flatten){ | 
					
						
							|  |  |  | 			ribbon = ribbon || this.current_ribbon | 
					
						
							|  |  |  | 			ribbon = ribbon instanceof Array ? ribbon : [ribbon] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// build the crop...
 | 
					
						
							|  |  |  | 			var crop = this.data.crop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// ribbon order...
 | 
					
						
							|  |  |  | 			crop.ribbon_order = crop.ribbon_order | 
					
						
							|  |  |  | 				.filter(function(r){ return ribbon.indexOf(r) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// ribbons...
 | 
					
						
							|  |  |  | 			ribbon.forEach(function(r){ delete crop.ribbons[r] }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// focus image...
 | 
					
						
							|  |  |  | 			var cr = this.current_ribbon | 
					
						
							|  |  |  | 			if(ribbon.indexOf(cr) >= 0){ | 
					
						
							|  |  |  | 				var i = this.data.getRibbonOrder(cr) | 
					
						
							|  |  |  | 				var r = this.data.ribbon_order | 
					
						
							|  |  |  | 					.slice(i+1) | 
					
						
							|  |  |  | 					.concat(this.data.ribbon_order.slice(0, i)) | 
					
						
							|  |  |  | 					.filter(function(r){ return crop.ribbons[r] && crop.ribbons[r].len > 0 }) | 
					
						
							|  |  |  | 					.shift() | 
					
						
							|  |  |  | 				crop.focusImage( | 
					
						
							|  |  |  | 					crop.getImage(this.current, 'after', r) | 
					
						
							|  |  |  | 						|| crop.getImage(this.current, 'before', r)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this.crop(crop, flatten) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	cropOutRibbonsBelow: ['Crop/Crop out ribbons bellow', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(ribbon, flatten){ | 
					
						
							| 
									
										
										
										
											2016-11-28 20:18:34 +03:00
										 |  |  | 			if(this.data.length == 0){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			if(typeof(ribbon) == typeof(true)){ | 
					
						
							|  |  |  | 				flatten = ribbon | 
					
						
							|  |  |  | 				ribbon = null | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ribbon = ribbon || this.data.getRibbon() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var data = this.data | 
					
						
							| 
									
										
										
										
											2016-11-28 20:08:08 +03:00
										 |  |  | 			if(data == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 20:08:08 +03:00
										 |  |  | 			var that = this | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			var i = data.ribbon_order.indexOf(ribbon) | 
					
						
							|  |  |  | 			var ribbons = data.ribbon_order.slice(0, i) | 
					
						
							|  |  |  | 			var images = ribbons | 
					
						
							|  |  |  | 				.reduce(function(a, b){  | 
					
						
							|  |  |  | 						return data.getImages(a).concat(data.getImages(b))  | 
					
						
							|  |  |  | 					}, data.getImages(ribbon)) | 
					
						
							|  |  |  | 				.compact() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this.crop(data.getImages(images), flatten) | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// XXX should this be here???
 | 
					
						
							|  |  |  | 	cropTagged: ['Tag|Crop/Crop tagged images', | 
					
						
							|  |  |  | 		function(tags, mode, flatten){ | 
					
						
							| 
									
										
										
										
											2016-11-28 20:18:34 +03:00
										 |  |  | 			if(this.data.length == 0){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 			var selector = mode == 'any' ? 'getTaggedByAny' : 'getTaggedByAll' | 
					
						
							|  |  |  | 			this.crop(this.data[selector](tags), flatten) | 
					
						
							|  |  |  | 		}], | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | }) | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | var Crop = | 
					
						
							|  |  |  | module.Crop = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: '', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tag: 'crop', | 
					
						
							|  |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'base', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	actions: CropActions, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Image Group...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ImageGroupActions = | 
					
						
							|  |  |  | module.ImageGroupActions = actions.Actions({ | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 	// grouping...
 | 
					
						
							|  |  |  | 	// XXX need to tell .images about this...
 | 
					
						
							|  |  |  | 	group: ['- Group|Edit/Group images',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(gids, group){ this.data.group(gids, group) }], | 
					
						
							|  |  |  | 	ungroup: ['Group|Edit/Ungroup images',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2016-12-30 03:54:10 +03:00
										 |  |  | 		{browseMode: function(){ | 
					
						
							|  |  |  | 			return this.data.getGroup() == null && 'disabled' }}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(gids, group){ this.data.ungroup(gids, group) }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// direction can be:
 | 
					
						
							|  |  |  | 	// 	'next'
 | 
					
						
							|  |  |  | 	// 	'prev'
 | 
					
						
							|  |  |  | 	groupTo: ['- Group|Edit/Group to',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target, direction){ | 
					
						
							|  |  |  | 			target = this.data.getImage(target) | 
					
						
							|  |  |  | 			var other = this.data.getImage(target, direction == 'next' ? 1 : -1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// we are start/end of ribbon...
 | 
					
						
							|  |  |  | 			if(other == null){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			// add into an existing group...
 | 
					
						
							|  |  |  | 			if(this.data.isGroup(other)){ | 
					
						
							|  |  |  | 				this.group(target, other) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// new group...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.group([target, other]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	// shorthands to .groupTo(..)
 | 
					
						
							| 
									
										
										
										
											2017-01-03 07:26:25 +03:00
										 |  |  | 	groupBack: ['Group|Edit/Group backwards',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.groupTo(target, 'prev') }], | 
					
						
							| 
									
										
										
										
											2017-01-03 07:26:25 +03:00
										 |  |  | 	groupForward: ['Group|Edit/Group forwards',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.groupTo(target, 'next') }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: this will only group loaded images...
 | 
					
						
							|  |  |  | 	groupMarked: ['Group|Mark/Group loaded marked images',  | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 		{journal: true}, | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(){ this.group(this.data.getImages(this.data.getTaggedByAny('marked'))) }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expandGroup: ['Group/Expand group',  | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		{browseMode: 'ungroup'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.data.expandGroup(target || this.current) }], | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	collapseGroup: ['Group/Collapse group', { | 
					
						
							|  |  |  | 		journal: true, | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		browseMode: 'ungroup'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.data.collapseGroup(target || this.current) }], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-31 07:10:28 +03:00
										 |  |  | 	cropGroup: ['Crop|Group/Crop group', { | 
					
						
							|  |  |  | 		journal: true, | 
					
						
							| 
									
										
										
										
											2016-12-31 23:30:14 +03:00
										 |  |  | 		browseMode: 'ungroup'},  | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 		function(target){ this.crop(this.data.cropGroup(target || this.current)) }], | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | var ImageGroup = | 
					
						
							|  |  |  | module.ImageGroup = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: '', | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	tag: 'image-group', | 
					
						
							|  |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'base', | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 		'changes', | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	actions: ImageGroupActions, | 
					
						
							| 
									
										
										
										
											2017-02-13 05:47:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							|  |  |  | 		[[ | 
					
						
							|  |  |  | 			'group', | 
					
						
							|  |  |  | 			'ungroup', | 
					
						
							|  |  |  | 			'expandGroup', | 
					
						
							|  |  |  | 			'collapseGroup', | 
					
						
							|  |  |  | 		],  | 
					
						
							|  |  |  | 			function(_, target){ this.markChanged('data') }], | 
					
						
							|  |  |  | 	], | 
					
						
							| 
									
										
										
										
											2015-12-17 03:34:20 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Meta base features...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // full features base...
 | 
					
						
							|  |  |  | core.ImageGridFeatures.Feature('base-full', [ | 
					
						
							| 
									
										
										
										
											2016-05-01 22:41:03 +03:00
										 |  |  | 	'introspection', | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	'base', | 
					
						
							|  |  |  | 	'tags', | 
					
						
							| 
									
										
										
										
											2016-04-22 16:13:20 +03:00
										 |  |  | 	'sort', | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +03:00
										 |  |  | 	'crop', | 
					
						
							|  |  |  | 	'image-group', | 
					
						
							| 
									
										
										
										
											2016-04-29 17:59:17 +03:00
										 |  |  | 	'tasks', | 
					
						
							| 
									
										
										
										
											2015-12-20 08:12:56 +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 }) |