mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-29 18:30:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			400 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| /**********************************************************************
 | |
| * 
 | |
| *
 | |
| *
 | |
| **********************************************************************/
 | |
| ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | |
| (function(require){ var module={} // make module AMD/node compatible...
 | |
| /*********************************************************************/
 | |
| 
 | |
| var toggler = require('lib/toggler')
 | |
| var actions = require('lib/actions')
 | |
| var features = require('lib/features')
 | |
| 
 | |
| var core = require('features/core')
 | |
| var base = require('features/base')
 | |
| 
 | |
| var widgets = require('features/ui-widgets')
 | |
| 
 | |
| var browse = require('lib/widget/browse')
 | |
| var overlay = require('lib/widget/overlay')
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| 
 | |
| // NOTE: this uses ui-chrome-hidden workspace to set the initial state
 | |
| // 		of the slideshow workspace.
 | |
| //
 | |
| // XXX would be a good idea to add provision for a timer to indicate 
 | |
| // 		slideshow progress/status... 
 | |
| // 			- make this a separate feature with a toggler
 | |
| // XXX might be a good idea to add "slideshow hold", for example while 
 | |
| // 		mouse down...
 | |
| var SlideshowActions = actions.Actions({
 | |
| 	config: {
 | |
| 		'slideshow-looping': 'on',
 | |
| 		'slideshow-direction': 'forward',
 | |
| 		'slideshow-interval': '3s',
 | |
| 		'slideshow-interval-max-count': 7,
 | |
| 		'slideshow-hold': 'on',
 | |
| 
 | |
| 		'slideshow-intervals': [
 | |
| 			'0.2s',
 | |
| 			'1s',
 | |
| 			'3s',
 | |
| 			'5s',
 | |
| 			'7s',
 | |
| 		],
 | |
| 	},
 | |
| 
 | |
| 	toggleSlideshow: ['Slideshow/$Slideshow quick toggle',
 | |
| 		toggler.CSSClassToggler(
 | |
| 			function(){ return this.dom }, 
 | |
| 			'slideshow-running',
 | |
| 			function(state){
 | |
| 				// start...
 | |
| 				if(state == 'on'){
 | |
| 					var that = this
 | |
| 
 | |
| 					// reset the timer...
 | |
| 					// NOTE: this means we were in a slideshow mode so we do not
 | |
| 					// 		need to prepare...
 | |
| 					if(this.__slideshow_timer){
 | |
| 						this.__slideshow_timer != 'suspended'
 | |
| 							&& clearInterval(this.__slideshow_timer)
 | |
| 						delete this.__slideshow_timer
 | |
| 
 | |
| 					// prepare for the slideshow...
 | |
| 					} else {
 | |
| 						// single image mode...
 | |
| 						this.toggleSingleImage('on')
 | |
| 
 | |
| 						// save current workspace...
 | |
| 						this.pushWorkspace()
 | |
| 
 | |
| 						// construct the slideshow workspace if it does
 | |
| 						// not exist...
 | |
| 						//
 | |
| 						// NOTE: this is partially redundant with the 
 | |
| 						// 		loadWorkspace.pre handler in the feature...
 | |
| 						if(this.workspaces['slideshow'] == null){
 | |
| 							this.loadWorkspace('ui-chrome-hidden') 
 | |
| 							this.saveWorkspace('slideshow') 
 | |
| 						}
 | |
| 
 | |
| 						// load the slideshow workspace...
 | |
| 						this.loadWorkspace('slideshow')
 | |
| 					}
 | |
| 
 | |
| 					// disable getting in/out of single image mode via clicks... 
 | |
| 					if(this.config['single-image-toggle-on-click']){
 | |
| 						this.config['single-image-toggle-on-click'] = false
 | |
| 					}
 | |
| 
 | |
| 					// start the timer... 
 | |
| 					this.__slideshow_timer = setInterval(function(){
 | |
| 						var cur = that.current
 | |
| 
 | |
| 						// next step...
 | |
| 						that.config['slideshow-direction'] == 'forward' ?
 | |
| 							that.nextImage()
 | |
| 							: that.prevImage()
 | |
| 
 | |
| 						// we have reached the end...
 | |
| 						if(that.current == cur){
 | |
| 							// loop...
 | |
| 							if(that.config['slideshow-looping'] == 'on'){
 | |
| 								that.config['slideshow-direction'] == 'forward' ?
 | |
| 									that.firstImage()
 | |
| 									: that.lastImage()
 | |
| 
 | |
| 							// stop...
 | |
| 							} else {
 | |
| 								that.toggleSlideshow('off')
 | |
| 							}
 | |
| 						}
 | |
| 					}, Date.str2ms(this.config['slideshow-interval'] || '3s'))
 | |
| 
 | |
| 				// stop...
 | |
| 				} else {
 | |
| 					// stop timer...
 | |
| 					this.__slideshow_timer
 | |
| 						&& clearInterval(this.__slideshow_timer)
 | |
| 					delete this.__slideshow_timer
 | |
| 
 | |
| 					// restore the original workspace...
 | |
| 					this.popWorkspace()
 | |
| 				}
 | |
| 			})],
 | |
| 
 | |
| 	// XXX should this reflect slideshow state???? (ready, running, paused???)
 | |
| 	slideshowDialog: ['Slideshow/Slideshow...',
 | |
| 		widgets.makeUIDialog(function(){
 | |
| 			var that = this
 | |
| 
 | |
| 			// suspend the timer if it's not suspended outside...
 | |
| 			var suspended_timer = this.__slideshow_timer == 'suspended'
 | |
| 			suspended_timer || this.suspendSlideshowTimer()
 | |
| 
 | |
| 			// XXX might be a good idea to make this generic...
 | |
| 			var _makeToggleHandler = function(toggler){
 | |
| 				return function(){
 | |
| 					var txt = $(this).find('.text').first().text()
 | |
| 					that[toggler]()
 | |
| 					o.update()
 | |
| 						.then(function(){ o.select(txt) })
 | |
| 					that.toggleSlideshow('?') == 'on' 
 | |
| 						&& o.close()
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			var o = browse.makeLister(null, function(path, make){
 | |
| 					make(['$Interval: ', 
 | |
| 							function(){ return that.config['slideshow-interval'] }])
 | |
| 						.on('open', function(){
 | |
| 							that.slideshowIntervalDialog(make.dialog) })
 | |
| 
 | |
| 					make(['$Direction: ', 
 | |
| 							function(){ return that.config['slideshow-direction'] }])
 | |
| 						.on('open', _makeToggleHandler('toggleSlideshowDirection'))
 | |
| 					make(['$Looping: ', 
 | |
| 							function(){ return that.config['slideshow-looping'] }])
 | |
| 						.on('open', _makeToggleHandler('toggleSlideshowLooping'))
 | |
| 
 | |
| 					// Start/stop...
 | |
| 					make([function(){ 
 | |
| 							return that.toggleSlideshow('?') == 'on' ? '$Stop' : '$Start' }])
 | |
| 						.on('open', function(){
 | |
| 							that.toggleSlideshow()
 | |
| 							o.close()
 | |
| 						})
 | |
| 				},
 | |
| 				{
 | |
| 					path: that.toggleSlideshow('?') == 'on' ? 'Stop' : 'Start',
 | |
| 					cls: 'table-view tail-action',
 | |
| 				})
 | |
| 				.on('close', function(){
 | |
| 					// reset the timer if it was not suspended outside...
 | |
| 					suspended_timer 
 | |
| 						|| that.resetSlideshowTimer()
 | |
| 				})
 | |
| 
 | |
| 			return o
 | |
| 		})],
 | |
| 	
 | |
| 	// settings...
 | |
| 	// NOTE: these are disabled as they are repeated in the slideshow dialog...
 | |
| 	// XXX do we need both these and the dialog???
 | |
| 	slideshowIntervalDialog: ['- Slideshow/Slideshow $interval...',
 | |
| 		widgets.makeUIDialog(function(parent){
 | |
| 			var that = this
 | |
| 			var dialog = widgets.makeConfigListEditor(
 | |
| 				that, 
 | |
| 				'slideshow-intervals',
 | |
| 				'slideshow-interval', 
 | |
| 				{
 | |
| 					length_limit: that.config['slideshow-interval-max-count'],
 | |
| 					check: Date.str2ms,
 | |
| 					unique: Date.str2ms,
 | |
| 					sort: function(a, b){
 | |
| 						return Date.str2ms(a) - Date.str2ms(b) },
 | |
| 				})
 | |
| 				.on('start', function(){
 | |
| 					// suspend the timer if it's not suspended outside...
 | |
| 					this.__slideshow_timer == 'suspended'
 | |
| 						|| this.suspendSlideshowTimer()
 | |
| 				})
 | |
| 				.on('close', function(){
 | |
| 					// reset the timer if it was not suspended outside...
 | |
| 					this.__slideshow_timer == 'suspended'
 | |
| 						|| that.resetSlideshowTimer()
 | |
| 
 | |
| 					if(parent){
 | |
| 						var txt = parent.select('!').find('.text').first().text()
 | |
| 
 | |
| 						parent.update()
 | |
| 							.then(function(){ 
 | |
| 								txt != ''
 | |
| 									&& parent.select(txt)
 | |
| 							})
 | |
| 					}
 | |
| 				})
 | |
| 			return dialog
 | |
| 		})],
 | |
| 	toggleSlideshowDirection: ['- Slideshow/Slideshow $direction',
 | |
| 		core.makeConfigToggler('slideshow-direction', ['forward', 'reverse'])],
 | |
| 	toggleSlideshowLooping: ['- Slideshow/Slideshow $looping',
 | |
| 		core.makeConfigToggler('slideshow-looping', ['on', 'off'])],
 | |
| 
 | |
| 	toggleSlideshowHold: ['Interface|Slideshow/Slideshow $hold',
 | |
| 		core.makeConfigToggler('slideshow-hold', ['on', 'off'])],
 | |
| 
 | |
| 	resetSlideshowTimer: ['- Slideshow/Reset slideshow timer',
 | |
| 		function(){
 | |
| 			this.__slideshow_timer && this.toggleSlideshow('on') }],
 | |
| 	suspendSlideshowTimer: ['- Slideshow/Suspend slideshow timer',
 | |
| 		function(){
 | |
| 			if(this.__slideshow_timer){
 | |
| 				clearInterval(this.__slideshow_timer)
 | |
| 				this.__slideshow_timer = 'suspended'
 | |
| 			}
 | |
| 		}],
 | |
| 	toggleSlideshowTimer:['Slideshow/Pause or resume running slideshow',
 | |
| 		core.doc`
 | |
| 
 | |
| 		NOTE: this will have no effect if the slideshow is not running...
 | |
| 		`,
 | |
| 		{browseMode: function(){ return this.toggleSlideshow('?') == 'off' && 'disabled' }},
 | |
| 		toggler.Toggler(null, 
 | |
| 			function(_, state){ 
 | |
| 				if(state == null){
 | |
| 					return this.__slideshow_timer == 'suspended' ? 'paused' 
 | |
| 						: !!this.__slideshow_timer ? 'running'
 | |
| 						: 'off'
 | |
| 				}
 | |
| 
 | |
| 				if(this.toggleSlideshow('?') != 'on'){
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				// handle state changing...
 | |
| 				if(state == 'paused' || state == 'off'){
 | |
| 					this.suspendSlideshowTimer()
 | |
| 					return 'paused'
 | |
| 
 | |
| 				} else {
 | |
| 					this.resetSlideshowTimer() 
 | |
| 					return 'running'
 | |
| 				}
 | |
| 			}, 
 | |
| 			// XXX should we return different sets of states when 
 | |
| 			// 		slideshow is running and when not?
 | |
| 			// 		...the problem is what to return when slideshow is 
 | |
| 			// 		not running???
 | |
| 			['paused', 'running', 'off'])],
 | |
| 
 | |
| 	resetSlideshowWorkspace: ['Slideshow/Reset workspace',
 | |
| 		function(){ delete this.workspaces['slideshow'] }],
 | |
| })
 | |
| 
 | |
| 
 | |
| var Slideshow = 
 | |
| module.Slideshow = core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'ui-slideshow',
 | |
| 	depends: [
 | |
| 		'ui',
 | |
| 		'ui-single-image',
 | |
| 	],
 | |
| 
 | |
| 	actions: SlideshowActions,
 | |
| 
 | |
| 	handlers: [
 | |
| 		// add a placeholder for slideshow workspace, this is to make the
 | |
| 		// workspace toggler show it as one of the options...
 | |
| 		//
 | |
| 		// NOTE: the slideshow workspace will get populated either on 
 | |
| 		// 		loading it for the first time or on first running a 
 | |
| 		// 		slideshow...
 | |
| 		['start',
 | |
| 			function(){ 
 | |
| 				if(this.workspaces['slideshow'] == null){
 | |
| 					this.workspaces['slideshow'] = null
 | |
| 				} 
 | |
| 			}],
 | |
| 
 | |
| 		['load',
 | |
| 			function(){ this.toggleSlideshow('off') }],
 | |
| 
 | |
| 		// - build the slideshow workspace for the first time if it's not
 | |
| 		// 		present yet (is null)...
 | |
| 		// - restore .config['single-image-toggle-on-click']
 | |
| 		['loadWorkspace.pre',
 | |
| 			function(workspace){
 | |
| 				// going out of slideshow -> restore settings...
 | |
| 				var data = this.workspaces[workspace] || {} 
 | |
| 				if(this.workspace == 'slideshow'){
 | |
| 					if(!('single-image-toggle-on-click' in data)){
 | |
| 						delete this.config['single-image-toggle-on-click']
 | |
| 
 | |
| 					} else {
 | |
| 						this.config['single-image-toggle-on-click'] = 
 | |
| 							data['single-image-toggle-on-click']
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// going into slideshow for first time -> setup...
 | |
| 				if(workspace == 'slideshow' && this.workspaces['slideshow'] == null){
 | |
| 					return function(){
 | |
| 						this.loadWorkspace('ui-chrome-hidden') 
 | |
| 						this.saveWorkspace('slideshow') 
 | |
| 					}
 | |
| 				}
 | |
| 			}],
 | |
| 
 | |
| 		// do not leave the viewer in slideshow mode...
 | |
| 		['stop',
 | |
| 			function(){ this.toggleSlideshow('off') }],
 | |
| 
 | |
| 		// slideshow hold...
 | |
| 		//
 | |
| 		// Holding down a key or mouse button will suspend the slideshow 
 | |
| 		// and resume it when the key/button is released...
 | |
| 		//
 | |
| 		// XXX experimental, needs more testing...
 | |
| 		['toggleSlideshow',
 | |
| 			function(){
 | |
| 				var that = this
 | |
| 
 | |
| 				if(!this.dom){
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				var hold = this.__slideshow_hold_handler 
 | |
| 					= this.__slideshow_hold_handler 
 | |
| 						|| function(){
 | |
| 							var h = that.__slideshow_holding
 | |
| 
 | |
| 							if(h == null && that.toggleSlideshowTimer('?') == 'running'){
 | |
| 								that.__slideshow_holding = 1
 | |
| 								that.suspendSlideshowTimer()
 | |
| 
 | |
| 							// handle multiple events stacked...
 | |
| 							} else {
 | |
| 								that.__slideshow_holding += 1
 | |
| 							}
 | |
| 						}
 | |
| 				var release = this.__slideshow_release_handler 
 | |
| 					= this.__slideshow_release_handler 
 | |
| 						|| function(){
 | |
| 							var h = that.__slideshow_holding = 
 | |
| 								// unstack multiple events...
 | |
| 								(that.__slideshow_holding || -1) - 1
 | |
| 
 | |
| 							if(h == 0){
 | |
| 								that.resetSlideshowTimer()
 | |
| 								delete that.__slideshow_holding
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 				if(this.toggleSlideshow('?') == 'on'){
 | |
| 					this.dom.on('keydown mousedown', hold)
 | |
| 					this.dom.on('keydown mouseup', release)
 | |
| 
 | |
| 				} else {
 | |
| 					this.dom.off('keyup mousedown', hold)
 | |
| 					this.dom.off('keyup mouseup', release)
 | |
| 				}
 | |
| 			}]
 | |
| 		//*/
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                               */ return module })
 |