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:
Alex A. Naanou 2020-11-05 22:46:27 +03:00
parent da63da2f2c
commit baa1b06dce
10 changed files with 425 additions and 153 deletions

View File

@ -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) }],

View File

@ -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,15 +391,16 @@ var LoggerActions = actions.Actions({
// XXX move this to console-logger???
handleLogItem: ['- System/',
function(path, status, ...rest){
console.log(
path.join(': ') + (path.length > 0 ? ': ' : '')
+ status
+ (rest.length > 1 ?
':\n\t'
: rest.length == 1 ?
': '
: ''), ...rest) }],
function(logger, path, status, ...rest){
logger.quiet
|| console.log(
path.join(': ') + (path.length > 0 ? ': ' : '')
+ status
+ (rest.length > 1 ?
':\n\t'
: rest.length == 1 ?
': '
: ''), ...rest) }],
})
var Logger =
@ -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 =

View File

@ -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 =

View File

@ -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') }],
//*/

View File

@ -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">&times;</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)
}
}],
})

View File

@ -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',

View File

@ -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 })

View File

@ -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"

View File

@ -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",

View File

@ -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...