mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-29 18:30:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			825 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			825 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| /**********************************************************************
 | |
| * 
 | |
| *
 | |
| *
 | |
| **********************************************************************/
 | |
| 
 | |
| //var DEBUG = DEBUG != null ? DEBUG : true
 | |
| 
 | |
| // NOTE: this is a sparse list, i.e. all elements are in the same 
 | |
| // 		position as they are in DATA.order, and the unmarked elements
 | |
| // 		are undefined.
 | |
| // 		This is done because:
 | |
| // 			- it drasticly simplifies adding, removing and access as 
 | |
| // 				there is no searching and checking involved, just insert
 | |
| // 				to the same spot as in order and you are safe.
 | |
| // 			- trivial sorting
 | |
| // 			- less maintenance and sync
 | |
| // 		The tradeoff being:
 | |
| // 			- load/save conversion to keep the json	data "packed".
 | |
| // NOTE: it would appear that JS is designed with sparse lists in mind:
 | |
| // 		- all iterators (map, filter, forEach, ..) skip undefined values
 | |
| // 		- really fast
 | |
| var MARKED = []
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * helpers...
 | |
| */
 | |
| 
 | |
| function _addMark(cls, gid, image){
 | |
| 	gid = gid == null ? getImageGID() : gid
 | |
| 	image = image == null ? getImage() : $(image)
 | |
| 
 | |
| 	// no image is loaded...
 | |
| 	if(image.length == 0){
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	var mark = $('.mark.'+cls+'.'+gid)
 | |
| 
 | |
| 	if(mark.length == 0){
 | |
| 		mark = $('<div class="mark"/>')
 | |
| 			.addClass(gid)
 | |
| 			.addClass(cls)
 | |
| 			//.insertAfter(image)
 | |
| 	} 
 | |
| 
 | |
| 	// make sure the mark is explicitly after the image...
 | |
| 	// XXX think of an eficient way to test if we need to re-insert...
 | |
| 	mark.insertAfter(image)
 | |
| 
 | |
| 	return mark
 | |
| }
 | |
| function _removeMark(cls, gid, image){
 | |
| 	gid = gid == null ? getImageGID() : gid
 | |
| 	image = image == null ? getImage() : $(image)
 | |
| 
 | |
| 	// no image is loaded...
 | |
| 	if(image.length == 0){
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	var mark = $('.mark.'+cls+'.'+gid)
 | |
| 
 | |
| 	if(mark.length != 0){
 | |
| 		mark.detach()
 | |
| 	}
 | |
| 	return mark
 | |
| }
 | |
| 
 | |
| 
 | |
| function makeMarkedLister(get_marked){
 | |
| 	return function(mode){
 | |
| 		var marked = get_marked()
 | |
| 		mode = mode == null ? 'all' : mode
 | |
| 		return mode == 'all' ? getLoadedGIDs(marked) 
 | |
| 			: mode.constructor.name == 'Array' ? getLoadedGIDs(mode)
 | |
| 			: typeof(mode) == typeof(123) ? getRibbonGIDs(marked, mode)
 | |
| 			: getRibbonGIDs(marked)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| // Make lister of unmarked images...
 | |
| //
 | |
| // The resulting function can take one argument (mode) which can be:
 | |
| // 	- null			- default, same as 'all'
 | |
| // 	- 'all'			- process all loaded gids
 | |
| // 	- 'ribbon'		- process curent ribbon
 | |
| // 	- number		- ribbon index to process
 | |
| // 	- Array			- list of gids to filter
 | |
| function makeUnmarkedLister(get_marked){
 | |
| 	return function(mode){
 | |
| 		mode = mode == null ? 'all' : mode
 | |
| 
 | |
| 		var marked = get_marked()
 | |
| 
 | |
| 		var gids = mode == 'all' ? getLoadedGIDs() 
 | |
| 			: mode.constructor.name == 'Array' ? getLoadedGIDs(mode)
 | |
| 			: typeof(mode) == typeof(123) ? getRibbonGIDs(marked, mode)
 | |
| 			: getRibbonGIDs(marked)
 | |
| 
 | |
| 		// calculate the set...
 | |
| 		var res = gids.filter(function(e){
 | |
| 			// keep only unmarked...
 | |
| 			return marked.indexOf(e) < 0
 | |
| 		})
 | |
| 
 | |
| 		return res
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| // The same as makeUnmarkedLister(..) but designed for sparse lists...
 | |
| //
 | |
| // NOTE: this is about an order of magnitude faster than the non-sparse
 | |
| // 		version...
 | |
| //
 | |
| // XXX if this gets used often, add caching -- this may get quite slow
 | |
| // 		for very large image sets...
 | |
| function makeUnmarkedSparseLister(get_marked){
 | |
| 	return function(mode){
 | |
| 		mode = mode == null ? 'all' : mode
 | |
| 
 | |
| 		var marked = get_marked()
 | |
| 
 | |
| 		var res = mode == 'all' ? 
 | |
| 				DATA.order.slice()
 | |
| 			: mode == 'ribbon' ? 
 | |
| 				populateSparseGIDList(getRibbonGIDs())
 | |
| 			: typeof(mode) == typeof(123) ? 
 | |
| 				populateSparseGIDList(getRibbonGIDs(mode))
 | |
| 			: mode
 | |
| 
 | |
| 		// for ribbon modes, remove non-ribbon marks...
 | |
| 		if(mode == 'ribbon'){
 | |
| 			marked = getRibbonGIDs(marked)
 | |
| 		} else if(typeof(mode) == typeof(123)){
 | |
| 			marked = getRibbonGIDs(marked, mode)
 | |
| 			mode = 'ribbon'
 | |
| 		}
 | |
| 
 | |
| 		// negate the list...
 | |
| 		marked.forEach(function(e, i){
 | |
| 			delete res[i]
 | |
| 		})
 | |
| 
 | |
| 		return getLoadedGIDs(compactSparceList(res))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| var getMarked = makeMarkedLister(function(){ return MARKED })
 | |
| var getUnmarked = makeUnmarkedSparseLister(function(){ return MARKED })
 | |
| 
 | |
| 
 | |
| var getMarkedGIDBefore = makeGIDBeforeGetterFromList(
 | |
| 		function(){ 
 | |
| 			return compactSparceList(MARKED)
 | |
| 		})
 | |
| 
 | |
| 
 | |
| // NOTE: this is not too fast as it will filter the marked images...
 | |
| // NOTE: this is restricted to current ribbon...
 | |
| var getUnmarkedGIDBefore = makeGIDBeforeGetterFromList(
 | |
| 		function(ribbon){ 
 | |
| 			return getUnmarked(ribbon)
 | |
| 		}, true)
 | |
| 
 | |
| 
 | |
| // Make a mark toggler
 | |
| //
 | |
| // The toggler will:
 | |
| // 	- toggle img_class on the target image
 | |
| // 	- add/remove a mark element after the image
 | |
| // 	- toggle mark_class on the mark element
 | |
| // 	- call the callback, if defined, passing it:
 | |
| // 		- gid
 | |
| // 		- action ('on' or 'off')
 | |
| // 	- trigger the evt_name on the viewer passing it:
 | |
| // 		- gid
 | |
| // 		- action ('on' or 'off')
 | |
| //
 | |
| // The actual toggler is built with createCSSClassToggler(..), see its
 | |
| // docs for protocol descrittion.
 | |
| //
 | |
| // The resulting toggler, by default, marks the current image 
 | |
| // (.current.image), but can be passed a different image as first 
 | |
| // argument.
 | |
| //
 | |
| // NOTE: when passing an alternative image as an argument, the second 
 | |
| // 		argument MUST also be passed. it can be one of:
 | |
| // 			- 'on'		: force create mark
 | |
| // 			- 'off'		: force remove mark
 | |
| // 			- 'next'	: toggle next state (default)
 | |
| // NOTE: when passing this a gid, the 'next' action is not supported
 | |
| function makeMarkToggler(img_class, mark_class, evt_name, callback){
 | |
| 	return createCSSClassToggler(
 | |
| 		'.current.image', 
 | |
| 		img_class,
 | |
| 		function(action, elem){
 | |
| 			toggleMarksView('on')
 | |
| 
 | |
| 			// we got a gid...
 | |
| 			if(elem.length == 0 && elem.selector in IMAGES){
 | |
| 				var gid = elem.selector
 | |
| 				elem = getImage(gid)
 | |
| 				elem = elem.length == 0 ? null : elem
 | |
| 
 | |
| 			// we are given an image...
 | |
| 			} else {
 | |
| 				var gid = getImageGID(elem)
 | |
| 			}
 | |
| 
 | |
| 			// do this only of the image is loaded...
 | |
| 			if(elem != null){
 | |
| 				if(action == 'on'){
 | |
| 					_addMark(mark_class, gid, elem)
 | |
| 				} else {
 | |
| 					_removeMark(mark_class, gid, elem)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if(callback != null){
 | |
| 				callback(gid, action)
 | |
| 			}
 | |
| 
 | |
| 			$('.viewer').trigger(evt_name, [gid, action])
 | |
| 		})
 | |
| }
 | |
| 
 | |
| 
 | |
| // Generate an image updater function...
 | |
| //
 | |
| // the resulting function will update image mark state by adding or 
 | |
| // removing the mark the specific mark object.
 | |
| function makeMarkUpdater(img_class, mark_class, test){
 | |
| 	var _updater = function(gid, image){
 | |
| 		// marks...
 | |
| 		if(test(gid)){
 | |
| 			image.addClass(img_class)
 | |
| 			_addMark(mark_class, gid, image)
 | |
| 		} else {
 | |
| 			image.removeClass(img_class)
 | |
| 			_removeMark(mark_class, gid, image)
 | |
| 		}
 | |
| 		return image
 | |
| 	}
 | |
| 	IMAGE_UPDATERS.push(_updater)
 | |
| 	return _updater
 | |
| }
 | |
| 
 | |
| 
 | |
| // NOTE: this supports only shifts by one position...
 | |
| // XXX this is similar to insertGIDToPosition(..) do we need both?
 | |
| // 		...this one is a special case and insertGIDToPosition(..) is 
 | |
| // 		general, the later uses search to find the position, here we 
 | |
| // 		know the aproximate location, the question is if this speedup
 | |
| // 		is worth the effort of maintaining a special case function...
 | |
| function shiftGIDToOrderInList(gid, direction, list){
 | |
| 	var gid_o = DATA.order.indexOf(gid)
 | |
| 	var gid_m = list.indexOf(gid)
 | |
| 
 | |
| 	var a_m = gid_m + (direction == 'next' ? 1 : -1)
 | |
| 	if(a_m < 0 || a_m >= list.length){
 | |
| 		return false
 | |
| 	}
 | |
| 	var a_gid = list[a_m]
 | |
| 	var a_o = DATA.order.indexOf(a_gid)
 | |
| 
 | |
| 	// if relative positions of cur and adjacent gids in list 
 | |
| 	// are different to that in DATA.order, then replace the gids
 | |
| 	// in list...
 | |
| 	if(sign(a_m - gid_m) != sign(a_o - gid_o)){
 | |
| 		list[a_m] = gid
 | |
| 		list[gid_m] = a_gid
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| 
 | |
| // a sparse version of shiftGIDToOrderInList(..)...
 | |
| //
 | |
| // returns true if list is updated....
 | |
| function shiftGIDInSparseList(gid, from, to, list){
 | |
| 	// XXX do we need this???
 | |
| 	if(list[from] == null 
 | |
| 			&& list[to] == null 
 | |
| 			// NOTE: if there is something between 'from' and 'to' it must 
 | |
| 			// 		be shifted...
 | |
| 			&& compactSparceList(list.slice(from, to)).length == 0){
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// if gid was never in list, we must it and remove leave things as 
 | |
| 	// we got them, and remove it again...
 | |
| 	var cleanup = list.indexOf(gid) < 0
 | |
| 
 | |
| 	// move the marked gid...
 | |
| 	list.splice(from, 1)
 | |
| 	list.splice(to, 0, gid)
 | |
| 
 | |
| 	// NOTE: essentially, we are using gid as a marker, as we can't 
 | |
| 	// 		.splice(..) an undefined into a list...
 | |
| 	if(cleanup){
 | |
| 		delete list[to]
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| 
 | |
| // NOTE: this is sparse-only...
 | |
| function setAllMarks(action, mode, list, toggler){
 | |
| 	action = action == null ? toggler('?') : action
 | |
| 	mode = mode == null ? 'ribbon' : mode
 | |
| 
 | |
| 	var updated = []
 | |
| 
 | |
| 	if(action == 'on'){
 | |
| 		var _update = function(e){
 | |
| 			if(list.indexOf(e) < 0){
 | |
| 				list[DATA.order.indexOf(e)] = e
 | |
| 				updated.push(e)
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		var _update = function(e){
 | |
| 			var i = list.indexOf(e)
 | |
| 			if(i >= 0){
 | |
| 				delete list[i]
 | |
| 				updated.push(e)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// marks from current ribbon (default)...
 | |
| 	if(mode == 'ribbon'){
 | |
| 		var res = getRibbonGIDs()
 | |
| 
 | |
| 	// all marks...
 | |
| 	} else if(mode == 'all'){
 | |
| 		var res = getLoadedGIDs()
 | |
| 	} 
 | |
| 
 | |
| 	res.forEach(_update)
 | |
| 
 | |
| 	updateImages(updated)
 | |
| 
 | |
| 	//return res
 | |
| 	return updated
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * 
 | |
| */
 | |
| 
 | |
| var updateSelectedImageMark = makeMarkUpdater(
 | |
| 		'marked',
 | |
| 		'selected', 
 | |
| 		function(gid){ 
 | |
| 			return MARKED.indexOf(gid) > -1 
 | |
| 		})
 | |
| 
 | |
| 
 | |
| // NOTE: to disable MARKED cleanout set no_cleanout_marks to true.
 | |
| // NOTE: MARKED may contain both gids that are not loaded and that do 
 | |
| // 		not exist, as there is no way to distinguish between the two 
 | |
| // 		situations the cleanup is optional...
 | |
| function cropMarkedImages(keep_ribbons, keep_unloaded_gids){
 | |
| 	cropDataTo(MARKED, keep_ribbons, keep_unloaded_gids)
 | |
| 	return DATA
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * Modes
 | |
| */
 | |
| 
 | |
| var toggleMarkedOnlyView = makeCropModeToggler(cropMarkedImages)
 | |
| 
 | |
| 
 | |
| var toggleMarkedOnlyWithRibbonsView = makeCropModeToggler(
 | |
| 		function(){
 | |
| 			cropMarkedImages(true)
 | |
| 		})
 | |
| 
 | |
| 
 | |
| var toggleMarksView = createCSSClassToggler(
 | |
| 	'.viewer', 
 | |
| 	'marks-visible',
 | |
| 	function(){
 | |
| 		var cur = getImage()
 | |
| 		// current is marked...
 | |
| 		if(cur.hasClass('marked')){
 | |
| 			centerView(null, 'css')
 | |
| 			return
 | |
| 		} 
 | |
| 		// there is a marked image in this ribbon...
 | |
| 		var target = getImageBefore(cur, null)
 | |
| 		if(target.length > 0){
 | |
| 			centerView(focusImage(target), 'css')
 | |
| 			return
 | |
| 		}
 | |
| 		// get marked image from other ribbons...
 | |
| 		prevRibbon()
 | |
| 		if(getImage().hasClass('marked')){
 | |
| 			return
 | |
| 		}
 | |
| 		nextRibbon()
 | |
| 	})
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * Actions
 | |
| */
 | |
| 
 | |
| var toggleMark = makeMarkToggler(
 | |
| 		'marked', 
 | |
| 		'selected', 
 | |
| 		'togglingMarks',
 | |
| 		function(gid, action){
 | |
| 			// add marked image to list...
 | |
| 			if(action == 'on'){
 | |
| 				if(MARKED.indexOf(gid) == -1){
 | |
| 					MARKED[DATA.order.indexOf(gid)] = gid
 | |
| 				} 
 | |
| 
 | |
| 			// remove marked image from list...
 | |
| 			} else {
 | |
| 				delete MARKED[MARKED.indexOf(gid)]
 | |
| 			}
 | |
| 
 | |
| 			marksUpdated()
 | |
| 		})
 | |
| 
 | |
| 
 | |
| function markAllImagesTo(action, mode){
 | |
| 	mode = mode == null ? 'ribbon' : mode
 | |
| 	var res = setAllMarks(action, mode, MARKED, toggleMark)
 | |
| 	$('.viewer')
 | |
| 		.trigger('togglingMarks', [res, action])
 | |
| 		.trigger('removingMarks', [res, mode])
 | |
| 	marksUpdated()
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| 
 | |
| // Mark/Unmark images...
 | |
| //
 | |
| // mode can be:
 | |
| //	- 'ribbon' (default)
 | |
| //	- 'all'
 | |
| //
 | |
| function markAll(mode){ markAllImagesTo('on', mode) }
 | |
| function unmarkAll(mode){ markAllImagesTo('off', mode) }
 | |
| 
 | |
| 
 | |
| // Invert marks on images...
 | |
| //
 | |
| // mode can be:
 | |
| //	- 'ribbon' (default)
 | |
| //	- 'all'
 | |
| //
 | |
| function invertImageMarks(mode, gids){
 | |
| 	mode = mode == null ? 'ribbon' : mode
 | |
| 	gids = gids != null ? gids 
 | |
| 			: mode == 'ribbon' ? getRibbonGIDs()
 | |
| 			: getLoadedGIDs()
 | |
| 
 | |
| 	var on = []
 | |
| 	var off = []
 | |
| 	var order = DATA.order
 | |
| 
 | |
| 	$.each(gids, function(_, e){
 | |
| 		var i = order.indexOf(e)
 | |
| 		if(MARKED[i] === undefined){
 | |
| 			on.push(e)
 | |
| 			MARKED[i] = e
 | |
| 		} else {
 | |
| 			off.push(e)
 | |
| 			delete MARKED[i]
 | |
| 		}
 | |
| 	})
 | |
| 	updateImages(gids)
 | |
| 
 | |
| 	$('.viewer')
 | |
| 		.trigger('invertingMarks', [gids])
 | |
| 		.trigger('togglingMarks', [on, 'on'])
 | |
| 		.trigger('togglingMarks', [off, 'off'])
 | |
| 
 | |
| 	marksUpdated()
 | |
| 
 | |
| 	return gids
 | |
| }
 | |
| 
 | |
| 
 | |
| // Toggle marks in the current continuous section of marked or unmarked
 | |
| // images...
 | |
| function toggleMarkBlock(image){
 | |
| 	image = image == null ? getImage() : image
 | |
| 	var gid = typeof(image) == typeof('str') ? image : getImageGID(image)
 | |
| 	image = typeof(image) == typeof('str') ? getImage(gid) : image
 | |
| 
 | |
| 	var state = toggleMark(image, 'next') == 'off' ? false : true
 | |
| 
 | |
| 	var ribbon = DATA.ribbons[getRibbonIndex(image)]
 | |
| 	var i = ribbon.indexOf(gid)
 | |
| 
 | |
| 	var updated = [gid]
 | |
| 	var order = DATA.order
 | |
| 
 | |
| 	var _convert = function(_, e){
 | |
| 		// break if state differs from current...
 | |
| 		if((MARKED.indexOf(e) >= 0) == state){
 | |
| 			return false
 | |
| 		}
 | |
| 		// do the toggle...
 | |
| 		if(state){
 | |
| 			MARKED[order.indexOf(e)] = e
 | |
| 		} else {
 | |
| 			delete MARKED[MARKED.indexOf(e)]
 | |
| 		}
 | |
| 		updated.push(e)
 | |
| 	}
 | |
| 
 | |
| 	// go left...
 | |
| 	var left = ribbon.slice(0, i)
 | |
| 	left.reverse()
 | |
| 	$.each(left, _convert)
 | |
| 
 | |
| 	// go right...
 | |
| 	var right = ribbon.slice(i+1)
 | |
| 	$.each(right, _convert)
 | |
| 
 | |
| 	updateImages(updated)
 | |
| 
 | |
| 	$('.viewer')
 | |
| 		.trigger('togglingImageBlockMarks', [image, updated, state])
 | |
| 		.trigger('togglingMarks', [updated, state ? 'on' : 'off'])
 | |
| 
 | |
| 	marksUpdated()
 | |
| 
 | |
| 	return state
 | |
| }
 | |
| 
 | |
| 
 | |
| // XXX need to account for empty ribbons...
 | |
| function shiftMarkedImages(direction, mode, new_ribbon){
 | |
| 	mode = mode == null ? 'ribbon' : mode
 | |
| 	var cur = getRibbonIndex()
 | |
| 	var orig_ribbon = cur
 | |
| 
 | |
| 	// ribbon only...
 | |
| 	if(mode == 'ribbon'){
 | |
| 		var ribbon = DATA.ribbons[cur]
 | |
| 		// remove all the marked images form the current ribbon...
 | |
| 		// NOTE: this builds a list of marked images ONLY in current 
 | |
| 		// 		ribbon...
 | |
| 		var marked = $.map(MARKED, function(e){
 | |
| 			var i = ribbon.indexOf(e)
 | |
| 			if(i >= 0){
 | |
| 				ribbon.splice(i, 1)
 | |
| 				return e
 | |
| 			}
 | |
| 			return null
 | |
| 		})
 | |
| 
 | |
| 	// shift all marked images...
 | |
| 	} else {
 | |
| 		var marked = MARKED.slice()
 | |
| 		// remove all the marked images form all other ribbons...
 | |
| 		$.each(DATA.ribbons, function(ribbon){
 | |
| 			$.each(marked, function(e){
 | |
| 				var i = ribbon.indexOf(e)
 | |
| 				i >= 0 ? ribbon.splice(i, 1) : null
 | |
| 			})
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	// if we are at the top or bottom ribbons we need to create a new 
 | |
| 	// ribbon regardless...
 | |
| 	if((cur == 0 && direction == 'prev') 
 | |
| 			|| (cur == DATA.ribbons.length-1 && direction == 'next')){
 | |
| 		new_ribbon = true
 | |
| 	}
 | |
| 
 | |
| 	// add marked to new ribbon...
 | |
| 	if(new_ribbon){
 | |
| 		cur += direction == 'next' ? 1 : 0
 | |
| 		DATA.ribbons.splice(cur, 0, marked)
 | |
| 	
 | |
| 	// add marked to existing ribbon...
 | |
| 	} else {
 | |
| 		cur += direction == 'next' ? 1 : -1
 | |
| 		DATA.ribbons[cur] = fastSortGIDsByOrder(DATA.ribbons[cur].concat(marked))
 | |
| 	}
 | |
| 	
 | |
| 	dataUpdated()
 | |
| 
 | |
| 	// remove empty ribbons and reload...
 | |
| 	dropEmptyRibbons()
 | |
| 	reloadViewer()
 | |
| 
 | |
| 	$('.viewer').trigger('shiftedImages', [marked, orig_ribbon, cur])
 | |
| }
 | |
| function shiftMarkedImagesUp(mode, new_ribbon){
 | |
| 	return shiftMarkedImages('prev', mode, new_ribbon)
 | |
| }
 | |
| function shiftMarkedImagesDown(mode, new_ribbon){
 | |
| 	return shiftMarkedImages('next', mode, new_ribbon)
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| // XXX these are ribbon wise only (???)
 | |
| // XXX this on first step this must pack all marked images
 | |
| function horizontalShiftMarkedImages(direction){
 | |
| 	// XXX
 | |
| }
 | |
| function shiftMarkedImagesLeft(){
 | |
| 	return horizontalShiftMarkedImages('prev')
 | |
| }
 | |
| function shiftMarkedImagesRight(){
 | |
| 	return horizontalShiftMarkedImages('next')
 | |
| }
 | |
| */
 | |
| 
 | |
| 
 | |
| // Focus next/prev marked image...
 | |
| //
 | |
| // NOTE: these will not jump to marks on other ribbons... to prevent this
 | |
| // 		add true as the final argument (see restrict_to_ribbon argument 
 | |
| // 		of makeNextFromListAction(..) for more info)
 | |
| var nextMark = makeNextFromListAction(
 | |
| 		getMarkedGIDBefore, 
 | |
| 		function(){ return compactSparceList(MARKED) })
 | |
| var prevMark = makePrevFromListAction(
 | |
| 		getMarkedGIDBefore, 
 | |
| 		function(){ return compactSparceList(MARKED) })
 | |
| 
 | |
| 
 | |
| // Focus next/prev unmarked image..
 | |
| //
 | |
| var nextUnmarked = makeNextFromListAction(
 | |
| 		getUnmarkedGIDBefore, 
 | |
| 		function(ribbon){ 
 | |
| 			return getUnmarked(ribbon == null ? 'ribbon' : ribbon) 
 | |
| 		})
 | |
| var prevUnmarked = makePrevFromListAction(
 | |
| 		getUnmarkedGIDBefore, 
 | |
| 		function(ribbon){ 
 | |
| 			return getUnmarked(ribbon == null ? 'ribbon' : ribbon) 
 | |
| 		})
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * Dialogs... 
 | |
| */
 | |
| 
 | |
| function markImagesDialog(){
 | |
| 
 | |
| 	updateStatus('Mark...').show()
 | |
| 
 | |
| 	var alg = 'Mark images:'
 | |
| 
 | |
| 	var cur = toggleMark('?') == 'on' ? 'Unmark' : 'Mark'
 | |
| 
 | |
| 	cfg = {}
 | |
| 	cfg[alg] = [
 | |
| 		cur + ' current image',
 | |
| 		cur + ' current block | '+
 | |
| 			'A block is a set of similarly marked images\n'+
 | |
| 			'to the left and right of the current image,\n'+
 | |
| 			'up until the closest images marked differently',
 | |
| 		'Invert marks in current ribbon',
 | |
| 		'Invert all marks',
 | |
| 		'Mark all in current ribbon',
 | |
| 		'Unmark all in current ribbon',
 | |
| 		'Mark all images',
 | |
| 		'Unmark all images'
 | |
| 	]
 | |
| 
 | |
| 	formDialog(null, '', 
 | |
| 			cfg,
 | |
| 			'OK', 
 | |
| 			'markImagesDialog')
 | |
| 		.done(function(res){
 | |
| 			res = res[alg]
 | |
| 
 | |
| 			// NOTE: these must be in order of least-specific last...
 | |
| 			if(/current image/.test(res)){
 | |
| 				toggleMark()
 | |
| 				var msg = (cur + ' image').toLowerCase()
 | |
| 
 | |
| 			} else if(/current block/.test(res)){
 | |
| 				toggleMarkBlock()
 | |
| 				var msg = 'toggled block marks'
 | |
| 
 | |
| 			} else if(/Invert .* ribbon/.test(res)){
 | |
| 				invertImageMarks()
 | |
| 				var msg = 'inverted ribbon marks'
 | |
| 
 | |
| 			} else if(/Invert all/.test(res)){
 | |
| 				invertImageMarks('all')
 | |
| 				var msg = 'inverted all marks'
 | |
| 
 | |
| 			} else if(/Mark all.*current ribbon/.test(res)){
 | |
| 				markAll('ribbon')
 | |
| 				var msg = 'marked ribbon'
 | |
| 
 | |
| 			} else if(/Mark all/.test(res)){
 | |
| 				markAll('all')
 | |
| 				var msg = 'marked all'
 | |
| 
 | |
| 			} else if(/Unmark all in/.test(res)){
 | |
| 				unmarkAll('ribbon')
 | |
| 				var msg = 'unmarked ribbon'
 | |
| 
 | |
| 			} else if(/Unmark all images/.test(res)){
 | |
| 				unmarkAll('all')
 | |
| 				var msg = 'unmarked all'
 | |
| 			}
 | |
| 
 | |
| 			showStatusQ('Mark: '+msg+'...')
 | |
| 		})
 | |
| 		.fail(function(){
 | |
| 			showStatusQ('Marking: canceled.')
 | |
| 		})
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * Files...
 | |
| */
 | |
| 
 | |
| // Load image marks form file
 | |
| //
 | |
| // NOTE: if no marks are found then set them to []
 | |
| var loadFileMarks = makeFileLoader(
 | |
| 		'Marks', 
 | |
| 		CONFIG.marked_file, 
 | |
| 		[],
 | |
| 		function(data){ 
 | |
| 			MARKED = populateSparseGIDList(data)
 | |
| 		},
 | |
| 		null,
 | |
| 		'marksLoaded')
 | |
| 
 | |
| 
 | |
| // Save image marks to file
 | |
| var saveFileMarks = makeFileSaver(
 | |
| 		'Marks',
 | |
| 		CONFIG.marked_file, 
 | |
| 		function(){ 
 | |
| 			return compactSparceList(MARKED)
 | |
| 		})
 | |
| 
 | |
| 
 | |
| function marksUpdated(){
 | |
| 	fileUpdated('Marks')
 | |
| 	$('.viewer').trigger('marksUpdated')
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * Setup...
 | |
| */
 | |
| 
 | |
| 
 | |
| function setupMarks(viewer){
 | |
| 	console.log('Marks: setup...')
 | |
| 
 | |
| 	// XXX make this viewer specific...
 | |
| 	makeContextIndicatorUpdater('marked')
 | |
| 
 | |
| 	// XXX make these viewer specific...
 | |
| 	showGlobalIndicator(
 | |
| 			'marks-visible', 
 | |
| 			'Marks visible (F2)')
 | |
| 		.click(function(){ toggleMarksView() })
 | |
| 	showGlobalIndicator(
 | |
| 			'marked-only-visible', 
 | |
| 			'Marked only images visible (shift-F2)')
 | |
| 		.click(function(){ toggleMarkedOnlyView() })
 | |
| 	showContextIndicator(
 | |
| 			'current-image-marked', 
 | |
| 			'Marked (Ins)')
 | |
| 		.click(function(){ toggleMark() })
 | |
| 
 | |
| 	return viewer
 | |
| 		.on('sortedImages', function(){
 | |
| 			MARKED = populateSparseGIDList(MARKED)
 | |
| 			marksUpdated()
 | |
| 		})
 | |
| 		.on('horizontalShiftedImage', function(evt, gid, direction, from, to){
 | |
| 			if(shiftGIDInSparseList(gid, from, to, MARKED)){
 | |
| 				marksUpdated()
 | |
| 			}
 | |
| 		})
 | |
| 		// when the gid is swapped update the cache...
 | |
| 		.on('updatedImageGID', function(evt, was, is){
 | |
| 			var i = MARKED.indexOf(was)
 | |
| 			if(i >= 0){
 | |
| 				MARKED[i] = is
 | |
| 				marksUpdated()
 | |
| 			}
 | |
| 		})
 | |
| }
 | |
| SETUP_BINDINGS.push(setupMarks)
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                                                */
 |