mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			824 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			824 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| /**********************************************************************
 | |
| * 
 | |
| *
 | |
| * Features:
 | |
| * 	- ui-ribbons-render
 | |
| * 	- ui-ribbons-edit-render
 | |
| * 	- ui-partial-ribbons
 | |
| *	- ui-animation
 | |
| *		manage UI non-css animations...
 | |
| *
 | |
| *
 | |
| **********************************************************************/
 | |
| ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | |
| (function(require){ var module={} // make module AMD/node compatible...
 | |
| /*********************************************************************/
 | |
| 
 | |
| var object = require('lib/object')
 | |
| var actions = require('lib/actions')
 | |
| var features = require('lib/features')
 | |
| 
 | |
| var core = require('features/core')
 | |
| 
 | |
| var ribbons = require('imagegrid/ribbons')
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // helpers...
 | |
| 
 | |
| // XXX make this compatible with multiple images...
 | |
| // XXX for muptiple targets this will just do a .reload()...
 | |
| var updateImagePosition =
 | |
| function updateImagePosition(actions, target){
 | |
| 	var s = actions.ribbons.getRibbonLocator()
 | |
| 
 | |
| 	if(s.length == 0){
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	target = target || actions.current
 | |
| 	target = target instanceof jQuery 
 | |
| 		? actions.ribbons.elemGID(target) 
 | |
| 		: target
 | |
| 
 | |
| 	var source_ribbon = actions.ribbons.elemGID(actions.ribbons.getRibbon(target))
 | |
| 	var source_order = actions.data.getImageOrder(target)
 | |
| 
 | |
| 	return function(){
 | |
| 		actions.ribbons.preventTransitions(s)
 | |
| 		var end = function(){
 | |
| 			// XXX not sure why this does not work without a setTimeout(..)
 | |
| 			//actions.ribbons.restoreTransitions(s, true)
 | |
| 			setTimeout(function(){ 
 | |
| 				actions.ribbons.restoreTransitions(s, true) }, 0) }
 | |
| 
 | |
| 		// XXX hack???
 | |
| 		if(target instanceof Array){
 | |
| 			actions.reload(true)
 | |
| 			return end()
 | |
| 		}
 | |
| 
 | |
| 		var target_ribbon = actions.data.getRibbon(target)
 | |
| 
 | |
| 		// nothing changed...
 | |
| 		if(source_ribbon == target_ribbon 
 | |
| 				&& actions.data.getImageOrder(target) == source_order){
 | |
| 			return end()
 | |
| 		}
 | |
| 
 | |
| 		// place image at position...
 | |
| 		var to = actions.data.getImage(target, 'next')
 | |
| 		if(to != null){
 | |
| 			actions.ribbons.placeImage(target, to, 'before')
 | |
| 
 | |
| 		} else {
 | |
| 			// place image after position...
 | |
| 			to = actions.data.getImage(target, 'prev')
 | |
| 			if(to != null){
 | |
| 				actions.ribbons.placeImage(target, to, 'after')
 | |
| 
 | |
| 			// new ribbon...
 | |
| 			} else {
 | |
| 				to = actions.data.getRibbon(target)
 | |
| 
 | |
| 				if(actions.ribbons.getRibbon(to).length == 0){
 | |
| 					actions.ribbons
 | |
| 						.placeRibbon(to, actions.data.getRibbonOrder(target))
 | |
| 				}
 | |
| 
 | |
| 				actions.ribbons.placeImage(target, to)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if(actions.data.getImages(source_ribbon).length == 0){
 | |
| 			actions.ribbons.getRibbon(source_ribbon).remove()
 | |
| 		}
 | |
| 
 | |
| 		actions.focusImage()
 | |
| 
 | |
| 		return end()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // Base ribbons viewer...
 | |
| 
 | |
| var RibbonsActions = 
 | |
| actions.Actions({
 | |
| 
 | |
| 	get dom(){
 | |
| 		return this.ribbons ? this.ribbons.viewer : undefined },
 | |
| 	
 | |
| 
 | |
| 	load: [
 | |
| 		function(data){
 | |
| 			return function(){
 | |
| 				// recycle the viewer if one is not given specifically...
 | |
| 				var viewer = data.viewer
 | |
| 				viewer = viewer == null && this.ribbons != null 
 | |
| 					? this.dom 
 | |
| 					: viewer
 | |
| 
 | |
| 				if(this.ribbons == null){
 | |
| 					this.ribbons = ribbons.Ribbons(viewer, this.images)
 | |
| 					// XXX is this correct???
 | |
| 					this.ribbons.__image_updaters = [this.updateImage.bind(this)]
 | |
| 
 | |
| 				} else {
 | |
| 					this.ribbons.clear()
 | |
| 					this.ribbons.images = this.images
 | |
| 				}
 | |
| 
 | |
| 				this.reload()
 | |
| 			}
 | |
| 		}],
 | |
| 	// NOTE: this will pass the .ribbons.updateData(..) a custom ribbon 
 | |
| 	// 		updater if one is defined here as .updateRibbon(target) action
 | |
| 	//
 | |
| 	// XXX HACK: two sins:
 | |
| 	// 		- actions.updateRibbon(..) and ribbons.updateRibbon(..)
 | |
| 	// 		  are NOT signature compatible...
 | |
| 	// 		- we depend on the internals of a custom add-on feature
 | |
| 	reload: ['Interface/Reload viewer',
 | |
| 		function(force){
 | |
| 			// full reload...
 | |
| 			if(force == 'full'){
 | |
| 				//this.stop()
 | |
| 				/*
 | |
| 				killAllWorkers()
 | |
| 					.done(function(){
 | |
| 						reload() 
 | |
| 					})
 | |
| 				*/
 | |
| 				location.reload()
 | |
| 			}
 | |
| 
 | |
| 			this.ribbons.preventTransitions()
 | |
| 
 | |
| 			// NOTE: this essentially sets the update threshold to 0...
 | |
| 			// XXX this should be a custom arg...
 | |
| 			force = force ? 0 : null
 | |
| 
 | |
| 			return function(){
 | |
| 				// see if we've got a custom ribbon updater...
 | |
| 				var that = this
 | |
| 				var settings = this.updateRibbon != null 
 | |
| 					// XXX this should be: { updateRibbon: this.updateRibbon.bind(this) }
 | |
| 					? { updateRibbon: function(_, ribbon){ 
 | |
| 							return that.updateRibbon(ribbon, null, null, force) 
 | |
| 						} }
 | |
| 					: null
 | |
| 
 | |
| 				this.ribbons.updateData(this.data, settings)
 | |
| 
 | |
| 				this
 | |
| 					// XXX should this be here???
 | |
| 					.refresh()
 | |
| 					.focusImage()
 | |
| 
 | |
| 				// XXX HACK to make browser redraw images...
 | |
| 				this.scale = this.scale
 | |
| 
 | |
| 				this.ribbons.restoreTransitions()
 | |
| 			}
 | |
| 		}],
 | |
| 	// NOTE: this will trigger .updateImage hooks...
 | |
| 	refresh: ['Interface/Refresh images without reloading',
 | |
| 		function(gids, scale){
 | |
| 			gids = gids || '*'
 | |
| 			var size = scale != null ? 
 | |
| 				this.ribbons.getVisibleImageSize('min', scale)
 | |
| 				: null
 | |
| 
 | |
| 			this.ribbons.updateImage(gids, null, size)
 | |
| 		}],
 | |
| 	clear: [
 | |
| 		function(){ this.ribbons && this.ribbons.clear() }],
 | |
| 	// XXX do we need clone???
 | |
| 	clone: [function(full){
 | |
| 		return function(res){
 | |
| 			if(this.ribbons){
 | |
| 				// NOTE: this is a bit wasteful as .ribbons will clone 
 | |
| 				// 		their ref to .images that we will throw away...
 | |
| 				res.ribbons = this.ribbons.clone()
 | |
| 				res.ribbons.images = res.images
 | |
| 			} 
 | |
| 		}
 | |
| 	}],
 | |
| 
 | |
| 
 | |
| 	replaceGid: [
 | |
| 		function(from, to){
 | |
| 			return function(res){
 | |
| 				res && this.ribbons.replaceGid(from, to)
 | |
| 			}
 | |
| 		}],
 | |
| 
 | |
| 
 | |
| 	// This is called by .ribbons, the goal is to use it to hook into 
 | |
| 	// image updating from features and extensions...
 | |
| 	//
 | |
| 	// NOTE: not intended for calling manually, use .refresh(..) instead...
 | |
| 	//
 | |
| 	// XXX EXPERIMENTAL...
 | |
| 	// 		...need this to get triggered by .ribbons
 | |
| 	// 		at this point manually triggering this will not do anything...
 | |
| 	// XXX problem: need to either redesign this or distinguish from 
 | |
| 	// 		other actions as I keep calling it expecting results...
 | |
| 	// XXX hide from user action list... (???)
 | |
| 	updateImage: ['- Interface/Update image (do not use directly)',
 | |
| 		'This is called by .refresh(..) and intended for use as an '
 | |
| 			+'trigger for handlers, and not as a user-callable acation.',
 | |
| 		core.notUserCallable(function(gid, image){
 | |
| 			// This is the image update protocol root function
 | |
| 			//
 | |
| 			// Not for direct use.
 | |
| 		})],
 | |
| 
 | |
| 	// NOTE: this not used directly, mainly designed as a utility to be 
 | |
| 	// 		used for various partial ribbon implementations...
 | |
| 	// XXX do we handle off-screen ribbons here???
 | |
| 	resizeRibbon: ['- Interface/Resize ribbon to n images',
 | |
| 		function(target, size){
 | |
| 			size = size 
 | |
| 				|| (this.config['ribbon-size-screens'] * this.screenwidth)
 | |
| 				|| (5 * this.screenwidth)
 | |
| 			var data = this.data
 | |
| 			var ribbons = this.ribbons
 | |
| 
 | |
| 			// localize transition prevention... 
 | |
| 			// NOTE: we can't get ribbon via target directly here as
 | |
| 			// 		the target might not be loaded...
 | |
| 			var r_gid = data.getRibbon(target)
 | |
| 			if(r_gid == null){
 | |
| 				return
 | |
| 			}
 | |
| 			// NOTE: for the initial load this may be empty...
 | |
| 			var r = ribbons.getRibbon(r_gid)
 | |
| 
 | |
| 			// XXX do we need to for example ignore unloaded (r.length == 0)
 | |
| 			// 		ribbons here, for example not load ribbons too far off 
 | |
| 			// 		screen??
 | |
| 			
 | |
| 			ribbons
 | |
| 				.preventTransitions(r)
 | |
| 				.updateRibbon(
 | |
| 					data.getImages(target, size, 'total'), 
 | |
| 					r_gid,
 | |
| 					target)
 | |
| 				.restoreTransitions(r, true)
 | |
| 		}],
 | |
| 
 | |
| 
 | |
| 	// NOTE: this will align only a single image...
 | |
| 	// XXX do we need these low level primitives here???
 | |
| 	centerImage: ['- Interface/Center an image in ribbon horizontally',
 | |
| 		function(target, align, offset, scale){
 | |
| 			target = target instanceof jQuery 
 | |
| 				? this.ribbons.elemGID(target)
 | |
| 				: target
 | |
| 
 | |
| 			// align current ribbon...
 | |
| 			this.ribbons.centerImage(target, align, offset, scale)
 | |
| 		}],
 | |
| 	centerRibbon: ['- Interface/Center a ribbon vertically',
 | |
| 		function(target){
 | |
| 			target = target instanceof jQuery 
 | |
| 				? this.ribbons.elemGID(target)
 | |
| 				: target
 | |
| 
 | |
| 			// align current ribbon...
 | |
| 			this.ribbons.centerRibbon(target)
 | |
| 		}],
 | |
| 
 | |
| 
 | |
| 	focusImage: [
 | |
| 		function(target, list){
 | |
| 			return function(){
 | |
| 				this.ribbons.focusImage(this.data != null ? this.current : target) } }],
 | |
| 	focusRibbon: [
 | |
| 		function(target, mode){
 | |
| 			mode = mode || this.config['ribbon-focus-mode']
 | |
| 
 | |
| 			var c = this.data.getRibbonOrder()
 | |
| 			var i = this.data.getRibbonOrder(target)
 | |
| 			// NOTE: we are not changing the direction here based on 
 | |
| 			// 		this.direction as swap will confuse the user...
 | |
| 			var direction = c < i ? 'before' : 'after'
 | |
| 
 | |
| 			if(mode == 'visual'){
 | |
| 				var ribbons = this.ribbons
 | |
| 				var r = this.data.getRibbon(target)
 | |
| 				var t = ribbons.getImageByPosition('current', r)
 | |
| 
 | |
| 				if(t.length > 1){
 | |
| 					t = t.eq(direction == 'before' ? 0 : 1)
 | |
| 				}
 | |
| 
 | |
| 				t = ribbons.elemGID(t)
 | |
| 
 | |
| 				this.focusImage(t, r)
 | |
| 			}
 | |
| 		}],
 | |
| 
 | |
| 
 | |
| 	// Zoom/scale protocol...
 | |
| 	resizing: [
 | |
| 		core.notUserCallable(function(unit, size, overflow){
 | |
| 			// This is a resizing protocol root function.
 | |
| 			//
 | |
| 			// This will never be used directly, but will wrap protocol user
 | |
| 			// functions.
 | |
| 			//
 | |
| 			// As an example see: .viewScale(..)
 | |
| 
 | |
| 			var that = this
 | |
| 			// stop currently running transition...
 | |
| 			this.ribbons.scale(this.ribbons.scale())
 | |
| 
 | |
| 			// transitionend handler...
 | |
| 			if(!this.__resize_handler){
 | |
| 				this.__resize_handler = function(){
 | |
| 					that.__post_resize
 | |
| 						&& that.resizingDone() 
 | |
| 					delete that.__post_resize
 | |
| 				}
 | |
| 			}
 | |
| 			this.ribbons.getRibbonSet()
 | |
| 				.off('transitionend', this.__resize_handler)
 | |
| 				.on('transitionend', this.__resize_handler)
 | |
| 
 | |
| 			// timeout handler...
 | |
| 			this.__post_resize && clearTimeout(this.__post_resize)
 | |
| 			return function(){
 | |
| 				this.__post_resize = setTimeout(
 | |
| 					this.__resize_handler, 
 | |
| 					this.config['resize-done-timeout'] || 300)
 | |
| 			}
 | |
| 		})],
 | |
| 
 | |
| 	viewScale: ['- Zoom/',
 | |
| 		function(scale){
 | |
| 			if(scale == null || scale == '?'){
 | |
| 				return this.ribbons != null ? this.ribbons.scale() : null
 | |
| 			}
 | |
| 
 | |
| 			this.resizing.chainCall(this, function(){
 | |
| 				this.ribbons 
 | |
| 					&& scale 
 | |
| 					&& this.ribbons.scale(scale)
 | |
| 				// NOTE: we pass explicit scale here to compensate for animation...
 | |
| 				this.refresh('*', scale)
 | |
| 			}, 'scale', scale)
 | |
| 		}],
 | |
| 	// NOTE: if this gets a count argument it will fit count images, 
 | |
| 	// 		default is one.
 | |
| 	// NOTE: this will add .config['fit-overflow'] to odd counts if no 
 | |
| 	// 		overflow if passed.
 | |
| 	// 		...this is done to add ability to control scroll indication.
 | |
| 	fitImage: ['Zoom/Fit image',
 | |
| 		function(count, overflow){
 | |
| 			if(count == '?'){
 | |
| 				return this.ribbons != null ? 
 | |
| 					this.ribbons.getScreenWidthImages() 
 | |
| 					: null
 | |
| 			}
 | |
| 
 | |
| 			this.resizing.chainCall(this, function(){
 | |
| 				if(count != null){
 | |
| 					overflow = overflow == false ? 0 : overflow
 | |
| 					var o = overflow != null ? overflow 
 | |
| 						: count % 2 != 1 ? 0
 | |
| 						: (this.config['fit-overflow'] || 0)
 | |
| 					count += o
 | |
| 				}
 | |
| 				// XXX .ribbons...
 | |
| 				this.ribbons.fitImage(count)
 | |
| 				// NOTE: we pass explicit scale here to compensate for animation...
 | |
| 				this.refresh('*', this.ribbons.getScreenWidthImages(1) / count)
 | |
| 			}, 'screenwidth', count, overflow)
 | |
| 		}],
 | |
| 	// NOTE: this does not account for ribbon spacing...
 | |
| 	fitRibbon: ['Zoom/Fit ribbon vertically',
 | |
| 		function(count, whole){
 | |
| 			if(count == '?'){
 | |
| 				return this.ribbons != null ? 
 | |
| 					this.ribbons.getScreenHeightRibbons() 
 | |
| 					: null
 | |
| 			}
 | |
| 
 | |
| 			this.resizing.chainCall(this, function(){
 | |
| 				// XXX .ribbons...
 | |
| 				this.ribbons.fitRibbon(count, whole)
 | |
| 				// NOTE: we pass explicit scale here to compensate for animation...
 | |
| 				this.refresh('*', this.ribbons.getScreenHeightRibbons(1, whole) / count)
 | |
| 			}, 'screenheight', count, whole)
 | |
| 		}],
 | |
| 
 | |
| 
 | |
| 	ribbonRotation: ['- Interface|Ribbon/', 
 | |
| 		function(a){ 
 | |
| 			if(arguments.length > 0){
 | |
| 				this.ribbons.rotate(a)
 | |
| 
 | |
| 			} else {
 | |
| 				return this.ribbons.rotate() || 0
 | |
| 			}
 | |
| 		}],
 | |
| })
 | |
| 
 | |
| 
 | |
| var Ribbons = 
 | |
| module.Ribbons = 
 | |
| core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'ui-ribbons-render',
 | |
| 	exclusive: ['ui-render'],
 | |
| 	depends: [
 | |
| 		// XXX BUG: for some reason this causes a dependency conflict...
 | |
| 		//'ui',
 | |
| 		'base',
 | |
| 	],
 | |
| 	suggested: [
 | |
| 		'ui-animation',
 | |
| 		'ui-ribbons-edit-render',
 | |
| 		'ui-partial-ribbons',
 | |
| 	],
 | |
| 
 | |
| 	actions: RibbonsActions, 
 | |
| 
 | |
| 	handlers: [],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // Edit...
 | |
| 
 | |
| var RibbonsEditActions = 
 | |
| actions.Actions({
 | |
| 	setBaseRibbon: [
 | |
| 		function(target){
 | |
| 			var r = this.data.getRibbon(target)
 | |
| 			r =  r == null ? this.ribbons.getRibbon(target) : r
 | |
| 			this.ribbons.setBaseRibbon(r)
 | |
| 		}],
 | |
| 	shiftImageLeft: [
 | |
| 		function(target){ this.ribbons.placeImage(target, -1) }],
 | |
| 	shiftImageRight: [
 | |
| 		function(target){ this.ribbons.placeImage(target, 1) }],
 | |
| 	shiftRibbonUp: [
 | |
| 		function(target){
 | |
| 			target = this.ribbons.getRibbon(target)
 | |
| 			var i = this.ribbons.getRibbonOrder(target)
 | |
| 			if(i > 0){
 | |
| 				this.ribbons.placeRibbon(target, i-1)
 | |
| 			}
 | |
| 		}],
 | |
| 	shiftRibbonDown: [
 | |
| 		function(target){
 | |
| 			target = this.ribbons.getRibbon(target)
 | |
| 			var i = this.ribbons.getRibbonOrder(target)
 | |
| 			if(i < this.data.ribbon_order.length-1){
 | |
| 				this.ribbons.placeRibbon(target, i+1)
 | |
| 			}
 | |
| 		}],
 | |
| 
 | |
| 	// basic image editing...
 | |
| 	//
 | |
| 	// XXX should we have .rotate(..) and .flip(..) generic actions???
 | |
| 	rotateCW: [ 
 | |
| 		function(target){ this.ribbons.rotateCW(target) }],
 | |
| 	rotateCCW: [ 
 | |
| 		function(target){ this.ribbons.rotateCCW(target) }],
 | |
| 	flipVertical: [ 
 | |
| 		function(target){ this.ribbons.flipVertical(target, 'view') }],
 | |
| 	flipHorizontal: [
 | |
| 		function(target){ this.ribbons.flipHorizontal(target, 'view') }],
 | |
| 
 | |
| 	// tags...
 | |
| 	tag: [ 
 | |
| 		function(tags, gids){ 
 | |
| 			gids = gids != null && gids.constructor !== Array ? [gids] : gids
 | |
| 			return function(){
 | |
| 				//this.ribbons.updateImage(gids) 
 | |
| 				this.refresh(gids)
 | |
| 			}
 | |
| 		}],
 | |
| 	untag: [
 | |
| 		function(tags, gids){ 
 | |
| 			gids = gids != null && gids.constructor !== Array ? [gids] : gids
 | |
| 			return function(){
 | |
| 				//this.ribbons.updateImage(gids) 
 | |
| 				this.refresh(gids)
 | |
| 			}
 | |
| 		}],
 | |
| })
 | |
| 
 | |
| var RibbonsEdit = 
 | |
| module.RibbonsEdit = 
 | |
| core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'ui-ribbons-edit-render',
 | |
| 	depends: [
 | |
| 		'edit',
 | |
| 		'tags',
 | |
| 		'sort',
 | |
| 		'crop',
 | |
| 		'image-group',
 | |
| 		'ui-ribbons-render',
 | |
| 	],
 | |
| 
 | |
| 	actions: RibbonsEditActions, 
 | |
| 
 | |
| 	handlers: [
 | |
| 		[[
 | |
| 			'shiftImageTo.pre',
 | |
| 			'shiftImageUp.pre',
 | |
| 			'shiftImageDown.pre',
 | |
| 		], 
 | |
| 			function(target){ 
 | |
| 				return updateImagePosition(this, target) }],
 | |
| 
 | |
| 
 | |
| 		// manage the .crop-mode css class...
 | |
| 		['crop uncrop',
 | |
| 			function(){
 | |
| 				this.dom[this.cropped ? 
 | |
| 					'addClass' 
 | |
| 					: 'removeClass']('crop-mode')
 | |
| 			}],
 | |
| 
 | |
| 		// reloading and updating...
 | |
| 		[[
 | |
| 			'sortImages',
 | |
| 			'alignToRibbon',
 | |
| 			'group',
 | |
| 			'ungroup',
 | |
| 			'groupTo',
 | |
| 			'groupMarked',
 | |
| 			'expandGroup',
 | |
| 			'collapseGroup',
 | |
| 			'crop',
 | |
| 			'uncrop',
 | |
| 		], 
 | |
| 			function(target){ return this.reload(true) }],
 | |
| 		[[
 | |
| 			'reverseImages',
 | |
| 			'reverseRibbons',
 | |
| 			'cropGroup',
 | |
| 		], 
 | |
| 			function(target){ return this.reload() }],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // Partial ribbons...
 | |
| 
 | |
| // XXX try using .ribbons.resizeRibbon(..) for basic tasks...
 | |
| // XXX try a strategy: load more in the direction of movement by an offset...
 | |
| // XXX .updateRibbon(..) is not signature compatible with data.updateRibbon(..)
 | |
| var PartialRibbonsActions = 
 | |
| actions.Actions({
 | |
| 	config: {
 | |
| 		// Number of screen widths to load...
 | |
| 		'ribbon-size-screens': 7,
 | |
| 
 | |
| 		// Number of screen widths to edge to trigger reload...
 | |
| 		'ribbon-resize-threshold': 1.5,
 | |
| 
 | |
| 		// Timeout before a non-forced ribbon size update happens after
 | |
| 		// the action...
 | |
| 		// NOTE: if set to null, the update will be sync...
 | |
| 		'ribbon-update-timeout': 120,
 | |
| 	},
 | |
| 
 | |
| 	// NOTE: this will force sync resize if one of the following is true:
 | |
| 	// 		- the target is not loaded
 | |
| 	// 		- we are less than screen width from the edge
 | |
| 	// 		- threshold is set to 0
 | |
| 	// XXX this is not signature compatible with data.updateRibbon(..)
 | |
| 	// XXX do not do anything for off-screen ribbons...
 | |
| 	updateRibbon: ['- Interface/Update partial ribbon size', 
 | |
| 		function(target, w, size, threshold){
 | |
| 			target = target instanceof jQuery 
 | |
| 				? this.ribbons.elemGID(target)
 | |
| 				// NOTE: data.getImage(..) can return null at start or end
 | |
| 				// 		of ribbon, thus we need to account for this...
 | |
| 				: (this.data.getImage(target)
 | |
| 					|| this.data.getImage(target, 'after'))
 | |
| 
 | |
| 			w = w || this.screenwidth
 | |
| 
 | |
| 			// get config data and normalize...
 | |
| 			size = (size 
 | |
| 				|| this.config['ribbon-size-screens'] 
 | |
| 				|| 5) * w
 | |
| 			threshold = threshold == 0 ? threshold
 | |
| 				: (threshold 
 | |
| 					|| this.config['ribbon-resize-threshold'] 
 | |
| 					|| 1) * w
 | |
| 
 | |
| 			var timeout = this.config['ribbon-update-timeout']
 | |
| 
 | |
| 			// next/prev loaded... 
 | |
| 			var img = this.ribbons.getImage(target)
 | |
| 			var nl = img.nextAll('.image:not(.clone)').length
 | |
| 			var pl = img.prevAll('.image:not(.clone)').length
 | |
| 
 | |
| 			// next/prev available...
 | |
| 			// NOTE: we subtract 1 to remove the current and make these 
 | |
| 			// 		compatible with: nl, pl
 | |
| 			var na = this.data.getImages(target, size, 'after').length - 1
 | |
| 			var pa = this.data.getImages(target, size, 'before').length - 1
 | |
| 
 | |
| 			// do the update...
 | |
| 			// no threshold means force load...
 | |
| 			if(threshold == 0 
 | |
| 					// the target is not loaded...
 | |
| 					|| img.length == 0
 | |
| 					// passed hard threshold on the right...
 | |
| 					|| (nl < w && na > nl) 
 | |
| 					// passed hard threshold on the left...
 | |
| 					|| (pl < w && pa > pl)){
 | |
| 
 | |
| 				this.resizeRibbon(target, size)
 | |
| 
 | |
| 			// do a late resize...
 | |
| 			// loaded more than we need (crop?)...
 | |
| 			} else if(na + pa < nl + pl
 | |
| 					// passed threshold on the right...
 | |
| 					|| (nl < threshold && na > nl) 
 | |
| 					// passed threshold on the left...
 | |
| 					|| (pl < threshold && pa > pl) 
 | |
| 					// loaded more than we need by threshold...
 | |
| 					|| nl + pl + 1 > size + threshold){
 | |
| 
 | |
| 				return function(){
 | |
| 					// sync update...
 | |
| 					if(timeout == null){
 | |
| 						this.resizeRibbon(target, size)
 | |
| 
 | |
| 					// async update...
 | |
| 					} else {
 | |
| 						// XXX need to check if we are too close to the edge...
 | |
| 						var that = this
 | |
| 						//setTimeout(function(){ that.resizeRibbon(target, size) }, 0)
 | |
| 						if(this.__update_timeout){
 | |
| 							clearTimeout(this.__update_timeout)
 | |
| 						}
 | |
| 						this.__update_timeout = setTimeout(function(){ 
 | |
| 							delete that.__update_timeout
 | |
| 							that.resizeRibbon(target, size) 
 | |
| 						}, timeout)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}],
 | |
| })
 | |
| 
 | |
| var PartialRibbons = 
 | |
| module.PartialRibbons = 
 | |
| core.ImageGridFeatures.Feature({
 | |
| 	title: 'Partial Ribbons',
 | |
| 	doc: core.doc`Maintains partially loaded ribbons, this enables very large
 | |
| 	image sets to be handled efficiently.`,
 | |
| 
 | |
| 	// NOTE: partial ribbons needs to be setup first...
 | |
| 	// 		...the reasons why things break otherwise is not too clear.
 | |
| 	priority: 'high',
 | |
| 
 | |
| 	tag: 'ui-partial-ribbons',
 | |
| 	exclusive: ['ui-partial-ribbons'],
 | |
| 	depends: [
 | |
| 		//'ui',
 | |
| 		'ui-ribbons-render',
 | |
| 	],
 | |
| 	suggested: [
 | |
| 		'ui-partial-ribbons-precache',
 | |
| 	],
 | |
| 
 | |
| 
 | |
| 	actions: PartialRibbonsActions,
 | |
| 
 | |
| 	handlers: [
 | |
| 		['focusImage.pre centerImage.pre', 
 | |
| 			function(target, list){
 | |
| 				// NOTE: we have to do this as we are called BEFORE the 
 | |
| 				// 		actual focus change happens...
 | |
| 				// XXX is there a better way to do this???
 | |
| 				target = list != null ? target = this.data.getImage(target, list) : target
 | |
| 
 | |
| 				this.updateRibbon(target)
 | |
| 			}],
 | |
| 		['resizing.pre',
 | |
| 			function(unit, size){
 | |
| 				// keep constant size in single image...
 | |
| 				if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){
 | |
| 					this.updateRibbon(
 | |
| 						'current', 
 | |
| 						this.config['ribbons-resize-single-image'] || 13)
 | |
| 
 | |
| 				} else if(unit == 'scale'){
 | |
| 					this.updateRibbon('current', this.screenwidth / size || 1)
 | |
| 
 | |
| 				} else if(unit == 'screenwidth'){
 | |
| 					this.updateRibbon('current', size || 1)
 | |
| 
 | |
| 				} else if(unit == 'screenheight'){
 | |
| 					size = size || 1
 | |
| 
 | |
| 					// convert target height in ribbons to width in images...
 | |
| 					// NOTE: this does not account for compensation that 
 | |
| 					// 		.updateRibbon(..) makes for fitting whole image
 | |
| 					// 		counts, this is a small enough error so as not
 | |
| 					// 		to waste time on...
 | |
| 					var s = this.ribbons.scale()
 | |
| 					var h = this.ribbons.getScreenHeightRibbons()
 | |
| 					var w = this.ribbons.getScreenWidthImages()
 | |
| 					var nw = w / (h/size)
 | |
| 
 | |
| 					this.updateRibbon('current', nw)
 | |
| 				}
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // Animation...
 | |
| 
 | |
| // XXX at this point this does not support target lists...
 | |
| // XXX shift up/down to new ribbon is not too correct...
 | |
| // XXX depends on .ribbons.makeShadow(..)...
 | |
| var ShiftAnimation =
 | |
| module.ShiftAnimation = core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'ui-animation',
 | |
| 	depends: [
 | |
| 		'ui-ribbons-render',
 | |
| 	],
 | |
| 	// NOTE: this will allow the animations to start as early as possible
 | |
| 	// 		in the action call...
 | |
| 	priority: 'high',
 | |
| 
 | |
| 	config: {
 | |
| 		// XXX make this duration...
 | |
| 		'shadow-animation-delay': 200,
 | |
| 		'shadow-animation-start-delay': 0,
 | |
| 	},
 | |
| 
 | |
| 	handlers: [
 | |
| 		//['shiftImageUp.pre shiftImageDown.pre '
 | |
| 		//		+'travelImageUp.pre travelImageDown.pre', 
 | |
| 		['shiftImageUp.pre shiftImageDown.pre',
 | |
| 			function(target){
 | |
| 				// XXX do not do target lists...
 | |
| 				if(target != null && target.constructor === Array 
 | |
| 						// do not animate in single image mode...
 | |
| 						&& this.toggleSingleImage('?') == 'on'){
 | |
| 					return
 | |
| 				}
 | |
| 				var s = this.ribbons.makeShadow(target, true, 
 | |
| 					// XXX make this duration...
 | |
| 					this.config['shadow-animation-delay'],
 | |
| 					this.config['shadow-animation-start-delay'])
 | |
| 				return function(){ s() }
 | |
| 			}],
 | |
| 		// NOTE: this will keep the shadow in place -- the shadow will not
 | |
| 		// 		go to the mountain, the mountain will come to the shadow ;)
 | |
| 		['shiftImageLeft.pre shiftImageRight.pre', 
 | |
| 			function(target){
 | |
| 				// XXX do not do target lists...
 | |
| 				if(target != null && target.constructor === Array
 | |
| 						// do not animate in single image mode...
 | |
| 						&& this.toggleSingleImage('?') == 'on'){
 | |
| 					return
 | |
| 				}
 | |
| 				var s = this.ribbons.makeShadow(target, undefined,
 | |
| 					// XXX make this duration...
 | |
| 					this.config['shadow-animation-delay'],
 | |
| 					this.config['shadow-animation-start-delay'])
 | |
| 				return function(){ s() }
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                               */ return module })
 |