| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 23:03:19 +03:00
										 |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | // XXX this is a hack...
 | 
					
						
							|  |  |  | // 		...need a way to escape these so as not to load them in browser...
 | 
					
						
							|  |  |  | if(typeof(process) != 'undefined'){ | 
					
						
							| 
									
										
										
										
											2017-07-23 23:03:19 +03:00
										 |  |  | 	var fs = requirejs('fs') | 
					
						
							|  |  |  | 	var path = requirejs('path') | 
					
						
							|  |  |  | 	var exiftool = requirejs('exiftool') | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// XXX EXPERIMENTAL: graph...
 | 
					
						
							| 
									
										
										
										
											2020-05-17 03:46:57 +03:00
										 |  |  | 	// do this only if browser is loaded...
 | 
					
						
							| 
									
										
										
										
											2020-06-06 14:58:08 +03:00
										 |  |  | 	var graph = typeof(window) != 'undefined' ? | 
					
						
							| 
									
										
										
										
											2020-05-17 03:46:57 +03:00
										 |  |  | 		requirejs('lib/components/ig-image-graph') | 
					
						
							|  |  |  | 		: null | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | var util = require('lib/util') | 
					
						
							|  |  |  | var toggler = require('lib/toggler') | 
					
						
							| 
									
										
										
										
											2016-03-26 04:01:13 +03:00
										 |  |  | var keyboard = require('lib/keyboard') | 
					
						
							| 
									
										
										
										
											2016-01-18 05:20:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | var actions = require('lib/actions') | 
					
						
							|  |  |  | var core = require('features/core') | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | var base = require('features/base') | 
					
						
							| 
									
										
										
										
											2016-05-04 21:28:46 +03:00
										 |  |  | var widgets = require('features/ui-widgets') | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-15 03:40:52 +03:00
										 |  |  | var browse = require('lib/widget/browse') | 
					
						
							|  |  |  | var overlay = require('lib/widget/overlay') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | // XXX make metadata a prop of image... (???)
 | 
					
						
							|  |  |  | // XXX Q: should we standardise metadata field names and adapt them to 
 | 
					
						
							|  |  |  | // 		lib???
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | var MetadataActions = actions.Actions({ | 
					
						
							| 
									
										
										
										
											2017-10-15 04:52:46 +03:00
										 |  |  | 	// XXX 
 | 
					
						
							|  |  |  | 	metadata: ['- Image/', | 
					
						
							|  |  |  | 		function(target, data){ | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	getMetadata: ['- Image/Get metadata data', | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 		function(image){ | 
					
						
							|  |  |  | 			var gid = this.data.getImage(image) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(this.images && this.images[gid]){ | 
					
						
							|  |  |  | 				return this.images[gid].metadata || {} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 			return null }], | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	setMetadata: ['- Image/Set metadata data', | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 		function(image, metadata, merge){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 			var gid = this.data.getImage(image) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(this.images && this.images[gid]){ | 
					
						
							|  |  |  | 				if(merge){ | 
					
						
							|  |  |  | 					var m = this.images[gid].metadata | 
					
						
							|  |  |  | 					Object.keys(metadata).forEach(function(k){ | 
					
						
							|  |  |  | 						m[k] = metadata[k] | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 					this.images[gid].metadata = metadata } } }] | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | var Metadata =  | 
					
						
							|  |  |  | module.Metadata = core.ImageGridFeatures.Feature({ | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 	title: '', | 
					
						
							|  |  |  | 	doc: '', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	tag: 'metadata', | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'base', | 
					
						
							|  |  |  | 	], | 
					
						
							| 
									
										
										
										
											2016-04-02 19:30:48 +03:00
										 |  |  | 	suggested: [ | 
					
						
							|  |  |  | 		'fs-metadata', | 
					
						
							|  |  |  | 		'ui-metadata', | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 		'ui-fs-metadata', | 
					
						
							| 
									
										
										
										
											2016-04-02 19:30:48 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	actions: MetadataActions, | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | // Metadata reader/writer...
 | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | // XXX add Metadata writer...
 | 
					
						
							|  |  |  | var MetadataReaderActions = actions.Actions({ | 
					
						
							| 
									
										
										
										
											2016-04-23 00:07:04 +03:00
										 |  |  | 	// NOTE: this will read both stat and metadata...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 	// XXX also check the metadata/ folder (???)
 | 
					
						
							|  |  |  | 	// XXX this uses .markChanged(..) form filesystem.FileSystemWriter 
 | 
					
						
							|  |  |  | 	// 		feature, but technically does not depend on it...
 | 
					
						
							|  |  |  | 	// XXX should we store metadata in an image (current) or in fs???
 | 
					
						
							| 
									
										
										
										
											2020-11-05 22:46:27 +03:00
										 |  |  | 	// XXX should this set .orientation / .flipped if they are not set???
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	readMetadata: ['- Image/Get metadata data', | 
					
						
							| 
									
										
										
										
											2020-11-05 22:46:27 +03:00
										 |  |  | 		core.doc`
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		This will overwrite/update if: | 
					
						
							|  |  |  | 			- image .metadata is not set | 
					
						
							|  |  |  | 			- image .metadata.ImageGridMetadata is not 'full' | 
					
						
							|  |  |  | 			- force is true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: also see: .cacheMetadata(..) | 
					
						
							|  |  |  | 		`,
 | 
					
						
							| 
									
										
										
										
											2020-12-08 02:35:06 +03:00
										 |  |  | 		core.sessionQueueHandler('Read image metadata',  | 
					
						
							| 
									
										
										
										
											2020-12-03 04:03:20 +03:00
										 |  |  | 			{quiet: true},  | 
					
						
							|  |  |  | 			function(queue, image, force){ | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 				return [ | 
					
						
							| 
									
										
										
										
											2020-12-03 04:03:20 +03:00
										 |  |  | 					image == 'all' ? | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 							this.images.keys() | 
					
						
							| 
									
										
										
										
											2020-12-03 04:03:20 +03:00
										 |  |  | 						: image == 'loaded' ? | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 							this.data.getImages('loaded') | 
					
						
							| 
									
										
										
										
											2020-12-03 04:03:20 +03:00
										 |  |  | 						: image, | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 					force,  | 
					
						
							|  |  |  | 				] }, | 
					
						
							|  |  |  | 			function(image, force){ | 
					
						
							|  |  |  | 				var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var gid = this.data.getImage(image) | 
					
						
							|  |  |  | 				var img = this.images && this.images[gid] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(!image && !img){ | 
					
						
							|  |  |  | 					return false } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				//var full_path = path.normalize(img.base_path +'/'+ img.path)
 | 
					
						
							|  |  |  | 				var full_path = this.getImagePath(gid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return new Promise(function(resolve, reject){ | 
					
						
							|  |  |  | 					if(!force  | 
					
						
							|  |  |  | 							&& (img.metadata || {}).ImageGridMetadata == 'full'){ | 
					
						
							|  |  |  | 						return resolve(img.metadata) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					fs.readFile(full_path, function(err, file){ | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 						if(err){ | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 							return reject(err) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						// read stat...
 | 
					
						
							|  |  |  | 						if(!that.images[gid].birthtime){ | 
					
						
							|  |  |  | 							var img = that.images[gid] | 
					
						
							|  |  |  | 							var stat = fs.statSync(full_path) | 
					
						
							|  |  |  | 							 | 
					
						
							|  |  |  | 							img.atime = stat.atime | 
					
						
							|  |  |  | 							img.mtime = stat.mtime | 
					
						
							|  |  |  | 							img.ctime = stat.ctime | 
					
						
							|  |  |  | 							img.birthtime = stat.birthtime | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							img.size = stat.size | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						// read image metadata...
 | 
					
						
							|  |  |  | 						exiftool.metadata(file, function(err, data){ | 
					
						
							|  |  |  | 							if(err){ | 
					
						
							|  |  |  | 								reject(err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							} else if(data.error){ | 
					
						
							|  |  |  | 								reject(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								// convert to a real dict...
 | 
					
						
							|  |  |  | 								// NOTE: exiftool appears to return an array 
 | 
					
						
							|  |  |  | 								// 		object rather than an actual dict/object
 | 
					
						
							|  |  |  | 								// 		and that is not JSON compatible....
 | 
					
						
							|  |  |  | 								that.images[gid].metadata = | 
					
						
							|  |  |  | 									Object.assign( | 
					
						
							|  |  |  | 										// XXX do we need to update or overwrite??
 | 
					
						
							|  |  |  | 										that.images[gid].metadata || {}, | 
					
						
							|  |  |  | 										data, | 
					
						
							|  |  |  | 										{ | 
					
						
							|  |  |  | 											ImageGridMetadataReader: 'exiftool/ImageGrid', | 
					
						
							|  |  |  | 											// mark metadata as full read...
 | 
					
						
							|  |  |  | 											ImageGridMetadata: 'full', | 
					
						
							|  |  |  | 										}) | 
					
						
							|  |  |  | 								that.markChanged  | 
					
						
							|  |  |  | 									&& that.markChanged('images', [gid]) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							resolve(data) }) }) }) })], | 
					
						
							| 
									
										
										
										
											2020-12-30 16:54:23 +03:00
										 |  |  | 	// XXX Q: should this be a linked task???
 | 
					
						
							|  |  |  | 	// 		...on one hand yes, on the other, saving after this may 
 | 
					
						
							|  |  |  | 	// 		unintentionally save other state from the main object...
 | 
					
						
							| 
									
										
										
										
											2017-07-23 23:03:19 +03:00
										 |  |  | 	readAllMetadata: ['File/Read all metadata', | 
					
						
							| 
									
										
										
										
											2020-12-02 18:49:45 +03:00
										 |  |  | 		'readMetadata: "all" ...'], | 
					
						
							| 
									
										
										
										
											2016-01-18 05:20:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	// XXX take image Metadata and write it to target...
 | 
					
						
							|  |  |  | 	writeMetadata: ['- Image/Set metadata data', | 
					
						
							| 
									
										
										
										
											2016-01-08 07:45:47 +03:00
										 |  |  | 		function(image, target){ | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | 			// XXX
 | 
					
						
							| 
									
										
										
										
											2020-10-19 04:33:47 +03:00
										 |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX add undo...
 | 
					
						
							|  |  |  | 	ratingToRibbons: ['- Ribbon|Crop/', | 
					
						
							|  |  |  | 		core.doc`Place images to ribbons by rating
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Place images to ribbons by rating in a crop... | 
					
						
							|  |  |  | 			.ratingToRibbons() | 
					
						
							|  |  |  | 			.ratingToRibbons('crop') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Place images to ribbons by rating (in-place)... | 
					
						
							|  |  |  | 			.ratingToRibbons('in-place') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: this will override the current ribbon structure. | 
					
						
							|  |  |  | 		NOTE: this needs .metadata to be loaded. | 
					
						
							|  |  |  | 		NOTE: we do not care about the actual rating values or their  | 
					
						
							|  |  |  | 			number, the number of ribbons corresponds to number of used  | 
					
						
							|  |  |  | 			ratings and thy are sorted by their text value. | 
					
						
							|  |  |  | 		`,
 | 
					
						
							|  |  |  | 		function(mode='crop'){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 			var images = this.images | 
					
						
							|  |  |  | 			var index = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var ribbons = this.data.order | 
					
						
							|  |  |  | 				.reduce(function(ribbons, gid, i){ | 
					
						
							|  |  |  | 					var r = ((images[gid] || {}).metadata || {}).rating || 0 | 
					
						
							|  |  |  | 					// NOTE: we can't just use the rating as ribbon gid 
 | 
					
						
							|  |  |  | 					// 		because currently number-like ribbon gids 
 | 
					
						
							|  |  |  | 					// 		break things -- needs revision...
 | 
					
						
							|  |  |  | 					var g = index[r] = index[r] || that.data.newGID() | 
					
						
							|  |  |  | 					// NOTE: this will create sparse ribbons...
 | 
					
						
							|  |  |  | 					;(ribbons[g] = (ribbons[g] || []))[i] = gid | 
					
						
							|  |  |  | 					return ribbons }, {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// build the new data...
 | 
					
						
							|  |  |  | 			var data = mode == 'in-place' ? | 
					
						
							|  |  |  | 				this.data | 
					
						
							|  |  |  | 				: this.data.clone() | 
					
						
							|  |  |  | 			data.ribbons = ribbons | 
					
						
							|  |  |  | 			// sort by rating then replace with "gid"...
 | 
					
						
							|  |  |  | 			data.ribbon_order = Object.keys(index) | 
					
						
							|  |  |  | 				.sort() | 
					
						
							|  |  |  | 				.reverse() | 
					
						
							|  |  |  | 				.map(function(r){  | 
					
						
							|  |  |  | 					return index[r] }) | 
					
						
							|  |  |  | 			this.setBaseRibbon(data.ribbon_order.last()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			mode == 'in-place' | 
					
						
							|  |  |  | 				&& this.markChanged | 
					
						
							|  |  |  | 				&& this.markChanged('data') | 
					
						
							|  |  |  | 			mode == 'crop' | 
					
						
							|  |  |  | 				&& this.crop(data) }], | 
					
						
							| 
									
										
										
										
											2020-11-05 22:46:27 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// shorthands...
 | 
					
						
							|  |  |  | 	cropRatingsAsRibbons: ['Ribbon|Crop/Crop ratings to ribbons', | 
					
						
							|  |  |  | 		'ratingToRibbons: "crop"'], | 
					
						
							|  |  |  | 	splitRatingsAsRibbons: ['Ribbon/Split ratings to ribbons (in-place)', | 
					
						
							|  |  |  | 		'ratingToRibbons: "in-place"'], | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | var MetadataReader =  | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | module.MetadataReader = core.ImageGridFeatures.Feature({ | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 	title: '', | 
					
						
							|  |  |  | 	doc: '', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | 	tag: 'fs-metadata', | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 	depends: [ | 
					
						
							| 
									
										
										
										
											2017-06-17 06:33:47 +03:00
										 |  |  | 		'fs-info', | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | 		'metadata', | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 08:08:40 +03:00
										 |  |  | 	isApplicable: function(){ return this.runtime.node }, | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	actions: MetadataReaderActions, | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							|  |  |  | 		// XXX STUB: need a better strategy to read metadata...
 | 
					
						
							| 
									
										
										
										
											2016-04-13 01:17:53 +03:00
										 |  |  | 		// 		Approach 1 (target):
 | 
					
						
							|  |  |  | 		// 			read the metadata on demand e.g. on .showMetadata(..)
 | 
					
						
							|  |  |  | 		// 				+ natural approach
 | 
					
						
							|  |  |  | 		// 				- not sync
 | 
					
						
							|  |  |  | 		// 					really complicated to organize...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 		Approach 2:
 | 
					
						
							|  |  |  | 		// 			lazy read -- timeout and overwrite on next image
 | 
					
						
							|  |  |  | 		// 				- hack-ish
 | 
					
						
							|  |  |  | 		// 				+ simple
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 		Approach 3:
 | 
					
						
							|  |  |  | 		// 			index a dir
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 		/* | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 		['focusImage',  | 
					
						
							|  |  |  | 			function(){ | 
					
						
							|  |  |  | 				var gid = this.current | 
					
						
							|  |  |  | 				metadata = this.images && this.images[gid] && this.images[gid].metadata | 
					
						
							|  |  |  | 				metadata = metadata && (Object.keys(metadata).length > 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(!metadata){ | 
					
						
							|  |  |  | 					this.readMetadata(gid) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}] | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 		*/ | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 	], | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 04:29:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:45:47 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | // Metadata editor/viewer...
 | 
					
						
							| 
									
										
										
										
											2016-01-09 01:55:19 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-01-16 08:53:03 +03:00
										 |  |  | // NOTE: this is by-design platform independent...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-01-09 01:55:19 +03:00
										 |  |  | // XXX first instinct is to use browse with editable fields as it will
 | 
					
						
							|  |  |  | // 		give us: 
 | 
					
						
							|  |  |  | // 			- searchability
 | 
					
						
							|  |  |  | // 			- navigation
 | 
					
						
							|  |  |  | // 			- ...
 | 
					
						
							|  |  |  | // 		missing functionality:
 | 
					
						
							|  |  |  | // 			- editor/form on open event
 | 
					
						
							|  |  |  | // 				- inline (preferred)
 | 
					
						
							|  |  |  | // 				- modal-form
 | 
					
						
							|  |  |  | // 			- table-like layout
 | 
					
						
							|  |  |  | // 				- template???
 | 
					
						
							|  |  |  | // 				- script layout tweaking (post-update)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		...need to think about this...
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:45:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-16 08:53:03 +03:00
										 |  |  | // XXX add ability to manually sort fields -- moving a field up/down 
 | 
					
						
							|  |  |  | // 		edits .config...
 | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | // 		...not sure how to go about this yet...
 | 
					
						
							|  |  |  | // XXX add combined fields...
 | 
					
						
							|  |  |  | // 		'Make' + 'Camera Model Name'
 | 
					
						
							|  |  |  | // XXX add identical fields -- show first available and hide the rest...
 | 
					
						
							|  |  |  | // 		'Shutter Speed', 'Exposure Time',
 | 
					
						
							|  |  |  | // 		'Lens ID', 'Lens'
 | 
					
						
							| 
									
										
										
										
											2016-01-16 08:53:03 +03:00
										 |  |  | // XXX add field editing... (open)
 | 
					
						
							| 
									
										
										
										
											2016-01-19 01:23:47 +03:00
										 |  |  | // XXX might be good to split this to sections...
 | 
					
						
							|  |  |  | // 		- base info
 | 
					
						
							|  |  |  | // 		- general metadata
 | 
					
						
							|  |  |  | // 		- full metadata
 | 
					
						
							|  |  |  | // 			- EXIF
 | 
					
						
							|  |  |  | // 			- IPTC
 | 
					
						
							|  |  |  | // 			- ...
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | var MetadataUIActions = actions.Actions({ | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | 	config: { | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 		'metadata-preview-size': 150, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 		'metadata-auto-select-modes': [ | 
					
						
							|  |  |  | 			'none', | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:01 +03:00
										 |  |  | 			'on focus', | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 			'on open', | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:01 +03:00
										 |  |  | 		'metadata-auto-select-mode': 'on focus', | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 		// XXX
 | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 		'metadata-editable-fields': [ | 
					
						
							| 
									
										
										
										
											2016-04-14 16:01:17 +03:00
										 |  |  | 			//'Artist',
 | 
					
						
							|  |  |  | 			//'Copyright',
 | 
					
						
							| 
									
										
										
										
											2017-01-19 03:40:22 +03:00
										 |  |  | 			'Comment', | 
					
						
							|  |  |  | 			'Tags', | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 		], | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | 		'metadata-field-order': [ | 
					
						
							| 
									
										
										
										
											2016-01-24 01:58:48 +03:00
										 |  |  | 			// base
 | 
					
						
							|  |  |  | 			'GID',  | 
					
						
							|  |  |  | 			'File Name', 'Parent Directory', 'Full Path', | 
					
						
							| 
									
										
										
										
											2016-04-21 01:42:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			'Date created', 'ctime', 'mtime', 'atime', | 
					
						
							| 
									
										
										
										
											2016-04-20 03:56:19 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			'Index (ribbon)', 'Index (crop)', 'Index (global)', | 
					
						
							| 
									
										
										
										
											2016-01-24 01:58:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-19 01:23:47 +03:00
										 |  |  | 			// metadata...
 | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 			'Make', 'Camera Model Name', 'Lens ID', 'Lens', 'Lens Profile Name', 'Focal Length', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'Metering Mode', 'Exposure Program', 'Exposure Compensation',  | 
					
						
							| 
									
										
										
										
											2016-04-01 18:42:45 +03:00
										 |  |  | 			'Shutter Speed Value', 'Exposure Time',  | 
					
						
							|  |  |  | 			'Aperture Value', 'F Number',  | 
					
						
							|  |  |  | 			'Iso', | 
					
						
							|  |  |  | 			'Quality', 'Focus Mode',  | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 02:15:13 +03:00
										 |  |  | 			'Rating', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 			'Artist', 'Copyright', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'Date/time Original', 'Create Date', 'Modify Date', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			'Mime Type', | 
					
						
							| 
									
										
										
										
											2016-01-12 04:47:40 +03:00
										 |  |  | 		], | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// XXX EXPERIMENTAL: graph...
 | 
					
						
							| 
									
										
										
										
											2020-05-17 18:59:20 +03:00
										 |  |  | 		'metadata-graph': 'on', | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 		'metadata-graph-config': { | 
					
						
							|  |  |  | 			graph: 'waveform', | 
					
						
							| 
									
										
										
										
											2020-05-15 21:23:13 +03:00
										 |  |  | 			mode: 'luminance', | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2016-01-13 18:04:56 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:01 +03:00
										 |  |  | 	toggleMetadataAutoSelect: ['Interface/Metadata value select', | 
					
						
							| 
									
										
										
										
											2016-03-28 22:07:38 +03:00
										 |  |  | 		core.makeConfigToggler('metadata-auto-select-mode',  | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 			function(){ return this.config['metadata-auto-select-modes'] })], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-17 18:59:20 +03:00
										 |  |  | 	toggleMetadataGraph: ['Interface/Metadata graph display', | 
					
						
							| 
									
										
										
										
											2020-05-20 20:31:19 +03:00
										 |  |  | 		{ mode: function(){ | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:01 +03:00
										 |  |  | 			return (!graph || this.config['browse-advanced-mode'] != 'on') && 'hidden' }}, | 
					
						
							| 
									
										
										
										
											2020-05-17 18:59:20 +03:00
										 |  |  | 		core.makeConfigToggler('metadata-graph', ['on', 'off'])], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 04:51:43 +03:00
										 |  |  | 	// XXX show only graph...
 | 
					
						
							|  |  |  | 	// XXX might be a good idea to show directly in main menu...
 | 
					
						
							|  |  |  | 	// XXX reuse in .showMetadata(..)
 | 
					
						
							|  |  |  | 	// XXX should be floating...
 | 
					
						
							|  |  |  | 	showGraph: ['- Image/', | 
					
						
							|  |  |  | 		function(make){ | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 	// NOTE: this will extend the Browse object with .updateMetadata(..)
 | 
					
						
							|  |  |  | 	// 		method to enable updating of metadata in the list...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2016-01-15 05:37:06 +03:00
										 |  |  | 	// XXX should we replace 'mode' with nested set of metadata???
 | 
					
						
							| 
									
										
										
										
											2016-01-16 08:53:03 +03:00
										 |  |  | 	// XXX make this support multiple images...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 	// XXX make things editable only in when edit is loaded...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 18:04:04 +03:00
										 |  |  | 	// XXX BUG: .dialog.updatePreview() is stealing marks from 
 | 
					
						
							|  |  |  | 	// 		the original image in ribbon...
 | 
					
						
							|  |  |  | 	// 		...see inside...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 	showMetadata: ['Image/Metadata...', | 
					
						
							| 
									
										
										
										
											2016-05-04 21:28:46 +03:00
										 |  |  | 		widgets.makeUIDialog(function(image, mode){ | 
					
						
							| 
									
										
										
										
											2016-01-24 00:05:58 +03:00
										 |  |  | 			var that = this | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | 			image = this.data.getImage(image) | 
					
						
							| 
									
										
										
										
											2016-01-15 05:43:37 +03:00
										 |  |  | 			mode = mode || 'disabled' | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 			data = this.images[image] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 			var preview_size = this.config['metadata-preview-size'] || 150 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var _normalize = typeof(path) != 'undefined' ?  | 
					
						
							|  |  |  | 				path.normalize | 
					
						
							|  |  |  | 				: function(e){ return e.replace(/\/\.\//, '') } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 			return browse.makeLister(null,  | 
					
						
							|  |  |  | 				function(p, make){ | 
					
						
							|  |  |  | 					// helper...
 | 
					
						
							|  |  |  | 					// NOTE: we intentionally rewrite this on each update,
 | 
					
						
							|  |  |  | 					// 		this is done to keep the ref to make(..) up-to-date...
 | 
					
						
							|  |  |  | 					make.dialog.wait = function(){ | 
					
						
							|  |  |  | 						make.Separator() | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 						make.Spinner() } | 
					
						
							|  |  |  | 					// XXX BUG: this when attached is stealing marks from 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 18:01:55 +03:00
										 |  |  | 					// 		the original image in ribbon...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 					make.dialog.updatePreview = function(){ | 
					
						
							| 
									
										
										
										
											2020-05-19 04:51:43 +03:00
										 |  |  | 						var preview = this.preview =  | 
					
						
							|  |  |  | 							this.preview  | 
					
						
							|  |  |  | 								|| that.ribbons.createImage(image) | 
					
						
							| 
									
										
										
										
											2019-10-10 18:01:55 +03:00
										 |  |  | 						return that.ribbons.updateImage(preview, image, preview_size, false, { | 
					
						
							|  |  |  | 								nochrome: true, | 
					
						
							|  |  |  | 								pre_updaters_callback: function([p]){ | 
					
						
							|  |  |  | 									p.classList.add('clone', 'preview') | 
					
						
							|  |  |  | 									p.style.height = preview_size +'px' | 
					
						
							|  |  |  | 									p.style.width = preview_size +'px' | 
					
						
							|  |  |  | 								}, | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 							}) } | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 					// XXX EXPERIMENTAL: graph
 | 
					
						
							|  |  |  | 					// XXX do the calculations in a worker...
 | 
					
						
							|  |  |  | 					make.dialog.updateGraph = function(gid, size){ | 
					
						
							| 
									
										
										
										
											2020-05-17 00:32:59 +03:00
										 |  |  | 						// prevent from updating too often...
 | 
					
						
							|  |  |  | 						if(this.__graph_updating){ | 
					
						
							|  |  |  | 							// request an update...
 | 
					
						
							|  |  |  | 							this.__graph_updating = [gid, size] | 
					
						
							|  |  |  | 							return this.graph } | 
					
						
							|  |  |  | 						this.__graph_updating = true | 
					
						
							|  |  |  | 						setTimeout(function(){ | 
					
						
							|  |  |  | 							// update was requested while we were out -> update now...
 | 
					
						
							|  |  |  | 							this.__graph_updating instanceof Array | 
					
						
							|  |  |  | 								&& this.updateGraph(...this.__graph_updating) | 
					
						
							|  |  |  | 							delete this.__graph_updating }.bind(this), 200) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						// graph disabled...
 | 
					
						
							| 
									
										
										
										
											2020-05-17 18:59:20 +03:00
										 |  |  | 						if(!graph || that.config['metadata-graph'] != 'on'){ | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 							return } | 
					
						
							| 
									
										
										
										
											2020-05-17 00:32:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 						// data...
 | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 						gid = that.data.getImage(gid || 'current') | 
					
						
							|  |  |  | 						var config = that.config['metadata-graph-config'] || {} | 
					
						
							|  |  |  | 						var url = that.images[gid].preview ? | 
					
						
							|  |  |  | 							that.images.getBestPreview(gid, size || 300, null, true).url | 
					
						
							|  |  |  | 							: that.getImagePath(gid) | 
					
						
							| 
									
										
										
										
											2020-05-17 00:32:59 +03:00
										 |  |  | 						var flipped = (that.images[gid] || {}).flipped || [] | 
					
						
							|  |  |  | 						flipped = flipped.length == 1 ?  | 
					
						
							|  |  |  | 								flipped[0] | 
					
						
							|  |  |  | 							: flipped.length == 2 ? | 
					
						
							|  |  |  | 								'both' | 
					
						
							|  |  |  | 							: null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						// build the element...
 | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 						var elem = this.graph =  | 
					
						
							| 
									
										
										
										
											2020-05-15 21:23:13 +03:00
										 |  |  | 							Object.assign( | 
					
						
							|  |  |  | 								this.graph  | 
					
						
							|  |  |  | 									|| document.createElement('ig-image-graph'),  | 
					
						
							| 
									
										
										
										
											2020-05-16 17:55:41 +03:00
										 |  |  | 								config, | 
					
						
							|  |  |  | 								// orientation....
 | 
					
						
							| 
									
										
										
										
											2020-05-17 00:32:59 +03:00
										 |  |  | 								{ | 
					
						
							|  |  |  | 									orientation: (that.images[gid] || {}).orientation || 0, | 
					
						
							|  |  |  | 									flipped: flipped,  | 
					
						
							|  |  |  | 								}) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 						Object.assign(elem.style, { | 
					
						
							|  |  |  | 							width: '500px', | 
					
						
							|  |  |  | 							height: '200px', | 
					
						
							|  |  |  | 						}) | 
					
						
							| 
									
										
										
										
											2020-05-15 21:23:13 +03:00
										 |  |  | 						// delay drawing a bit...
 | 
					
						
							|  |  |  | 						setTimeout(function(){ | 
					
						
							|  |  |  | 							elem.src = url }, 0) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 						return elem } | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 19:43:56 +03:00
										 |  |  | 					// preview...
 | 
					
						
							| 
									
										
										
										
											2019-10-11 01:28:19 +03:00
										 |  |  | 					make(['Preview:', this.updatePreview()],  | 
					
						
							|  |  |  | 						{ cls: 'preview' }) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 					// XXX EXPERIMENTAL: graph
 | 
					
						
							|  |  |  | 					// graph...
 | 
					
						
							|  |  |  | 					graph  | 
					
						
							| 
									
										
										
										
											2020-05-17 18:59:20 +03:00
										 |  |  | 						&& that.config['metadata-graph'] == 'on' | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 						&& make(['Graph:', $(this.updateGraph())],  | 
					
						
							|  |  |  | 							{ cls: 'preview' }) | 
					
						
							| 
									
										
										
										
											2019-10-11 15:19:03 +03:00
										 |  |  | 					// NOTE: these are 1-based and not 0-based...
 | 
					
						
							|  |  |  | 					make(['Position: ',  | 
					
						
							|  |  |  | 						$('<small>') | 
					
						
							|  |  |  | 							.addClass('text') | 
					
						
							|  |  |  | 							.css({ | 
					
						
							|  |  |  | 								whiteSpace: 'pre', | 
					
						
							|  |  |  | 							}) | 
					
						
							|  |  |  | 							.html([ | 
					
						
							|  |  |  | 								// ribbon...
 | 
					
						
							|  |  |  | 								that.data.getImageOrder('ribbon', image) + 1 | 
					
						
							|  |  |  | 									+'/'+  | 
					
						
							|  |  |  | 									that.data.getImages(image).len | 
					
						
							|  |  |  | 									+ '<small>R</small>', | 
					
						
							|  |  |  | 								...((that.crop_stack && that.crop_stack.len > 0) ? | 
					
						
							|  |  |  | 									// crop...
 | 
					
						
							|  |  |  | 									[that.data.getImageOrder('loaded', image) + 1 | 
					
						
							|  |  |  | 										+'/'+  | 
					
						
							|  |  |  | 										that.data.getImages('loaded').len | 
					
						
							|  |  |  | 										+ '<small>C</small>'] | 
					
						
							|  |  |  | 									// global...
 | 
					
						
							|  |  |  | 									: [that.data.getImageOrder(image) + 1 | 
					
						
							|  |  |  | 										+'/'+  | 
					
						
							|  |  |  | 										that.data.getImages('all').len | 
					
						
							|  |  |  | 										+ '<small>G</small>']), | 
					
						
							|  |  |  | 								// ribbon...
 | 
					
						
							|  |  |  | 								'<span>R:</span>'+ | 
					
						
							|  |  |  | 									(that.data.getRibbonOrder(image) + 1) | 
					
						
							|  |  |  | 									+'/'+ | 
					
						
							|  |  |  | 									Object.keys(that.data.ribbons).length, | 
					
						
							|  |  |  | 							].join('   ')) ], | 
					
						
							|  |  |  | 						{ cls: 'index' }) | 
					
						
							|  |  |  | 					make.Separator() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// comment...
 | 
					
						
							|  |  |  | 					make.Editable(['$Comment: ',  | 
					
						
							|  |  |  | 						function(){  | 
					
						
							|  |  |  | 							return data && data.comment || '' }],  | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							start_on: 'open', | 
					
						
							|  |  |  | 							edit_text: 'last', | 
					
						
							|  |  |  | 							multiline: true, | 
					
						
							|  |  |  | 							reset_on_commit: false, | 
					
						
							|  |  |  | 							editdone: function(evt, value){ | 
					
						
							|  |  |  | 								if(value.trim() == ''){ | 
					
						
							|  |  |  | 									return } | 
					
						
							|  |  |  | 								data = that.images[image] = that.images[image] || {} | 
					
						
							|  |  |  | 								data.comment = value | 
					
						
							|  |  |  | 								// mark image as changed...
 | 
					
						
							|  |  |  | 								that.markChanged  | 
					
						
							|  |  |  | 									&& that.markChanged('images', [image]) | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 						})  | 
					
						
							| 
									
										
										
										
											2019-10-10 19:43:56 +03:00
										 |  |  | 					make.Separator() | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 15:19:03 +03:00
										 |  |  | 					// gid...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					make(['$GID: ', image]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 19:40:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					if(data){ | 
					
						
							|  |  |  | 						// some abstractions...
 | 
					
						
							|  |  |  | 						var _basename = typeof(path) != 'undefined' ? | 
					
						
							|  |  |  | 							path.basename | 
					
						
							|  |  |  | 							: function(e){ return e.split(/[\\\/]/g).pop() } | 
					
						
							|  |  |  | 						var _dirname = typeof(path) != 'undefined' ? | 
					
						
							|  |  |  | 							function(e){ return path.normalize(path.dirname(e)) } | 
					
						
							|  |  |  | 							: function(e){  | 
					
						
							|  |  |  | 								return _normalize(e.split(/[\\\/]/g).slice(0, -1).join('/')) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						// paths...
 | 
					
						
							|  |  |  | 						data.path  | 
					
						
							|  |  |  | 							&& make(['File $Name: ',  | 
					
						
							|  |  |  | 								_basename(data.path)]) | 
					
						
							|  |  |  | 							&& make(['Parent $Directory: ',  | 
					
						
							|  |  |  | 								_dirname((data.base_path || '.') +'/'+ data.path)]) | 
					
						
							|  |  |  | 							&& make(['Full $Path: ',  | 
					
						
							|  |  |  | 								_normalize((data.base_path || '.') +'/'+ data.path)]) | 
					
						
							|  |  |  | 						// times...
 | 
					
						
							|  |  |  | 						data.birthtime  | 
					
						
							|  |  |  | 							&& make(['Date created: ',  | 
					
						
							|  |  |  | 								data.birthtime && new Date(data.birthtime).toShortDate()]) | 
					
						
							|  |  |  | 						data.ctime | 
					
						
							|  |  |  | 							&& make(['- ctime: ',  | 
					
						
							|  |  |  | 								data.ctime && new Date(data.ctime).toShortDate()], | 
					
						
							|  |  |  | 								{disabled: true}) | 
					
						
							|  |  |  | 						data.mtime | 
					
						
							|  |  |  | 							&& make(['- mtime: ', | 
					
						
							|  |  |  | 								data.mtime && new Date(data.mtime).toShortDate()], | 
					
						
							|  |  |  | 								{disabled: true}) | 
					
						
							|  |  |  | 						data.atime | 
					
						
							|  |  |  | 							&& make(['- atime: ',  | 
					
						
							|  |  |  | 								data.atime && new Date(data.atime).toShortDate()], | 
					
						
							|  |  |  | 								{disabled: true}) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					// get other sections...
 | 
					
						
							|  |  |  | 					that.callSortedAction('metadataSection', make, image, data, mode) | 
					
						
							|  |  |  | 				}, { | 
					
						
							| 
									
										
										
										
											2019-10-10 19:40:55 +03:00
										 |  |  | 					cls: 'table-view metadata-view', | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					showDisabled: false, | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 				.on('attached', function(){  | 
					
						
							|  |  |  | 					this.updatePreview()  | 
					
						
							|  |  |  | 					graph | 
					
						
							|  |  |  | 						&& this.updateGraph() | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				.on('update', function(){  | 
					
						
							|  |  |  | 					this.updatePreview()  | 
					
						
							|  |  |  | 					graph | 
					
						
							|  |  |  | 						&& this.updateGraph() | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 				// select value of current item...
 | 
					
						
							|  |  |  | 				.on('select', function(evt, elem){ | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:01 +03:00
										 |  |  | 					that.config['metadata-auto-select-mode'] == 'on focus' | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 						&& $(elem).find('.text').last().selectText() }) | 
					
						
							|  |  |  | 				.close(function(){ | 
					
						
							|  |  |  | 					// XXX handle comment and tag changes...
 | 
					
						
							|  |  |  | 					// XXX
 | 
					
						
							| 
									
										
										
										
											2019-10-10 16:54:50 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					that.refresh(image) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:46:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// XXX EXPERIMENTAL: graph...
 | 
					
						
							|  |  |  | 					// save graph settings...
 | 
					
						
							|  |  |  | 					this.graph | 
					
						
							|  |  |  | 						&& (that.config['metadata-graph-config'] = { | 
					
						
							|  |  |  | 							graph: this.graph.graph, | 
					
						
							|  |  |  | 							mode: this.graph.mode, | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 						}) }) })], | 
					
						
							| 
									
										
										
										
											2016-06-29 20:28:38 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 	metadataSection: ['- Image/', | 
					
						
							|  |  |  | 		{ sortedActionPriority: 'normal' }, | 
					
						
							|  |  |  | 		core.notUserCallable(function(make, gid, image, mode){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 			var metadata = this.getMetadata(gid) || {}  | 
					
						
							|  |  |  | 			var field_order = this.config['metadata-field-order'] || [] | 
					
						
							|  |  |  | 			var x = field_order.length + 1 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 			// NOTE: this is called on showMetadata.pre in the .handlers 
 | 
					
						
							|  |  |  | 			// 		feature section...
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 			make.dialog.updateMetadata =  | 
					
						
							|  |  |  | 				function(metadata){ | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 					metadata = metadata  | 
					
						
							|  |  |  | 						|| that.getMetadata() | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// build new data set and update view...
 | 
					
						
							|  |  |  | 					//this.options.data = _buildInfoList(image, metadata)
 | 
					
						
							|  |  |  | 					this.update() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 					return this } | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// build fields...
 | 
					
						
							|  |  |  | 			var fields = [] | 
					
						
							|  |  |  | 			Object.keys(metadata) | 
					
						
							|  |  |  | 				.forEach(function(k){ | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 					var n =  k | 
					
						
							|  |  |  | 						// convert camel-case to human-case ;)
 | 
					
						
							|  |  |  | 						.replace(/([A-Z]+)/g, ' $1') | 
					
						
							|  |  |  | 						.capitalize() | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					var opts = {} | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// skip metadata stuff in short mode...
 | 
					
						
							|  |  |  | 					if(mode != 'full'  | 
					
						
							|  |  |  | 							&& field_order.indexOf(n) == -1){ | 
					
						
							|  |  |  | 						if(mode == 'short'){ | 
					
						
							|  |  |  | 							return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						} else if(mode == 'disabled') { | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 							opts.disabled = true } } | 
					
						
							| 
									
										
										
										
											2016-01-15 05:19:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 					fields.push([ | 
					
						
							|  |  |  | 						[ n + ': ', metadata[k] ],  | 
					
						
							|  |  |  | 						opts, | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 					]) }) | 
					
						
							| 
									
										
										
										
											2016-05-04 21:28:46 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 			// make fields...
 | 
					
						
							|  |  |  | 			fields | 
					
						
							|  |  |  | 				.sort(function(a, b){ | 
					
						
							|  |  |  | 					a = field_order.indexOf(a[0][0] | 
					
						
							|  |  |  | 						.replace(/\$(\w)/g, '$1') | 
					
						
							|  |  |  | 						.replace(/^- |: $/g, '')) | 
					
						
							|  |  |  | 					a = a == -1 ? x : a | 
					
						
							|  |  |  | 					b = field_order.indexOf(b[0][0] | 
					
						
							|  |  |  | 						.replace(/\$(\w)/g, '$1') | 
					
						
							|  |  |  | 						.replace(/^- |: $/g, '')) | 
					
						
							|  |  |  | 					b = b == -1 ? x : b | 
					
						
							|  |  |  | 					return a - b }) | 
					
						
							|  |  |  | 				.run(function(){ | 
					
						
							|  |  |  | 					this.length > 0 | 
					
						
							|  |  |  | 						&& make.Separator() }) | 
					
						
							|  |  |  | 				.forEach(function(e){ | 
					
						
							| 
									
										
										
										
											2020-11-03 04:49:56 +03:00
										 |  |  | 					make(...e) }) })], | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | var MetadataUI =  | 
					
						
							|  |  |  | module.MetadataUI = core.ImageGridFeatures.Feature({ | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | 	title: '', | 
					
						
							|  |  |  | 	doc: '', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	tag: 'ui-metadata', | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'ui', | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 		'metadata', | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-12 04:33:10 +03:00
										 |  |  | 	actions: MetadataUIActions, | 
					
						
							| 
									
										
										
										
											2016-01-09 04:59:57 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-17 05:02:37 +03:00
										 |  |  | // Load etdata on demand...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | var MetadataFSUI =  | 
					
						
							|  |  |  | module.MetadataFSUI = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: '', | 
					
						
							|  |  |  | 	doc: '', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tag: 'ui-fs-metadata', | 
					
						
							|  |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'ui', | 
					
						
							|  |  |  | 		'metadata', | 
					
						
							|  |  |  | 		'fs-metadata', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							| 
									
										
										
										
											2016-04-13 05:45:10 +03:00
										 |  |  | 		// Read metadata and when done update the list...
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 		['showMetadata.pre', | 
					
						
							|  |  |  | 			function(image){ | 
					
						
							|  |  |  | 				var that = this | 
					
						
							|  |  |  | 				var reader = this.readMetadata(image) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 06:07:17 +03:00
										 |  |  | 				return reader  | 
					
						
							|  |  |  | 					&& function(client){ | 
					
						
							|  |  |  | 						// add a loading indicator...
 | 
					
						
							|  |  |  | 						// NOTE: this will get overwritten when calling .updateMetadata()
 | 
					
						
							|  |  |  | 						client.wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						reader | 
					
						
							|  |  |  | 							.then(function(data){ | 
					
						
							|  |  |  | 								client.updateMetadata() }) | 
					
						
							|  |  |  | 							.catch(function(){ | 
					
						
							| 
									
										
										
										
											2020-10-19 04:33:47 +03:00
										 |  |  | 								client.update() }) } }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// reload view when .ratingToRibbons('in-place') is called...
 | 
					
						
							|  |  |  | 		['ratingToRibbons', | 
					
						
							|  |  |  | 			function(res, mode){ | 
					
						
							|  |  |  | 				mode == 'in-place' | 
					
						
							| 
									
										
										
										
											2020-10-19 04:59:34 +03:00
										 |  |  | 					&& this.reload(true) }], | 
					
						
							| 
									
										
										
										
											2016-04-13 03:05:25 +03:00
										 |  |  | 	], | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 07:30:51 +03:00
										 |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2016-08-20 22:49:36 +03:00
										 |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |