mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-11-04 13:20:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			751 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			751 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**********************************************************************
 | 
						|
* 
 | 
						|
*
 | 
						|
*
 | 
						|
**********************************************************************/
 | 
						|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | 
						|
(function(require){ var module={} // make module AMD/node compatible...
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
if(typeof(process) != 'undefined'){
 | 
						|
	var pathlib = requirejs('path')
 | 
						|
	var url = requirejs('url')
 | 
						|
}
 | 
						|
 | 
						|
var electron
 | 
						|
try{
 | 
						|
	electron = requirejs('electron')
 | 
						|
} catch(e){ }
 | 
						|
 | 
						|
var VERSION = require('version').version
 | 
						|
 | 
						|
var actions = require('lib/actions')
 | 
						|
var features = require('lib/features')
 | 
						|
var toggler = require('lib/toggler')
 | 
						|
 | 
						|
var core = require('features/core')
 | 
						|
var base = require('features/base')
 | 
						|
var widgets = require('features/ui-widgets')
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
// helpers...
 | 
						|
 | 
						|
// XXX this includes a WebKit bug patch -- see inside...
 | 
						|
var img2canvas = 
 | 
						|
function({url, orientation, flipped}, callback){
 | 
						|
 | 
						|
	// XXX PATCH...
 | 
						|
	// XXX <canvas>.drawImage(..) seems to take EXIF into account, ignoring 
 | 
						|
	// 		the .imageOrientation setting both the canvas and image are in
 | 
						|
	// 		DOM and the image needs to be added to dom before .src is set
 | 
						|
	var PATCHED_ELEMENTS
 | 
						|
	var PATCH = function(e){
 | 
						|
		PATCHED_ELEMENTS = 
 | 
						|
			PATCHED_ELEMENTS
 | 
						|
			|| document.body.appendChild(
 | 
						|
				document.createElement('div')
 | 
						|
					.run(function(){
 | 
						|
						this.style.position = 'absolute'
 | 
						|
						this.style.with = '0'
 | 
						|
						this.style.height = '0'
 | 
						|
						this.style.top = '200%'
 | 
						|
						this.style.left = '0'
 | 
						|
						this.style.opacity = 0
 | 
						|
						this.style.overflow = 'hidden' }))
 | 
						|
		PATCHED_ELEMENTS.appendChild(e) }
 | 
						|
	// XXX PATCH...
 | 
						|
	var CLEANUP_PATCH = function(){
 | 
						|
		PATCHED_ELEMENTS
 | 
						|
			.parentElement
 | 
						|
				.removeChild(PATCHED_ELEMENTS) }
 | 
						|
 | 
						|
	var img = new Image
 | 
						|
	img.onload = function(){
 | 
						|
		var width = this.naturalWidth
 | 
						|
		var height = this.naturalHeight
 | 
						|
 | 
						|
		var c = document.createElement('canvas')
 | 
						|
		c.style.imageOrientation = 'none'
 | 
						|
 | 
						|
		// XXX PATCH...
 | 
						|
		PATCH(c)
 | 
						|
 | 
						|
		var ctx = c.getContext('2d')
 | 
						|
		// prepare for rotate...
 | 
						|
		// 90 / 270
 | 
						|
		if(orientation == 90 || orientation == 270){
 | 
						|
			var w = c.width = this.naturalHeight
 | 
						|
			var h = c.height = this.naturalWidth
 | 
						|
		// 0 / 180
 | 
						|
		} else {
 | 
						|
			var w = c.width = this.naturalWidth
 | 
						|
			var h = c.height = this.naturalHeight }
 | 
						|
		// prepare for flip...
 | 
						|
		var x = flipped && flipped.includes('horizontal') ? 
 | 
						|
			-1 
 | 
						|
			: 1
 | 
						|
		var y = flipped && flipped.includes('vertical') ? 
 | 
						|
			-1 
 | 
						|
			: 1
 | 
						|
 | 
						|
		ctx.translate(w/2, h/2)
 | 
						|
		ctx.rotate(orientation * Math.PI/180)
 | 
						|
		ctx.scale(x, y)
 | 
						|
		ctx.drawImage(this, -width/2, -height/2)
 | 
						|
 | 
						|
		// XXX PATCH...
 | 
						|
		CLEANUP_PATCH()
 | 
						|
 | 
						|
		callback.call(this, c) }
 | 
						|
 | 
						|
	// prevent the browser from rotating the image via exif...
 | 
						|
	img.style.imageOrientation = 'none'
 | 
						|
	img.crossOrigin = ''
 | 
						|
 | 
						|
	// XXX PATCH...
 | 
						|
	PATCH(img)
 | 
						|
 | 
						|
	img.src = url 
 | 
						|
	return img }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
var Widget = 
 | 
						|
module.Widget = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'widget',
 | 
						|
	depends: [
 | 
						|
		'-ui-app-control',
 | 
						|
	],
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
var NWHostActions = actions.Actions({
 | 
						|
	// window stuff...
 | 
						|
	// XXX should this be nested in a .window object???
 | 
						|
	get title(){
 | 
						|
		return nw.Window.get().title },
 | 
						|
	set title(value){
 | 
						|
		nw.Window.get().title = value },
 | 
						|
	get size(){
 | 
						|
		var win = nw.Window.get()
 | 
						|
		return [
 | 
						|
			win.width,
 | 
						|
			win.height,
 | 
						|
		]
 | 
						|
	},
 | 
						|
	set size(value){
 | 
						|
		var win = nw.Window.get()
 | 
						|
		win.width = value[0]
 | 
						|
		win.height = value[1]
 | 
						|
	},
 | 
						|
	get position(){
 | 
						|
		var win = nw.Window.get()
 | 
						|
		return [
 | 
						|
			win.x,
 | 
						|
			win.y,
 | 
						|
		]
 | 
						|
	},
 | 
						|
	set position(value){
 | 
						|
		var win = nw.Window.get()
 | 
						|
		win.x = value[0]
 | 
						|
		win.y = value[1]
 | 
						|
	},
 | 
						|
 | 
						|
	show: ['- Window/',
 | 
						|
		function(){
 | 
						|
			nw.Window.get().show() }],
 | 
						|
	minimize: ['Window/Minimize',
 | 
						|
		function(){
 | 
						|
			nw.Window.get().minimize() }],
 | 
						|
	toggleFullScreen: ['Window/Full screen mode',
 | 
						|
		toggler.CSSClassToggler(
 | 
						|
			function(){ return document.body }, 
 | 
						|
			'.full-screen-mode',
 | 
						|
			function(action){
 | 
						|
				var that = this
 | 
						|
				var w = nw.Window.get()
 | 
						|
 | 
						|
				// change the state only if the target state is not the same
 | 
						|
				// as the current state...
 | 
						|
				if((w.isFullscreen ? 'on' : 'off') != action){
 | 
						|
					this.ribbons.preventTransitions()
 | 
						|
 | 
						|
					// hide the viewer to hide any animation crimes...
 | 
						|
					this.dom[0].style.visibility = 'hidden'
 | 
						|
 | 
						|
					// XXX async...
 | 
						|
					// 		...this complicates things as we need to do the next
 | 
						|
					// 		bit AFTER the resize is done...
 | 
						|
					w.toggleFullscreen()
 | 
						|
 | 
						|
					setTimeout(function(){ 
 | 
						|
						that
 | 
						|
							.centerViewer()
 | 
						|
							.focusImage()
 | 
						|
							.ribbons
 | 
						|
								.restoreTransitions()
 | 
						|
 | 
						|
						that.dom[0].style.visibility = ''
 | 
						|
					}, 100)
 | 
						|
				}
 | 
						|
 | 
						|
				// NOTE: we delay this to account for window animation...
 | 
						|
				setTimeout(function(){ 
 | 
						|
					that.storeWindowGeometry() 
 | 
						|
				}, 500)
 | 
						|
			})],
 | 
						|
 | 
						|
	// XXX add ability to use devtools on background page (node context)...
 | 
						|
	// XXX get the devtools stage...
 | 
						|
	showDevTools: ['Interface|Development/Show Dev Tools',
 | 
						|
		{mode: 'advancedBrowseModeAction'},
 | 
						|
		function(action){
 | 
						|
			if(action == '?'){
 | 
						|
				// XXX get the devtools stage...
 | 
						|
				return false }
 | 
						|
			nw.Window.get().showDevTools &&
 | 
						|
				nw.Window.get().showDevTools() }],
 | 
						|
	showInFolder: ['File|Image/Show in $folder',
 | 
						|
		function(image){
 | 
						|
			image = this.images[this.data.getImage(image)]
 | 
						|
 | 
						|
			var base = image.base_path || this.location.path
 | 
						|
			var filename = image.path
 | 
						|
			var path = pathlib.normalize(base + '/' + filename)
 | 
						|
 | 
						|
			nw.Shell.showItemInFolder(path) }],
 | 
						|
 | 
						|
	toggleSplashScreen: ['Interface/',
 | 
						|
		{mode: 'advancedBrowseModeAction'},
 | 
						|
		function(){
 | 
						|
		}],
 | 
						|
})
 | 
						|
 | 
						|
var NWHost = 
 | 
						|
module.NWHost = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'ui-nw-host',
 | 
						|
	exclusive: 'ui-host',
 | 
						|
	depends: [],
 | 
						|
 | 
						|
	actions: NWHostActions,
 | 
						|
 | 
						|
	isApplicable: function(){ return this.runtime.nw },
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
var ElectronHostActions = actions.Actions({
 | 
						|
	// window stuff...
 | 
						|
	// XXX should this be nested in a .window object???
 | 
						|
	// XXX should these be props or methods???
 | 
						|
	get title(){
 | 
						|
		return document.title },
 | 
						|
	set title(value){
 | 
						|
		document.title = value },
 | 
						|
	get size(){
 | 
						|
		return [window.outerWidth, window.outerHeight] },
 | 
						|
	set size(value){
 | 
						|
		value 
 | 
						|
			&& window.resizeTo(...value) },
 | 
						|
	get position(){
 | 
						|
		return [window.screenX, window.screenY] },
 | 
						|
	set position(value){
 | 
						|
		value 
 | 
						|
			&& window.moveTo(...value) },
 | 
						|
 | 
						|
	// XXX need to handle maximize correctly -- see e.js...
 | 
						|
	// XXX do we need .hide(..) here???
 | 
						|
	show: ['- Window/',
 | 
						|
		function(){
 | 
						|
			electron.ipcRenderer.send('show') }],
 | 
						|
	minimize: ['Window/Minimize',
 | 
						|
		function(){
 | 
						|
			electron.ipcRenderer.send('minimize') }],
 | 
						|
	toggleFullScreen: ['Window/Full screen mode',
 | 
						|
		toggler.CSSClassToggler(
 | 
						|
			function(){ return document.body }, 
 | 
						|
			'.full-screen-mode',
 | 
						|
			function(action){
 | 
						|
				var that = this
 | 
						|
 | 
						|
				// get current state...
 | 
						|
				var state = document.appFullScreen ? 'on' : 'off'
 | 
						|
 | 
						|
				// change the state only if the target state is not the same
 | 
						|
				// as the current state...
 | 
						|
				if(state != action){
 | 
						|
					this.ribbons.preventTransitions()
 | 
						|
 | 
						|
					// hide the viewer to hide any animation crimes...
 | 
						|
					this.dom[0].style.visibility = 'hidden'
 | 
						|
 | 
						|
					// NOTE: electrons policy that developers can't trust 
 | 
						|
					// 		their own code making them jump through context 
 | 
						|
					// 		hoops all of the time instead of in the specific 
 | 
						|
					// 		contexts that need isolation is crap...
 | 
						|
					electron.ipcRenderer.send(
 | 
						|
						state == 'on' ?
 | 
						|
							'exitFullScreen'
 | 
						|
							: 'enterFullScreen')
 | 
						|
 | 
						|
					setTimeout(function(){ 
 | 
						|
						that
 | 
						|
							.centerViewer()
 | 
						|
							.focusImage()
 | 
						|
							.ribbons
 | 
						|
								.restoreTransitions()
 | 
						|
						// show viewer after we are done...
 | 
						|
						that.dom[0].style.visibility = '' }, 150) } })],
 | 
						|
 | 
						|
	// XXX should this be a toggler???
 | 
						|
	showDevTools: ['Interface|Development/Show Dev Tools',
 | 
						|
		{mode: 'advancedBrowseModeAction'},
 | 
						|
		function(action){
 | 
						|
			if(action == '?'){
 | 
						|
				return document.appDevTools }
 | 
						|
			electron.ipcRenderer.send('openDevTools') }],
 | 
						|
	// XXX make this portable (osx, linux)...
 | 
						|
	showInFolder: ['File|Image/Show in $folder',
 | 
						|
		function(image){
 | 
						|
			image = this.images[this.data.getImage(image)]
 | 
						|
 | 
						|
			var base = image.base_path || this.location.path
 | 
						|
			var filename = image.path
 | 
						|
			var path = pathlib.normalize(base + '/' + filename)
 | 
						|
 | 
						|
			requirejs('child_process')
 | 
						|
				// XXX this is windows-specific...
 | 
						|
				.exec(`explorer.exe /select,"${ pathlib.normalize(path) }"`)
 | 
						|
				// XXX osx...
 | 
						|
				//.exec('open -R '+JSON.stringify(path))
 | 
						|
		}],
 | 
						|
 | 
						|
	// XXX make this a real toggler...
 | 
						|
	toggleSplashScreen: ['Interface/',
 | 
						|
		{mode: 'advancedBrowseModeAction'},
 | 
						|
		function(action){
 | 
						|
			var splash = document.appSplashScreen
 | 
						|
 | 
						|
			if(action == '?'){
 | 
						|
				return !splash ? 'off' : 'on' }
 | 
						|
 | 
						|
			if(action != 'off' && !splash){
 | 
						|
				electron.ipcRenderer.send('openSplashScreen')
 | 
						|
 | 
						|
			} else if(action != 'on' && splash){
 | 
						|
				electron.ipcRenderer.send('closeSplashScreen') } }],
 | 
						|
 | 
						|
	// XXX should this support resizing???
 | 
						|
	copy: ['Image|Edit/Copy image',
 | 
						|
		core.doc`Copy image
 | 
						|
 | 
						|
			Copy current image (original ref)...
 | 
						|
			.copy()
 | 
						|
 | 
						|
			Copy best matching preview of current image...
 | 
						|
			.copy(size)
 | 
						|
 | 
						|
		NOTE: this will rotate and flip the image according to image metadata...
 | 
						|
		`,
 | 
						|
		function(size){
 | 
						|
			var that = this
 | 
						|
			var url = this.images.getBestPreview(this.current, size, true).url
 | 
						|
			// prep image for copy...
 | 
						|
			img2canvas({
 | 
						|
				...this.images[this.current], 
 | 
						|
				url,
 | 
						|
			}, function(c){
 | 
						|
				electron.clipboard.write({
 | 
						|
					bookmark: that.images.getImageFileName(),
 | 
						|
					// compatibility -- windows paths...
 | 
						|
					text: process.platform.startsWith('win') ?
 | 
						|
						url.replace(/\//g, '\\')
 | 
						|
						: url,
 | 
						|
					image: electron.nativeImage.createFromDataURL(c.toDataURL('image/png')),
 | 
						|
				}) }) }],
 | 
						|
	paste: ['- Image|Edit/Paste image',
 | 
						|
		function(){
 | 
						|
			// XXX
 | 
						|
		}],
 | 
						|
})
 | 
						|
 | 
						|
var ElectronHost = 
 | 
						|
module.ElectronHost = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'ui-electron-host',
 | 
						|
	exclusive: 'ui-host',
 | 
						|
	depends: [],
 | 
						|
 | 
						|
	actions: ElectronHostActions,
 | 
						|
 | 
						|
	isApplicable: function(){ return this.runtime.electron },
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// XXX this needs to trigger only when fullwindow browser mode and not 
 | 
						|
// 		get loaded when in widget mode...
 | 
						|
 | 
						|
var BrowserHostActions = actions.Actions({
 | 
						|
	// window stuff...
 | 
						|
	get title(){
 | 
						|
		return $('title').text() },
 | 
						|
	set title(value){
 | 
						|
		$('title').text(value) },
 | 
						|
 | 
						|
	// XXX this makes document.body fullscreen as expanding .dom breaks 
 | 
						|
	// 		aligning -- this might be a sign that we are not placing 
 | 
						|
	// 		some things relative to .dom...
 | 
						|
	toggleFullScreen: ['Window/Full screen mode',
 | 
						|
		toggler.CSSClassToggler(
 | 
						|
			function(){ return document.body }, 
 | 
						|
			'.full-screen-mode',
 | 
						|
			function(action){
 | 
						|
				var that = this
 | 
						|
 | 
						|
				// get current state...
 | 
						|
				var state = document.fullscreenElement ? 'on' : 'off'
 | 
						|
 | 
						|
				// change the state only if the target state is not the same
 | 
						|
				// as the current state...
 | 
						|
				if(state != action){
 | 
						|
					this.ribbons.preventTransitions()
 | 
						|
 | 
						|
					// hide the viewer to hide any animation crimes...
 | 
						|
					this.dom[0].style.visibility = 'hidden'
 | 
						|
 | 
						|
					state == 'on' ?
 | 
						|
						document.exitFullscreen()
 | 
						|
						// XXX id document.body the right scope here???
 | 
						|
						// 		...this.dom[0] seems to break alignment...
 | 
						|
						//: this.dom[0].requestFullscreen()
 | 
						|
						: document.body.requestFullscreen()
 | 
						|
 | 
						|
					setTimeout(function(){ 
 | 
						|
						that
 | 
						|
							.centerViewer()
 | 
						|
							.focusImage()
 | 
						|
							.ribbons
 | 
						|
								.restoreTransitions()
 | 
						|
						// show viewer after we are done...
 | 
						|
						that.dom[0].style.visibility = '' }, 150)
 | 
						|
				}
 | 
						|
			})],
 | 
						|
 | 
						|
 | 
						|
	// XXX these do not work from file://
 | 
						|
	// XXX would be nice to add a path/title here...
 | 
						|
	// XXX should this support resizing???
 | 
						|
	copy: ['Image|Edit/Copy image',
 | 
						|
		core.doc`Copy image
 | 
						|
 | 
						|
		NOTE: this must be called from within an event handler...
 | 
						|
		NOTE: this will not work with file:// paths...
 | 
						|
		`,
 | 
						|
		function(size){
 | 
						|
			img2canvas({
 | 
						|
				url: this.images.getBestPreview(this.current, size, true).url ,
 | 
						|
				...this.images[this.current] 
 | 
						|
			}, function(c){
 | 
						|
				c.toBlob(function(blob){
 | 
						|
					// copy...
 | 
						|
					// XXX would be nice to add a path/title here...
 | 
						|
					navigator.clipboard.write([
 | 
						|
						new ClipboardItem({
 | 
						|
							[blob.type]: blob,
 | 
						|
						}) ]) }, 
 | 
						|
					"image/png") })
 | 
						|
		}],
 | 
						|
	paste: ['- Image|Edit/Paste image',
 | 
						|
		function(){
 | 
						|
			// XXX
 | 
						|
		}],
 | 
						|
})
 | 
						|
 | 
						|
// NOTE: keep this defined last as a fallback...
 | 
						|
var BrowserHost = 
 | 
						|
module.BrowserHost = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'ui-browser-host',
 | 
						|
	exclusive: 'ui-host',
 | 
						|
	depends: [
 | 
						|
		// XXX remove id buttons control moves elsewhere...
 | 
						|
		'ui-buttons',
 | 
						|
	],
 | 
						|
 | 
						|
	actions: BrowserHostActions,
 | 
						|
 | 
						|
	isApplicable: function(){ return !this.runtime.widget },
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
var PortableAppControl = 
 | 
						|
module.PortableAppControl = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'ui-portable-app-control',
 | 
						|
	depends: [
 | 
						|
		'ui',
 | 
						|
		'ui-host',
 | 
						|
	],
 | 
						|
 | 
						|
	isApplicable: function(){ return this.runtime.browser },
 | 
						|
 | 
						|
	config: {
 | 
						|
		//'window-title': 'ImageGrid.Viewer (${VERSION}): ${FILENAME}',
 | 
						|
		'window-title': '${FILENAME} - ImageGrid.Viewer (${VERSION})',
 | 
						|
 | 
						|
	},
 | 
						|
 | 
						|
	handlers: [
 | 
						|
		// update window title...
 | 
						|
		// XXX make this generic...
 | 
						|
		['focusImage clear',
 | 
						|
			function(){
 | 
						|
				if(this.images){
 | 
						|
					var img = this.images[this.current]
 | 
						|
					this.title = (this.config['window-title'] 
 | 
						|
							|| 'ImageGrid.Viewer (${VERSION}): ${FILENAME}')
 | 
						|
						// XXX get this from the viewer...
 | 
						|
						.replace('${VERSION}', this.version || 'gen4')
 | 
						|
						.replace('${FILENAME}', 
 | 
						|
							img ? 
 | 
						|
									(img.name 
 | 
						|
										|| (img.path && img.path.replace(/\.[\\\/]/, ''))
 | 
						|
										|| this.current
 | 
						|
										|| '')
 | 
						|
								: (this.current || ''))
 | 
						|
						.replace('${PATH}', 
 | 
						|
							(img && img.path) ?
 | 
						|
									(img.base_path || '.') 
 | 
						|
										+'/'+ img.path.replace(/\.[\\\/]/, '')
 | 
						|
								: '')
 | 
						|
						/*
 | 
						|
						.replace('${DIR}', 
 | 
						|
							pathlib.dirname((img.base_path || '.') 
 | 
						|
								+'/'+ img.path.replace(/\.[\\\/]/, '')))
 | 
						|
						*/
 | 
						|
						// XXX add ...
 | 
						|
				}
 | 
						|
			}],
 | 
						|
	],
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
// XXX should we create and show the window here???
 | 
						|
// 		...if yes, then we'll need to start the ui features later as 
 | 
						|
// 		they need the dom ready...
 | 
						|
var WindowedAppControlActions = actions.Actions({
 | 
						|
	config: {
 | 
						|
		// Window state:
 | 
						|
		// 'window': {
 | 
						|
		// 		width: <number>,
 | 
						|
		// 		height: <number>,
 | 
						|
		// 		x: <number>,
 | 
						|
		// 		y: <number>,
 | 
						|
		//
 | 
						|
		// 		fullscreen: <bool>,
 | 
						|
		//
 | 
						|
		// 		devtools: <bool>,
 | 
						|
		// },
 | 
						|
 | 
						|
		'window-delay-initial-display': 500,
 | 
						|
		'splash-screen-delay-hide': 500,
 | 
						|
 | 
						|
		'show-splash-screen': 'on',
 | 
						|
	},
 | 
						|
 | 
						|
	close: ['File|Window/Close viewer',
 | 
						|
		function(){ 
 | 
						|
			this.stop()
 | 
						|
			window.close() 
 | 
						|
		}],
 | 
						|
 | 
						|
	storeWindowGeometry: ['- Window/Store window state',
 | 
						|
		function(){
 | 
						|
			// store window parameters (size, state)...
 | 
						|
			//var win = nw.Window.get()
 | 
						|
			var size = this.size
 | 
						|
			var position = this.position
 | 
						|
 | 
						|
			// fullscreen...
 | 
						|
			// ...avoid overwriting size...
 | 
						|
			if(this.toggleFullScreen('?') == 'on'){
 | 
						|
				// NOTE: this needs to be rewritten to correctly get stored
 | 
						|
				// 		in the config store, especially if a default state
 | 
						|
				// 		is defined...
 | 
						|
				var cfg = this.config.window = this.config.window || {}
 | 
						|
				cfg.fullscreen = true
 | 
						|
				cfg.devtools = this.showDevTools
 | 
						|
					&& this.showDevTools('?')
 | 
						|
 | 
						|
			} else {
 | 
						|
				this.config.window = {
 | 
						|
					width: size[0],
 | 
						|
					height: size[1],
 | 
						|
 | 
						|
					x: position[0],
 | 
						|
					y: position[1],
 | 
						|
 | 
						|
					fullscreen: false,
 | 
						|
					devtools: this.showDevTools 
 | 
						|
						&& this.showDevTools('?'),
 | 
						|
				} } }],
 | 
						|
	restoreWindowGeometry: ['- Window/Restore window state',
 | 
						|
		function(){
 | 
						|
			var that = this
 | 
						|
			var cfg = this.config.window || {}
 | 
						|
 | 
						|
			if(cfg.fullscreen){
 | 
						|
				that.toggleFullScreen('on')
 | 
						|
 | 
						|
			} else {
 | 
						|
				var w = cfg.width || 800
 | 
						|
				var h = cfg.height || 600 
 | 
						|
				var x = cfg.x || (screen.width - w)/2
 | 
						|
				var y = cfg.y || (screen.height - h)/2
 | 
						|
 | 
						|
				this.position = [x, y]
 | 
						|
				this.size = [w, h] } }],
 | 
						|
 | 
						|
	toggleSplashScreenShowing: ['Interface/Splash screen on start',
 | 
						|
		{mode: 'advancedBrowseModeAction'},
 | 
						|
		core.makeConfigToggler('show-splash-screen', 
 | 
						|
			['on', 'off'],
 | 
						|
			function(action){
 | 
						|
				if(action == 'on'){
 | 
						|
					delete localStorage.disableSplashScreen
 | 
						|
 | 
						|
				} else if(action == 'off'){
 | 
						|
					localStorage.disableSplashScreen = true
 | 
						|
				}
 | 
						|
			})],
 | 
						|
})
 | 
						|
 | 
						|
var WindowedAppControl = 
 | 
						|
module.WindowedAppControl = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'ui-windowed-app-control',
 | 
						|
	depends: [
 | 
						|
		'ui',
 | 
						|
		'ui-host',
 | 
						|
	],
 | 
						|
 | 
						|
	actions: WindowedAppControlActions,
 | 
						|
 | 
						|
	// XXX BUG: when running in electron:
 | 
						|
	// 			- loading this breaks the other buttons (menu, collections, ...)
 | 
						|
	// 			- .toggleFullScreen(..) works in an odd manner -- ok when 
 | 
						|
	// 				explicitly called or button pushed but in opposite manner 
 | 
						|
	// 				when F11 (FIXED?)
 | 
						|
	// 			- ready protocol breaks -- need to call .ready() to unstall 
 | 
						|
	// 				the viewer
 | 
						|
	// 		...does this all have anything to do with double init???
 | 
						|
	isApplicable: function(){ return this.runtime.desktop },
 | 
						|
	//isApplicable: function(){ return this.runtime.desktop && !this.runtime.electron },
 | 
						|
 | 
						|
	// XXX show main window...
 | 
						|
	handlers: [
 | 
						|
		['start.pre',
 | 
						|
			function(){ 
 | 
						|
				// we are going to declare ready ourselves...
 | 
						|
				this.requestReadyAnnounce('ui-windowed-app-control') }],
 | 
						|
		// XXX should we create and show the window here???
 | 
						|
		['start',
 | 
						|
			function(){ 
 | 
						|
				var that = this
 | 
						|
				var cfg = this.config.window
 | 
						|
 | 
						|
				// set the initial non-fullscreen window geometry...
 | 
						|
				// NOTE: this will avoid overwriting the initial geometry
 | 
						|
				// 		with the values set in e.js if .fullscreen is set...
 | 
						|
				// NOTE: this will also set the size to which the OS will 
 | 
						|
				// 		resize the window in state change...
 | 
						|
				if(cfg){
 | 
						|
					cfg.devtools
 | 
						|
						&& this.showDevTools() 
 | 
						|
 | 
						|
					var W = screen.width
 | 
						|
					var H = screen.height
 | 
						|
					var w = cfg.width || Math.max(0.8 * W, 600)
 | 
						|
					var h = cfg.height || Math.max(0.8 * H, 400)
 | 
						|
					var x = cfg.x || (W - w)/2
 | 
						|
					var y = cfg.y || (H - h)/2
 | 
						|
 | 
						|
					this.position = [x, y]
 | 
						|
					this.size = [w, h] }
 | 
						|
 | 
						|
				// restore actual window state... 
 | 
						|
				this.restoreWindowGeometry() 
 | 
						|
 | 
						|
				// declare we are ready when DOM is ready...
 | 
						|
				$(function(){ 
 | 
						|
					that.declareReady('ui-windowed-app-control') }) }],
 | 
						|
 | 
						|
		// show window + hide splash screen...
 | 
						|
		['ready',
 | 
						|
			function(){ 
 | 
						|
				var that = this
 | 
						|
				// NOTE: we delay this to enable the browser time to render
 | 
						|
				// 		things before we show them to the user...
 | 
						|
				setTimeout(function(){
 | 
						|
					// show window...
 | 
						|
					that.show()
 | 
						|
 | 
						|
					// hide splash screen...
 | 
						|
					setTimeout(function(){
 | 
						|
						that.toggleSplashScreen('off')
 | 
						|
					}, (that.config['splash-screen-delay-hide'] || 0)) 
 | 
						|
				}, this.config['window-delay-initial-display'] || 0) }],
 | 
						|
 | 
						|
		[[
 | 
						|
			'stop.pre',
 | 
						|
			'toggleFullScreen.pre',
 | 
						|
			'toggleFullScreen',
 | 
						|
		],
 | 
						|
			function(){ this.storeWindowGeometry() }],
 | 
						|
	],
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
var AppControl = 
 | 
						|
module.AppControl = core.ImageGridFeatures.Feature('app-control', [
 | 
						|
	'ui-windowed-app-control',
 | 
						|
	'ui-portable-app-control',
 | 
						|
])
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**********************************************************************
 | 
						|
* vim:set ts=4 sw=4 :                               */ return module })
 |