mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 10:20:08 +00:00
reworked long action abort... the results still need some tweaking...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
da63da2f2c
commit
baa1b06dce
@ -1575,7 +1575,7 @@ module.CropActions = actions.Actions({
|
||||
return this.crop(list.slice(list.indexOf(image)), flatten) }],
|
||||
|
||||
// XXX not sure if we actually need this...
|
||||
cropFlatten: ['Crop/$Flatten',
|
||||
cropFlatten: ['Crop|Ribbon/Crop $flatten',
|
||||
{mode: function(){
|
||||
return this.data.ribbon_order.length <= 1 && 'disabled' }},
|
||||
function(list){ this.data.length > 0 && this.crop(list, true) }],
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
* - introspection
|
||||
* - lifecycle
|
||||
* base life-cycle events (start/stop/..)
|
||||
* base abort api
|
||||
* - serialization
|
||||
* base methods to handle loading, serialization and cloning...
|
||||
* - cache
|
||||
@ -226,6 +227,8 @@ var LoggerActions = actions.Actions({
|
||||
Logger: object.Constructor('BaseLogger', {
|
||||
doc: `Logger object constructor...`,
|
||||
|
||||
quiet: false,
|
||||
|
||||
__context: null,
|
||||
get context(){
|
||||
return this.__context || this.root.__context },
|
||||
@ -324,11 +327,20 @@ var LoggerActions = actions.Actions({
|
||||
|
||||
|
||||
// main API...
|
||||
//
|
||||
// .push(str, ...)
|
||||
//
|
||||
// .push(str, ..., attrs)
|
||||
//
|
||||
push: function(...msg){
|
||||
attrs = typeof(msg.last()) != typeof('str') ?
|
||||
msg.pop()
|
||||
: {}
|
||||
return msg.length == 0 ?
|
||||
this
|
||||
: Object.assign(
|
||||
this.constructor(),
|
||||
attrs,
|
||||
{
|
||||
root: this.root,
|
||||
path: this.path.concat(msg),
|
||||
@ -359,7 +371,7 @@ var LoggerActions = actions.Actions({
|
||||
// call context log handler...
|
||||
this.context
|
||||
&& this.context.handleLogItem
|
||||
&& this.context.handleLogItem(this.path, status, ...rest)
|
||||
&& this.context.handleLogItem(this, this.path, status, ...rest)
|
||||
return this },
|
||||
|
||||
|
||||
@ -379,8 +391,9 @@ var LoggerActions = actions.Actions({
|
||||
|
||||
// XXX move this to console-logger???
|
||||
handleLogItem: ['- System/',
|
||||
function(path, status, ...rest){
|
||||
console.log(
|
||||
function(logger, path, status, ...rest){
|
||||
logger.quiet
|
||||
|| console.log(
|
||||
path.join(': ') + (path.length > 0 ? ': ' : '')
|
||||
+ status
|
||||
+ (rest.length > 1 ?
|
||||
@ -583,6 +596,82 @@ module.Introspection = ImageGridFeatures.Feature({
|
||||
//---------------------------------------------------------------------
|
||||
// System life-cycle...
|
||||
|
||||
// XXX docs...
|
||||
//
|
||||
// action: ['Path/To/Action',
|
||||
// abortablePromise('abort-id', function(abort, ...args){
|
||||
//
|
||||
// abort.cleanup(function(reason, res){
|
||||
// if(reason == 'done'){
|
||||
// // ...
|
||||
// }
|
||||
// if(reason == 'aborted'){
|
||||
// // ...
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// return new Promise(function(resolve, reject){
|
||||
// // ...
|
||||
//
|
||||
// if(abort.isAborted){
|
||||
// // handle abort...
|
||||
// }
|
||||
//
|
||||
// // ...
|
||||
// }) })],
|
||||
//
|
||||
//
|
||||
// NOTE: if the returned promise is not resolved .cleanup(..) will not
|
||||
// be called even if the appropriate .abort(..) as called...
|
||||
var abortablePromise =
|
||||
module.abortablePromise =
|
||||
function(title, func){
|
||||
return Object.assign(
|
||||
function(...args){
|
||||
var that = this
|
||||
|
||||
var abort = object.mixinFlat(
|
||||
this.abortable(title, function(){
|
||||
that.clearAbortable(title, abort)
|
||||
return abort }),
|
||||
{
|
||||
get isAborted(){
|
||||
return !((that.__abortable || new Map())
|
||||
.get(title) || new Set())
|
||||
.has(this) },
|
||||
|
||||
__cleanup: null,
|
||||
cleanup: function(func){
|
||||
var args = [...arguments]
|
||||
var reason = this.isAborted ?
|
||||
'aborted'
|
||||
: 'done'
|
||||
typeof(func) == 'function' ?
|
||||
// register handler...
|
||||
(this.__cleanup = this.__cleanup
|
||||
|| new Set()).add(func)
|
||||
// call cleanup handlers...
|
||||
: [...(this.__cleanup || [])]
|
||||
.forEach(function(f){
|
||||
f.call(that, reason, ...args) })
|
||||
return this },
|
||||
})
|
||||
|
||||
return func.call(this, abort, ...args)
|
||||
.then(function(res){
|
||||
abort.cleanup(res)()
|
||||
return res })
|
||||
.catch(function(res){
|
||||
abort.cleanup(res)() }) },
|
||||
{
|
||||
toString: function(){
|
||||
return `core.abortablePromise('${ title }', \n${ func.toString() })` },
|
||||
}) }
|
||||
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// XXX should his have state???
|
||||
// ...if so, should this be a toggler???
|
||||
var LifeCycleActions = actions.Actions({
|
||||
@ -966,6 +1055,98 @@ var LifeCycleActions = actions.Actions({
|
||||
.stop()
|
||||
.clear()
|
||||
.start() }],
|
||||
|
||||
|
||||
// Abortable...
|
||||
//
|
||||
// Format:
|
||||
// Map({
|
||||
// title: Set([ func, ... ]),
|
||||
// ...
|
||||
// })
|
||||
//
|
||||
__abortable: null,
|
||||
|
||||
abortable: ['- System/Register abort handler',
|
||||
doc`Register abortable action
|
||||
|
||||
.abortable(title, func)
|
||||
-> func
|
||||
|
||||
`,
|
||||
function(title, callback){
|
||||
// reserved titles...
|
||||
if(title == 'all' || title == '*'){
|
||||
throw new Error('.abortable(..): can not set reserved title: "'+ title +'".') }
|
||||
|
||||
var abortable = this.__abortable = this.__abortable || new Map()
|
||||
var set = abortable.get(title) || new Set()
|
||||
abortable.set(title, set)
|
||||
set.add(callback)
|
||||
|
||||
return actions.ASIS(callback) }],
|
||||
clearAbortable: ['- System/Clear abort handler(s)',
|
||||
doc`Clear abort handler(s)
|
||||
|
||||
Clear abort handler...
|
||||
.clearAbortable(title, callback)
|
||||
|
||||
Clear all abort handlers for title...
|
||||
.clearAbortable(title)
|
||||
.clearAbortable(title, 'all')
|
||||
|
||||
Clear all abort handlers...
|
||||
.clearAbortable('all')
|
||||
|
||||
`,
|
||||
function(title, callback){
|
||||
callback = callback || '*'
|
||||
|
||||
// clear all...
|
||||
if(title == '*' || title == 'all'){
|
||||
delete this.__abortable }
|
||||
|
||||
var set = ((this.__abortable || new Map()).get(title) || new Set())
|
||||
// clear specific handler...
|
||||
callback != '*'
|
||||
&& callback != 'all'
|
||||
&& set.delete(callback)
|
||||
// cleanup / clear title...
|
||||
;(set.size == 0
|
||||
|| callback == '*'
|
||||
|| callback == 'all')
|
||||
&& (this.__abortable || new Set()).delete(title)
|
||||
// cleanup...
|
||||
this.__abortable
|
||||
&& this.__abortable.size == 0
|
||||
&& (delete this.__abortable) }],
|
||||
abort: ['- System/Run abort handler(s)',
|
||||
doc`
|
||||
|
||||
.abort(title)
|
||||
.abort([title, .. ])
|
||||
|
||||
.abort('all')
|
||||
|
||||
`,
|
||||
function(title){
|
||||
title = title == '*' || title == 'all' ?
|
||||
[...(this.__abortable || new Map()).keys()]
|
||||
: title instanceof Array ?
|
||||
title
|
||||
: [title]
|
||||
|
||||
this.__abortable
|
||||
&& title
|
||||
.forEach(function(title){
|
||||
[...(this.__abortable || new Map()).get(title) || []]
|
||||
.forEach(function(f){ f() })
|
||||
this.__abortable
|
||||
&& this.__abortable.delete(title) }.bind(this))
|
||||
// cleanup...
|
||||
this.__abortable
|
||||
&& this.__abortable.size == 0
|
||||
&& (delete this.__abortable) }],
|
||||
})
|
||||
|
||||
var LifeCycle =
|
||||
|
||||
@ -106,7 +106,18 @@ var MetadataReaderActions = actions.Actions({
|
||||
// XXX this uses .markChanged(..) form filesystem.FileSystemWriter
|
||||
// feature, but technically does not depend on it...
|
||||
// XXX should we store metadata in an image (current) or in fs???
|
||||
// XXX should this set .orientation / .flipped if they are not set???
|
||||
readMetadata: ['- Image/Get metadata data',
|
||||
core.doc`
|
||||
|
||||
This will overwrite/update if:
|
||||
- image .metadata is not set
|
||||
- image .metadata.ImageGridMetadata is not 'full'
|
||||
- force is true
|
||||
|
||||
|
||||
NOTE: also see: .cacheMetadata(..)
|
||||
`,
|
||||
function(image, force){
|
||||
var that = this
|
||||
|
||||
@ -169,6 +180,7 @@ var MetadataReaderActions = actions.Actions({
|
||||
|
||||
resolve(data) }) }) }) }],
|
||||
|
||||
// XXX make this abortable...
|
||||
// XXX STUB: add support for this to .readMetadata(..)
|
||||
readAllMetadata: ['File/Read all metadata',
|
||||
function(){
|
||||
@ -254,6 +266,12 @@ var MetadataReaderActions = actions.Actions({
|
||||
&& this.markChanged('data')
|
||||
mode == 'crop'
|
||||
&& this.crop(data) }],
|
||||
|
||||
// shorthands...
|
||||
cropRatingsAsRibbons: ['Ribbon|Crop/Crop ratings to ribbons',
|
||||
'ratingToRibbons: "crop"'],
|
||||
splitRatingsAsRibbons: ['Ribbon/Split ratings to ribbons (in-place)',
|
||||
'ratingToRibbons: "in-place"'],
|
||||
})
|
||||
|
||||
var MetadataReader =
|
||||
@ -705,13 +723,6 @@ var MetadataUIActions = actions.Actions({
|
||||
&& make.Separator() })
|
||||
.forEach(function(e){
|
||||
make(...e) }) })],
|
||||
|
||||
|
||||
// shorthands...
|
||||
cropRatingsAsRibbons: ['Ribbon|Crop/Split ratings to ribbons (crop)',
|
||||
'ratingToRibbons: "crop"'],
|
||||
splitRatingsAsRibbons: ['Ribbon/Split ratings to ribbons (in-place)',
|
||||
'ratingToRibbons: "in-place"'],
|
||||
})
|
||||
|
||||
var MetadataUI =
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
(function(require){ var module={} // make module AMD/node compatible...
|
||||
/*********************************************************************/
|
||||
|
||||
var array = require('lib/types/Array')
|
||||
|
||||
var actions = require('lib/actions')
|
||||
var features = require('lib/features')
|
||||
|
||||
@ -245,13 +247,22 @@ var SharpActions = actions.Actions({
|
||||
NOTE: all options are optional.
|
||||
NOTE: this will not overwrite existing images.
|
||||
`,
|
||||
function(images, size, path, options={}){
|
||||
core.abortablePromise('makeResizedImage', function(abort, images, size, path, options={}){
|
||||
var that = this
|
||||
|
||||
// sanity check...
|
||||
if(arguments.length < 3){
|
||||
throw new Error('.makeResizedImage(..): '
|
||||
+'need at least images, size and path.') }
|
||||
|
||||
var CHUNK_SIZE = 4
|
||||
|
||||
abort.cleanup(function(reason, res){
|
||||
logger
|
||||
&& logger.emit('close')
|
||||
&& reason == 'aborted'
|
||||
&& logger.emit(res) })
|
||||
|
||||
// get/normalize images...
|
||||
//images = images || this.current
|
||||
images = images
|
||||
@ -304,7 +315,7 @@ var SharpActions = actions.Actions({
|
||||
logger = logger !== false ?
|
||||
(logger || this.logger)
|
||||
: false
|
||||
logger = logger && logger.push('Resize')
|
||||
logger = logger && logger.push('Resize', {onclose: abort})
|
||||
|
||||
// backup...
|
||||
// XXX make backup name pattern configurable...
|
||||
@ -314,8 +325,11 @@ var SharpActions = actions.Actions({
|
||||
i++ }
|
||||
return `${to}.${timestamp}.bak`+ (i || '') }
|
||||
|
||||
return Promise.all(images
|
||||
.map(function(gid){
|
||||
return images
|
||||
.mapChunks(CHUNK_SIZE, function(gid){
|
||||
if(abort.isAborted){
|
||||
throw array.StopIteration('aborted') }
|
||||
|
||||
// skip non-images...
|
||||
if(!['image', null, undefined]
|
||||
.includes(that.images[gid].type)){
|
||||
@ -342,7 +356,7 @@ var SharpActions = actions.Actions({
|
||||
&& Math.max(m.width, m.height) < size)
|
||||
|| (fit == 'outside'
|
||||
&& Math.min(m.width, m.height) < size)){
|
||||
skipping(gid)
|
||||
logger && logger.emit('skipping', gid)
|
||||
return }
|
||||
// continue...
|
||||
return img })
|
||||
@ -362,7 +376,7 @@ var SharpActions = actions.Actions({
|
||||
fse.removeSync(to)
|
||||
// skip...
|
||||
} else {
|
||||
skipping(gid)
|
||||
logger && logger.emit('skipping', gid)
|
||||
return } }
|
||||
|
||||
// write...
|
||||
@ -396,11 +410,11 @@ var SharpActions = actions.Actions({
|
||||
.then(function(){
|
||||
logger
|
||||
&& logger.emit('done', to)
|
||||
return img }) }) }) })) }],
|
||||
return img }) }) }) }) })],
|
||||
|
||||
// XXX test against .makePreviews(..) for speed...
|
||||
// XXX this does not update image.base_path -- is this correct???
|
||||
// XXX do we need to be able to run this in a worker???
|
||||
// XXX add support for offloading the processing to a thread/worker...
|
||||
// XXX should we use task.Queue()???
|
||||
makePreviews: ['Sharp|File/Make image $previews',
|
||||
core.doc`Make image previews
|
||||
|
||||
@ -428,16 +442,23 @@ var SharpActions = actions.Actions({
|
||||
NOTE: if base_path is given .images will not be updated with new
|
||||
preview paths...
|
||||
`,
|
||||
function(images, sizes, base_path, logger){
|
||||
core.abortablePromise('makePreviews', function(abort, images, sizes, base_path, logger){
|
||||
var that = this
|
||||
|
||||
var CHUNK_SIZE = 4
|
||||
|
||||
abort.cleanup(function(reason, res){
|
||||
logger
|
||||
&& logger.emit('close')
|
||||
&& reason == 'aborted'
|
||||
&& logger.emit(res) })
|
||||
|
||||
var logger_mode = this.config['preview-progress-mode'] || 'gids'
|
||||
logger = logger !== false ?
|
||||
(logger || this.logger)
|
||||
: false
|
||||
var gid_logger = logger && logger.push('Images')
|
||||
logger = logger && logger.push('Previews')
|
||||
|
||||
var gid_logger = logger && logger.push('Images', {onclose: abort})
|
||||
logger = logger && logger.push('Previews', {onclose: abort})
|
||||
|
||||
// get/normalize images...
|
||||
//images = images || this.current
|
||||
@ -473,8 +494,11 @@ var SharpActions = actions.Actions({
|
||||
var path_tpl = that.config['preview-path-template']
|
||||
.replace(/\$INDEX|\$\{INDEX\}/g, that.config['index-dir'] || '.ImageGrid')
|
||||
|
||||
return Promise.all(images
|
||||
.map(function(gid){
|
||||
return images
|
||||
.mapChunks(CHUNK_SIZE, function(gid){
|
||||
if(abort.isAborted){
|
||||
throw array.StopIteration('aborted') }
|
||||
|
||||
var img = that.images[gid]
|
||||
var base = base_path
|
||||
|| img.base_path
|
||||
@ -484,6 +508,9 @@ var SharpActions = actions.Actions({
|
||||
|
||||
return sizes
|
||||
.map(function(size, i){
|
||||
if(abort.isAborted){
|
||||
throw array.StopIteration('aborted') }
|
||||
|
||||
var name = path = path_tpl
|
||||
.replace(/\$RESOLUTION|\$\{RESOLUTION\}/g, parseInt(size))
|
||||
.replace(/\$GID|\$\{GID\}/g, gid)
|
||||
@ -512,14 +539,12 @@ var SharpActions = actions.Actions({
|
||||
&& that.markChanged('images', [gid]) }
|
||||
|
||||
return [gid, size, name] }) }) })
|
||||
.flat()) }],
|
||||
|
||||
.then(function(res){
|
||||
return res.flat() }) })],
|
||||
|
||||
// XXX add support for offloading the processing to a thread/worker...
|
||||
// XXX would be nice to be able to abort this...
|
||||
// ...and/or have a generic abort protocol triggered when loading...
|
||||
// ...use task queue???
|
||||
// XXX make each section optional...
|
||||
// XXX should we use task.Queue()???
|
||||
__cache_metadata_reading: null,
|
||||
cacheMetadata: ['- Sharp|Image/',
|
||||
core.doc`Cache metadata
|
||||
|
||||
@ -550,21 +575,41 @@ var SharpActions = actions.Actions({
|
||||
-> promise([ gid | null, .. ])
|
||||
|
||||
|
||||
This quickly reads/caches essential (.orientation and .flipped)
|
||||
metadata and some non-essential but already there values.
|
||||
|
||||
|
||||
This will overwrite/update if:
|
||||
- .orientation and .flipped iff image .orientation AND .flipped
|
||||
are unset or force is true
|
||||
- metadata if image .metadata is not set or
|
||||
.metadata.ImageGridMetadata is not set
|
||||
- all metadata if force is set to true
|
||||
|
||||
|
||||
NOTE: this will effectively update metadata format to the new spec...
|
||||
NOTE: for info on full metadata format see: .readMetadata(..)
|
||||
`,
|
||||
function(images, logger){
|
||||
core.abortablePromise('cacheMetadata', function(abort, images, logger){
|
||||
var that = this
|
||||
|
||||
var CHUNK_SIZE = 4
|
||||
|
||||
abort.cleanup(function(reason, res){
|
||||
logger
|
||||
&& logger.emit('close')
|
||||
&& reason == 'aborted'
|
||||
&& logger.emit(res)
|
||||
delete that.__cache_metadata_reading })
|
||||
|
||||
// handle logging and processing list...
|
||||
// NOTE: these will maintain .__metadata_reading helping
|
||||
// NOTE: these will maintain .__cache_metadata_reading helping
|
||||
// avoid processing an image more than once at the same
|
||||
// time...
|
||||
var done = function(gid, msg){
|
||||
logger && logger.emit(msg || 'done', gid)
|
||||
if(that.__metadata_reading){
|
||||
that.__metadata_reading.delete(gid)
|
||||
if(that.__metadata_reading.size == 0){
|
||||
delete that.__metadata_reading } }
|
||||
if(that.__cache_metadata_reading){
|
||||
that.__cache_metadata_reading.delete(gid) }
|
||||
return gid }
|
||||
var skipping = function(gid){
|
||||
return done(gid, 'skipping') }
|
||||
@ -601,13 +646,14 @@ var SharpActions = actions.Actions({
|
||||
images
|
||||
: [images])
|
||||
.filter(function(gid){
|
||||
return !that.__metadata_reading
|
||||
|| !that.__metadata_reading.has(gid) })
|
||||
return !that.__cache_metadata_reading
|
||||
|| !that.__cache_metadata_reading.has(gid) })
|
||||
|
||||
logger = logger !== false ?
|
||||
(logger || this.logger)
|
||||
: false
|
||||
logger = logger && logger.push('Caching image metadata')
|
||||
logger = logger
|
||||
&& logger.push('Caching image metadata', {onclose: abort})
|
||||
logger && logger.emit('queued', images)
|
||||
|
||||
/*/ XXX set this to tmp for .location.load =='loadImages'
|
||||
@ -624,11 +670,15 @@ var SharpActions = actions.Actions({
|
||||
//*/
|
||||
|
||||
return images
|
||||
.mapChunks(function(gid){
|
||||
.mapChunks(CHUNK_SIZE, function(gid){
|
||||
// abort...
|
||||
if(abort.isAborted){
|
||||
throw array.StopIteration('aborted') }
|
||||
|
||||
var img = cached_images[gid]
|
||||
var path = img && that.getImagePath(gid)
|
||||
;(that.__metadata_reading =
|
||||
that.__metadata_reading || new Set())
|
||||
;(that.__cache_metadata_reading =
|
||||
that.__cache_metadata_reading || new Set())
|
||||
.add(gid)
|
||||
|
||||
// skip...
|
||||
@ -702,11 +752,19 @@ var SharpActions = actions.Actions({
|
||||
that.ribbons
|
||||
&& that.ribbons.updateImage(gid)
|
||||
|
||||
return done(gid) }) }) }],
|
||||
return done(gid) }) }) })],
|
||||
cacheAllMetadata: ['- Sharp|Image/',
|
||||
core.doc`Cache all metadata
|
||||
NOTE: this is a shorthand to .cacheMetadata('all', ..)`,
|
||||
'cacheMetadata: "all" ...'],
|
||||
|
||||
|
||||
// shorthands...
|
||||
// XXX do we need these???
|
||||
abortMakePreviews: ['- Sharp/',
|
||||
'abort: "makePreviews"'],
|
||||
abortCacheMetadata: ['- Sharp/',
|
||||
'abort: "cacheMetadata"'],
|
||||
})
|
||||
|
||||
|
||||
@ -727,9 +785,19 @@ module.Sharp = core.ImageGridFeatures.Feature({
|
||||
isApplicable: function(){ return !!sharp },
|
||||
|
||||
handlers: [
|
||||
/* XXX this needs to be run in the background...
|
||||
// XXX
|
||||
['load.pre',
|
||||
function(){
|
||||
this.abort([
|
||||
'makeResizedImage',
|
||||
'makePreviews',
|
||||
'cacheMetadata',
|
||||
]) }],
|
||||
|
||||
//* XXX this needs to be run in the background...
|
||||
// XXX this is best done in a thread + needs to be abortable (on .load(..))...
|
||||
['loadImages',
|
||||
[['loadImages',
|
||||
'loadNewImages'],
|
||||
function(){
|
||||
this.cacheMetadata('all') }],
|
||||
//*/
|
||||
|
||||
@ -24,28 +24,6 @@ var ProgressActions = actions.Actions({
|
||||
'progress-update-min': 200,
|
||||
},
|
||||
|
||||
// Progress bar widget...
|
||||
//
|
||||
// Create progress bar...
|
||||
// .showProgress('text')
|
||||
//
|
||||
// Update progress bar (value, max, msg)...
|
||||
// .showProgress('text', 0, 10)
|
||||
// .showProgress('text', 10, 50, 'message')
|
||||
//
|
||||
// Update progress bar value (has no effect if max is not set)...
|
||||
// .showProgress('text', 10)
|
||||
//
|
||||
// Close progress bar...
|
||||
// .showProgress('text', 'close')
|
||||
//
|
||||
// Relative progress modification...
|
||||
// .showProgress('text', '+1')
|
||||
// .showProgress('text', '+0', '+1')
|
||||
//
|
||||
// .showProgress(logger)
|
||||
//
|
||||
//
|
||||
// XXX add message to be shown...
|
||||
// XXX should we report errors and stoppages??? (error state??)
|
||||
// XXX multiple containers...
|
||||
@ -53,17 +31,50 @@ var ProgressActions = actions.Actions({
|
||||
// XXX revise styles...
|
||||
__progress_cache: null,
|
||||
showProgress: ['- Interface/Show progress bar...',
|
||||
function(text, value, max){
|
||||
core.doc`Progress bar widget...
|
||||
|
||||
Create progress bar...
|
||||
.showProgress('text')
|
||||
|
||||
Update progress bar (value, max, msg)...
|
||||
.showProgress('text', 0, 10)
|
||||
.showProgress('text', 10, 50, 'message')
|
||||
|
||||
Update progress bar value (has no effect if max is not set)...
|
||||
.showProgress('text', 10)
|
||||
|
||||
Close progress bar...
|
||||
.showProgress('text', 'close')
|
||||
|
||||
Relative progress modification...
|
||||
.showProgress('text', '+1')
|
||||
.showProgress('text', '+0', '+1')
|
||||
|
||||
.showProgress(logger)
|
||||
|
||||
|
||||
`,
|
||||
function(text, value, max, attrs){
|
||||
var that = this
|
||||
var viewer = this.dom
|
||||
|
||||
// get attrs...
|
||||
var args = [...arguments]
|
||||
attrs = args.slice(1).last() instanceof Object ?
|
||||
args.pop()
|
||||
: null
|
||||
;[text, value, max] = args
|
||||
|
||||
var msg = text instanceof Array ? text.slice(1).join(': ') : null
|
||||
text = text instanceof Array ? text[0] : text
|
||||
|
||||
// make sure we do not update too often...
|
||||
if(value != 'close'){
|
||||
var cache = (this.__progress_cache = this.__progress_cache || {})
|
||||
cache = cache[text] = cache[text] || {}
|
||||
cache = cache[text] =
|
||||
Object.assign(
|
||||
cache[text] || {},
|
||||
attrs || {})
|
||||
|
||||
var updateValue = function(name, value){
|
||||
var v = cache[name] || 0
|
||||
@ -115,7 +126,11 @@ var ProgressActions = actions.Actions({
|
||||
.text(text)
|
||||
// close button...
|
||||
.append($('<span class="close">×</span>')
|
||||
.on('click', function(){ widget.trigger('progressClose') }))
|
||||
.on('click', function(){
|
||||
var cache = (that.__progress_cache || {})[text]
|
||||
cache.onclose
|
||||
&& cache.onclose()
|
||||
widget.trigger('progressClose') }))
|
||||
// state...
|
||||
.append($('<span/>')
|
||||
.addClass('progress-details'))
|
||||
@ -125,10 +140,12 @@ var ProgressActions = actions.Actions({
|
||||
.on('progressClose', function(){
|
||||
widget
|
||||
.fadeOut(that.config['progress-fade-duration'] || 200, function(){
|
||||
var cache = (that.__progress_cache || {})
|
||||
cache[text].timeout
|
||||
&& clearTimeout(cache[text].timeout)
|
||||
delete cache[text]
|
||||
var cache = (that.__progress_cache || {})[text]
|
||||
cache.timeout
|
||||
&& clearTimeout(cache.timeout)
|
||||
cache.ondone
|
||||
&& cache.ondone()
|
||||
delete (that.__progress_cache || {})[text]
|
||||
$(this).remove() }) })
|
||||
.appendTo(container)
|
||||
: widget
|
||||
@ -169,7 +186,7 @@ var ProgressActions = actions.Actions({
|
||||
// handle logger progress...
|
||||
// XXX revise...
|
||||
handleLogItem: ['- System/',
|
||||
function(path, status, ...rest){
|
||||
function(logger, path, status, ...rest){
|
||||
var msg = path.join(': ')
|
||||
var l = (rest.length == 1 && rest[0] instanceof Array) ?
|
||||
rest[0].length
|
||||
@ -196,24 +213,24 @@ var ProgressActions = actions.Actions({
|
||||
// close...
|
||||
// XXX is the right keyword...
|
||||
if(status == 'done' && rest.length == 0){
|
||||
this.showProgress(path, 'close')
|
||||
this.showProgress(path, 'close', logger)
|
||||
|
||||
// report progress...
|
||||
// XXX HACK -- need meaningful status...
|
||||
} else if(add.includes(status)){
|
||||
this.showProgress(path, '+0', '+'+l)
|
||||
this.showProgress(path, '+0', '+'+l, logger)
|
||||
|
||||
} else if(done.includes(status)){
|
||||
this.showProgress(path, '+'+l)
|
||||
this.showProgress(path, '+'+l, logger)
|
||||
|
||||
} else if(skipped.includes(status)){
|
||||
// XXX if everything is skipped the indicator does not
|
||||
// get hidden...
|
||||
this.showProgress(path, '+'+l)
|
||||
this.showProgress(path, '+'+l, logger)
|
||||
|
||||
// XXX STUB...
|
||||
} else if(status == 'error' ){
|
||||
this.showProgress(['Error'].concat(msg), '+0', '+'+l)
|
||||
this.showProgress(['Error'].concat(msg), '+0', '+'+l, logger)
|
||||
}
|
||||
}],
|
||||
})
|
||||
|
||||
@ -2189,13 +2189,13 @@ var BrowseActionsActions = actions.Actions({
|
||||
// it to sort an prioritize stuff...
|
||||
'action-category-order': [
|
||||
'99:$File',
|
||||
// ...
|
||||
// We can order any sub-tree we want in the same manner
|
||||
// as the root...
|
||||
'File/-80:Clear viewer',
|
||||
'File/-90:Close viewer',
|
||||
// Non existing elements will not get drawn...
|
||||
//'File/-99:moo',
|
||||
// XXX this seems over-crowded -- revise!!!
|
||||
'99:$Edit',
|
||||
'Edit/90:Undo',
|
||||
'Edit/90:Redo',
|
||||
@ -2210,10 +2210,12 @@ var BrowseActionsActions = actions.Actions({
|
||||
'Edit/70:.*shift.*',
|
||||
'Edit/60:.*rotate.*',
|
||||
'Edit/50:.*flip.*',
|
||||
// ...
|
||||
'$Navigate',
|
||||
'Navigate/90:.*image.*',
|
||||
'Navigate/80:.*screen.*',
|
||||
'Navigate/70:.*ribbon.*',
|
||||
// ...
|
||||
'$Image',
|
||||
'Image/99:$Copy image',
|
||||
'Image/99:.*copy.*',
|
||||
@ -2229,6 +2231,7 @@ var BrowseActionsActions = actions.Actions({
|
||||
'Image/65:.*shift.*',
|
||||
'Image/60:.*rotate.*',
|
||||
'Image/55:.*flip.*',
|
||||
// ...
|
||||
'Image/-70:---',
|
||||
'Image/-70:.*remove.*',
|
||||
'$Virtual block',
|
||||
@ -2238,14 +2241,25 @@ var BrowseActionsActions = actions.Actions({
|
||||
'Virtual block/60:.*mark.*',
|
||||
'Virtual block/50:---',
|
||||
'Virtual block/40:.*crop.*',
|
||||
// ...
|
||||
'$Ribbon',
|
||||
'Ribbon/80:set.*',
|
||||
'Ribbon/70:.*shift.*',
|
||||
'Ribbon/60:.*merge.*',
|
||||
'Ribbon/50:Flatten',
|
||||
'Ribbon/40:.*split.*',
|
||||
'Ribbon/30:.*crop.*',
|
||||
'Ribbon/20:.*order.*',
|
||||
'Ribbon/20:.*align.*',
|
||||
// ...
|
||||
'Ribbon/-60:Add.*collection.*',
|
||||
'Ribbon/-70:---',
|
||||
'Ribbon/-70:.*remove.*',
|
||||
'$Crop',
|
||||
'Crop/80:Crop $marked images',
|
||||
'Crop/80:Crop $bookmarked images',
|
||||
'Crop/70:$Crop',
|
||||
'Crop/70:$Flatten',
|
||||
'Crop/70:Crop $flatten',
|
||||
|
||||
// Path patterns...
|
||||
//
|
||||
@ -2279,32 +2293,35 @@ var BrowseActionsActions = actions.Actions({
|
||||
'Crop/-70:---',
|
||||
//*/
|
||||
|
||||
// ...
|
||||
'Crop/-50:---',
|
||||
'Crop/-60:Remove from crop',
|
||||
'Crop/-70:Remove ribbon.*',
|
||||
'Crop/-71:Remove marked.*',
|
||||
'Crop/-72:.*remove.*',
|
||||
|
||||
'Crop/-75:---',
|
||||
|
||||
'Crop/-80:Uncrop keeping image order',
|
||||
'Crop/-81:Uncrop all',
|
||||
'Crop/-82:$Uncrop',
|
||||
'Co$llections',
|
||||
// ...
|
||||
'Collections/-50:.*exit.*',
|
||||
'Collections/-60:.*edit.*',
|
||||
'Collections/-70:---',
|
||||
'Collections/-70:.*remove.*',
|
||||
'$Tag',
|
||||
// ...
|
||||
// XXX revise...
|
||||
'Tag/-80:---',
|
||||
'Tag/-90:.*remove.*',
|
||||
'$Mark',
|
||||
// ...
|
||||
'Mark/-75:.*collection...',
|
||||
'Mark/-80:---',
|
||||
'Mark/-80:.*remove.*',
|
||||
'Mark/-90:.*unmark.*',
|
||||
'$Bookmark',
|
||||
// ...
|
||||
'Bookmark/-80:---',
|
||||
'Bookmark/-80:.*remove.*',
|
||||
|
||||
@ -2312,6 +2329,7 @@ var BrowseActionsActions = actions.Actions({
|
||||
|
||||
'-40:Interface',
|
||||
'Interface/90:Theme',
|
||||
// ...
|
||||
'-50:$Workspace',
|
||||
'-60:System',
|
||||
'-70:$Help',
|
||||
|
||||
@ -17,6 +17,13 @@ var object = require('lib/object')
|
||||
var QueuePrototype = Object.create(actions.MetaActions)
|
||||
|
||||
|
||||
// XXX might be good to add a Promise-like api:
|
||||
// .then(..)
|
||||
// .catch(..)
|
||||
// .finally(..)
|
||||
// ...but since the queue may be fed and running without stopping
|
||||
// not sure what purpose these can serve if they are global...
|
||||
// ...these could have the semantics of run after last task added...
|
||||
// XXX need a mechanism to either queue chains of tasks that depend on
|
||||
// on the previous results or a way to delay a task until what it
|
||||
// needs is finished...
|
||||
@ -49,9 +56,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
: 0)
|
||||
+ (typeof(b) == typeof(1) ? b
|
||||
: that[b] ? that[b].len
|
||||
: 0)
|
||||
}, 0)
|
||||
},
|
||||
: 0) }, 0) },
|
||||
set length(val){},
|
||||
|
||||
// can be:
|
||||
@ -71,8 +76,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
//
|
||||
// XXX should be more informative -- now supports only 'running' and 'stopped'
|
||||
get state(){
|
||||
return this._state || 'stopped'
|
||||
},
|
||||
return this._state || 'stopped' },
|
||||
|
||||
|
||||
// General task life cycle events...
|
||||
@ -111,8 +115,8 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
} else {
|
||||
var tag = null
|
||||
var task = a
|
||||
var mode = b
|
||||
}
|
||||
var mode = b }
|
||||
|
||||
mode = mode || this.config['default-queue-mode']
|
||||
var ready = this.__ready = this.__ready || []
|
||||
|
||||
@ -120,22 +124,19 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
this.taskQueued(tag, task, mode)
|
||||
|
||||
// restart in case the queue was depleted...
|
||||
this._run()
|
||||
}],
|
||||
this._run() }],
|
||||
unqueue: ['',
|
||||
function(a, b){
|
||||
var that = this
|
||||
var ready = this.__ready
|
||||
// empty queue...
|
||||
if(ready == null || ready.len == 0){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
// special case -- drop all...
|
||||
if(a == '*'){
|
||||
ready.splice(0, ready.length)
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
// XXX prep args...
|
||||
var tag = typeof(a) == typeof('str') ? a : b
|
||||
@ -143,8 +144,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
|
||||
// no args...
|
||||
if(tag == null && task == null){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
// remove matching tasks from the queue...
|
||||
ready.forEach(function(e, i){
|
||||
@ -155,17 +155,13 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
// both task and tag given...
|
||||
: e[0] == tag && e[1] === task){
|
||||
delete ready[i]
|
||||
that.taskDropped(e[0], e[1], e[2])
|
||||
}
|
||||
})
|
||||
}],
|
||||
that.taskDropped(e[0], e[1], e[2]) } }) }],
|
||||
delay: ['',
|
||||
function(a, b){
|
||||
var ready = this.__ready
|
||||
// empty queue...
|
||||
if(ready == null || ready.len == 0){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
// XXX prep args...
|
||||
var tag = typeof(a) == typeof('str') ? a : b
|
||||
@ -173,8 +169,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
|
||||
// no args...
|
||||
if(tag == null && task == null){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
var delayed = []
|
||||
// remove the matching tasks...
|
||||
@ -188,19 +183,14 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
|
||||
if(res){
|
||||
delete ready[i]
|
||||
|
||||
delayed.push(e)
|
||||
}
|
||||
})
|
||||
delayed.push(e) } })
|
||||
|
||||
// push delayed list to the end of the queue...
|
||||
delayed.forEach(function(e){
|
||||
ready.push(e)
|
||||
})
|
||||
ready.push(e) })
|
||||
|
||||
// restart in case the queue was depleted...
|
||||
this._run()
|
||||
}],
|
||||
this._run() }],
|
||||
|
||||
// Run the queue...
|
||||
//
|
||||
@ -230,8 +220,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
_run: ['',
|
||||
function(){
|
||||
if(this.__is_running){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
var that = this
|
||||
var size = this.config['running-pool-size']
|
||||
@ -249,8 +238,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
// XXX this might race...
|
||||
var elem = that.__ready.shift()
|
||||
if(elem == null){
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
var task = elem[1]
|
||||
that.__is_running = true
|
||||
@ -292,9 +280,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
that.done()
|
||||
|
||||
that.config['clear-on-done']
|
||||
&& that.clear()
|
||||
}
|
||||
})
|
||||
&& that.clear() } })
|
||||
// push to done and ._run some more...
|
||||
.then(function(){
|
||||
// pop self of .__running
|
||||
@ -316,9 +302,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
that.done()
|
||||
|
||||
that.config['clear-on-done']
|
||||
&& that.clear()
|
||||
}
|
||||
})
|
||||
&& that.clear() } })
|
||||
|
||||
// other...
|
||||
} else {
|
||||
@ -338,13 +322,10 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
that.done()
|
||||
|
||||
that.config['clear-on-done']
|
||||
&& that.clear()
|
||||
}
|
||||
}
|
||||
&& that.clear() } }
|
||||
})() }
|
||||
|
||||
delete that.__is_running
|
||||
}],
|
||||
delete that.__is_running }],
|
||||
|
||||
// State manipulation actions...
|
||||
//
|
||||
@ -352,12 +333,10 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
start: ['',
|
||||
function(){
|
||||
this._state = 'ready'
|
||||
this._run()
|
||||
}],
|
||||
this._run() }],
|
||||
stop: ['',
|
||||
function(){
|
||||
delete this._state
|
||||
}],
|
||||
delete this._state }],
|
||||
clear: ['',
|
||||
function(){
|
||||
// XXX should this stop???
|
||||
@ -365,8 +344,7 @@ module.QueueActions = actions.Actions(QueuePrototype, {
|
||||
delete this.__ready
|
||||
delete this.__running
|
||||
delete this.__failed
|
||||
delete this.__done
|
||||
}],
|
||||
delete this.__done }],
|
||||
})
|
||||
|
||||
|
||||
@ -376,5 +354,6 @@ object.Constructor('Queue', QueueActions)
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 : */ return module })
|
||||
|
||||
6
Viewer/package-lock.json
generated
6
Viewer/package-lock.json
generated
@ -1117,9 +1117,9 @@
|
||||
"integrity": "sha512-EzT4CP6d6lI8bnknNgT3W8mUQhSVXflO0yPbKD4dKsFcINiC6npjoEBz+8m3VQmWJhc+36pXD4JLwNxUEgzi+Q=="
|
||||
},
|
||||
"ig-types": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ig-types/-/ig-types-3.0.2.tgz",
|
||||
"integrity": "sha512-djnS6UnS+a0y37DNFUk8PZf0lt7TQO5u/RQPYpFVW4sqwA2HpSdAmmrWyu6UCVdhw+b9Q6E+RjAXrWVEkn1w3A==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-types/-/ig-types-3.1.0.tgz",
|
||||
"integrity": "sha512-k/QbS9D30Fun3Xrh+6LHpCYsbQOwYlj1PX0uaNOnpmxg2tlWSdLOdykH8IMUbGIm/tI8MsJeKnJc4vu51s89Tg==",
|
||||
"requires": {
|
||||
"ig-object": "^5.2.8",
|
||||
"object-run": "^1.0.1"
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"ig-argv": "^2.15.0",
|
||||
"ig-features": "^3.4.2",
|
||||
"ig-object": "^5.2.8",
|
||||
"ig-types": "^3.0.2",
|
||||
"ig-types": "^3.1.0",
|
||||
"moment": "^2.29.1",
|
||||
"object-run": "^1.0.1",
|
||||
"requirejs": "^2.3.6",
|
||||
|
||||
@ -77,8 +77,7 @@ $(function(){
|
||||
} catch(err){
|
||||
console.error(err)
|
||||
//throw err
|
||||
return
|
||||
}
|
||||
return }
|
||||
|
||||
|
||||
// used to switch experimental actions on (set to true) or off (unset or false)...
|
||||
@ -105,8 +104,7 @@ $(function(){
|
||||
err.missing_suggested)
|
||||
err.missing.length > 0
|
||||
&& console.warn('Missing dependencies:',
|
||||
err.missing)
|
||||
}
|
||||
err.missing) }
|
||||
|
||||
|
||||
// setup the viewer...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user