From e9a31923358739fef24f1cb68f83104dc3668a06 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Thu, 26 Nov 2020 00:44:46 +0300 Subject: [PATCH] reworked core.Tasks, almost ready to remove legacy code... Signed-off-by: Alex A. Naanou --- Viewer/features/core.js | 264 ++++++--------------------------- Viewer/features/examples.js | 37 ++++- Viewer/features/sharp.js | 16 +- Viewer/features/ui-progress.js | 3 +- Viewer/package-lock.json | 40 ++--- Viewer/package.json | 8 +- 6 files changed, 119 insertions(+), 249 deletions(-) diff --git a/Viewer/features/core.js b/Viewer/features/core.js index f28ad8d2..09ff2b47 100755 --- a/Viewer/features/core.js +++ b/Viewer/features/core.js @@ -51,6 +51,8 @@ // XXX var DEBUG = typeof(DEBUG) != 'undefined' ? DEBUG : true +var types = require('lib/types') +var runner = require('lib/types/runner') var util = require('lib/util') var object = require('lib/object') var actions = require('lib/actions') @@ -59,6 +61,7 @@ var toggler = require('lib/toggler') + /*********************************************************************/ // NOTE: if no toggler state is set this assumes that the first state @@ -964,9 +967,7 @@ var LifeCycleActions = actions.Actions({ evt.indexOf('stopped.pre'), evt.indexOf('stopped.post')) >= 0 && this.isStopped() - && func.call(this) - } - }], + && func.call(this) } }], // helpers... restart: ['System/Soft restart', @@ -2350,8 +2351,8 @@ module.Workspace = ImageGridFeatures.Feature({ // XXX should this be a separate module??? //var tasks = require('lib/tasks') -var task = -module.tast = +var Task = +module.Tast = function(func){ func.__task__ = true return func } @@ -2393,11 +2394,12 @@ function(func){ // - list/sort/prioritize // - remote (peer/worker) // XXX docs... +// XXX LEGACY... var abortablePromise = module.abortablePromise = function(title, func){ return Object.assign( - task(function(...args){ + Task(function(...args){ var that = this var abort = object.mixinFlat( @@ -2440,96 +2442,33 @@ function(title, func){ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var Task = -module.Task = -object.Constructor('Task', { -}) - - -// XXX -var events = require('lib/types/event') - +// Task action action helpers... +// +// NOTE: for examples see: +// features/examples.js: +// ExampleActions.exampleTask(..) +// ExampleActions.exampleSessionTask(..) var taskAction = module.taskAction = function(title, func){ var action - return (action = Object.assign( - task(function(...args){ - var that = this - - // XXX - var ticket = events.EventMixin({ - // can be: - // - ready - // - running - // - done - state: null, - - start: events.Event('start', function(handle, ...args){ - if(this.state == 'ready'){ - that.resumeTask(title, action) - handle(...args) } }), - pause: events.Event('pause', function(handle, ...args){ - if(this.state == 'running'){ - that.pauseTask(title, action) - handle(...args) } }), - abort: events.Event('abort', function(handle, ...args){ - if(!this.state != 'done'){ - that.abortTask(title, action) - handle(...args) } }), - }) - - // XXX - - return func.call(this, ticket, ...args) }), + return (action = object.mixin( + Task(function(...args){ + return this.tasks.Task(title, func.bind(this), ...args) }), { + __task_title__: title, toString: function(){ - return `core.taskAction('${ title }', \n${ func.toString() })` }, + return `core.taskAction('${ title }', \n\t${ + object.normalizeIndent('\t'+func.toString()) })` }, })) } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var makeTaskAction = -function(name, from, to, callback){ - return function(title, task='all'){ - title = title == '*' || title == 'all' ? - [...(this.__running_tasks || new Map()).keys()] - : title instanceof Array ? - title - : [title] - this.__running_tasks - && title - .forEach(function(title){ - [...(this.__running_tasks || new Map()).get(title) || []] - .forEach(function(t){ - // filter task... - ;(task == 'all' - || task == '*' - || task === t - || (task instanceof Array - && task.includes(t))) - // filter states... - && (from == '*' - || from == 'all' - || t.state == from) - // XXX do we retrigger??? - //&& t.state != to - // call handler... - && t[name] - && t[name]() !== false - // state... - && to - && (t.state = to) }) - callback - && callback.call(this, title, task) }.bind(this)) - // cleanup... - this.__running_tasks - && this.__running_tasks.size == 0 - && (delete this.__running_tasks) } } - +var sessionTaskAction = +module.sessionTaskAction = +function(title, func){ + return object.mixin( + taskAction(...arguments), + { __session_task__: true }) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2538,120 +2477,30 @@ var TaskActions = actions.Actions({ config: { }, - // Format: - // Map({ - // title: Set([ - // { - // state: ..., - // abort: func, - // pause: func, - // resume: func, - // ... - // }, - // ... - // ]), - // ... - // }) + // actions that generate tasks... // - __running_tasks: null, + // XXX cache these??? + get taskActions(){ + return this.actions + .filter(function(action){ + return !!this.getActionAttr(action, '__task__') }.bind(this)) }, + get sessionTaskActions(){ + return this.actions + .filter(function(action){ + return !!this.getActionAttr(action, '__session_task__') }.bind(this)) }, - // XXX should this .resume(..)??? - // XXX might be a good idea to make this compatible with tasks.Queue(..) - // ...and return a queue if not task is given?? - Task: ['- System/', - doc` - `, - function(title, task){ - // reserved titles... - if(title == 'all' || title == '*'){ - throw new Error('.abortable(..): can not set reserved title: "'+ title +'".') } - - var tasks = this.__running_tasks = this.__running_tasks || new Map() - var set = tasks.get(title) || new Set() - tasks.set(title, set) - set.add(task) - - task.state = 'running' - - return task }], - - getTasks: ['- System/', - function(title='all', state='all'){ - var normArg = function(arg){ - return !arg ? - 'all' - : arg == 'all' || arg == '*' ? - arg - : arg instanceof Array ? - new Set(arg) - : new Set([arg]) } - - title = normArg(title) - state = normArg(state) - - return this.__running_tasks ? - [...this.__running_tasks.entries()] - .reduce(function(res, [t, set]){ - if(title != 'all' - && title != '*' - && !title.has(t)){ - return res } - var l = [...set] - .filter(function(t){ - return state == 'all' - || state == '*' - || state.has(t.state) }) - l.length > 0 - && (res[t] = l) - return res }, {}) - : {} }], - // XXX should this abort the cleared tasks??? - // ...if not would be logical to rename this to ._clearTask(..) - clearTask: ['- System/', - function(title, task='all'){ - // clear all... - if(title == '*' || title == 'all'){ - delete this.__running_tasks } - - var set = ((this.__running_tasks || new Map()).get(title) || new Set()) - // clear specific handler... - task != '*' - && task != 'all' - && set.delete(task) - // cleanup / clear title... - ;(set.size == 0 - || task == '*' - || task == 'all') - && (this.__running_tasks || new Set()).delete(title) - // cleanup... - this.__running_tasks - && this.__running_tasks.size == 0 - && (delete this.__running_tasks) }], - - // XXX cache??? + // task manager... + // + __task_manager__: runner.TaskManager, + __tasks: null, get tasks(){ - return this.actions.filter(function(action){ - return !!this.getActionAttr(action, '__task__') }.bind(this)) }, + return (this.__tasks = + this.__tasks + || this.__task_manager__()) }, + // session tasks are stopped when the index is cleared... + get sessionTasks(){ + return this.tasks.titled(...this.sessionTaskActions) }, - get tasksActive(){ - return this.getTasks() }, - get tasksRunning(){ - return this.getTasks('all', 'running') }, - get tasksPaused(){ - return this.getTasks('all', 'paused') }, - - pauseTask: ['- System/', - makeTaskAction('pause', 'running', 'paused')], - resumeTask: ['- System/', - makeTaskAction('resume', 'paused', 'running')], - abortTask: ['- System/', - makeTaskAction('abort', 'all', null, - function(title, task='all'){ - this.__running_tasks - && (task == 'all' - || task == '*' - || this.__running_tasks.get(title).size == 0) - && this.__running_tasks.delete(title) })], // XXX LEGACY -- remove after migrating sharp.js and abortablePromise(..) @@ -2748,25 +2597,6 @@ var TaskActions = actions.Actions({ this.__abortable && this.__abortable.size == 0 && (delete this.__abortable) }], - - - /* XXX LEGACY... - get jobs(){ - return this.__jobs }, - - getJob: ['- Jobs/', - function(name){ - name = name || this.data.newGID() - - // get/init task dict... - var t = this.__jobs = this.__jobs || {} - // get/init task... - var job = t[name] = t[name] || tasks.Queue() - job.name = name - - return job - }], - //*/ }) @@ -2781,6 +2611,8 @@ module.Tasks = ImageGridFeatures.Feature({ actions: TaskActions, handlers: [ + ['clear', + 'sessionTasks.stop'], ], }) diff --git a/Viewer/features/examples.js b/Viewer/features/examples.js index 4b00aa19..1c083983 100755 --- a/Viewer/features/examples.js +++ b/Viewer/features/examples.js @@ -267,14 +267,41 @@ var ExampleActions = actions.Actions({ // XXX inner/outer action... + // NOTE: action name and task name should be the same to avoid + // confusion... + // XXX it would be quite complicated to support both and + // confusing to support either... exampleTask: ['- Test/', - core.taskAction('Example task', function(ticket, ...args){ + core.taskAction('exampleTask', + function(ticket, ...args){ + console.log('###', ticket.title+':', 'START:', ...args, + '\n\t\t(supported messages: "stop", "break", "error", ...)') + ticket.onmessage(function(msg, ...args){ + // stop... + if(msg == 'stop'){ + console.log('###', ticket.title+':', 'STOP') + ticket.resolve(...args) - // XXX - console.log('>>>>', ticket, ...args) + // break... + } else if(msg == 'break'){ + console.log('###', ticket.title+':', 'BREAK') + ticket.reject(...args) - return Promise.cooperative() - })], + // error... + } else if(msg == 'error'){ + console.log('###', ticket.title+':', 'ERROR') + throw new Error('Task error') + + // other... + } else { + console.log('###', ticket.title+':', 'Got message:', msg, ...args) } }) })], + exampleSessionTask: ['- Test/', + core.sessionTaskAction('exampleSessionTask', + function(ticket, ...args){ + console.log('###', ticket.title+':', 'START:', ...args) + ticket.onmessage('stop', function(){ + console.log('###', ticket.title+':', 'STOP:', ...args) + ticket.resolve(...args) }) })], }) var Example = diff --git a/Viewer/features/sharp.js b/Viewer/features/sharp.js index d95bd2e1..9b9b0105 100755 --- a/Viewer/features/sharp.js +++ b/Viewer/features/sharp.js @@ -411,7 +411,11 @@ var SharpActions = actions.Actions({ .then(function(){ logger && logger.emit('done', to) - return img }) }) }) }) })], + return img }) }) }) }) + .then(function(res){ + return res == 'aborted' ? + Promise.reject('aborted') + : res }) })], // XXX this does not update image.base_path -- is this correct??? // XXX add support for offloading the processing to a thread/worker... @@ -544,7 +548,9 @@ var SharpActions = actions.Actions({ return [gid, size, name] }) }) }) .then(function(res){ - return res.flat() }) })], + return res == 'aborted' ? + Promise.reject('aborted') + : res.flat() }) })], // XXX add support for offloading the processing to a thread/worker... // XXX should we use task.Queue()??? @@ -762,7 +768,11 @@ var SharpActions = actions.Actions({ that.ribbons && that.ribbons.updateImage(gid) - return done(gid) }) }) })], + return done(gid) }) }) + .then(function(res){ + return res == 'aborted' ? + Promise.reject('aborted') + : res }) })], cacheAllMetadata: ['- Sharp|Image/', core.doc`Cache all metadata NOTE: this is a shorthand to .cacheMetadata('all', ..)`, diff --git a/Viewer/features/ui-progress.js b/Viewer/features/ui-progress.js index 10298b46..f17174da 100755 --- a/Viewer/features/ui-progress.js +++ b/Viewer/features/ui-progress.js @@ -90,6 +90,7 @@ var ProgressActions = actions.Actions({ // XXX multiple containers... // XXX shorten the nested css class names... // XXX revise styles... + // XXX make the "X" bigger -- finger usable... __progress_cache: null, showProgress: ['- Interface/Show progress bar...', core.doc`Progress bar widget... @@ -201,7 +202,7 @@ var ProgressActions = actions.Actions({ .append($('')) // events... .on('progressClose', function(){ - widget + $(this) .fadeOut(that.config['progress-fade-duration'] || 200, function(){ var cache = (that.__progress_cache || {})[text] cache.timeout diff --git a/Viewer/package-lock.json b/Viewer/package-lock.json index 543ca2b5..0106fa26 100755 --- a/Viewer/package-lock.json +++ b/Viewer/package-lock.json @@ -1,6 +1,6 @@ { "name": "ImageGrid.Viewer.g4", - "version": "4.0.0a", + "version": "4.0.0-a", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -549,9 +549,9 @@ } }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { "ms": "2.1.2" } @@ -636,9 +636,9 @@ } }, "electron": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.4.tgz", - "integrity": "sha512-OHP8qMKgW8D8GtH+altB22WJw/lBOyyVdoz5e8D0/iPBmJU3Jm93vO4z4Eh/9DvdSXlH8bMHUCMLL9PVW6f+tw==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.5.tgz", + "integrity": "sha512-EPmDsp7sO0UPtw7nLD1ufse/nBskP+ifXzBgUg9psCUlapkzuwYi6pmLAzKLW/bVjwgyUKwh1OKWILWfOeLGcQ==", "requires": { "@electron/get": "^1.0.1", "@types/node": "^12.0.12", @@ -646,9 +646,9 @@ }, "dependencies": { "@types/node": { - "version": "12.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.4.tgz", - "integrity": "sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w==" + "version": "12.19.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.6.tgz", + "integrity": "sha512-U2VopDdmBoYBmtm8Rz340mvvSz34VgX/K9+XCuckvcLGMkt3rbMX8soqFOikIPlPBc5lmw8By9NUK7bEFSBFlQ==" } } }, @@ -1073,9 +1073,9 @@ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ig-actions": { - "version": "3.24.13", - "resolved": "https://registry.npmjs.org/ig-actions/-/ig-actions-3.24.13.tgz", - "integrity": "sha512-x7+37fJ1wsdSl/VNb4W569IHiUx2SKtEr76PI6LxXOW3fRkv6/9KnDXJK8pL+XVO012oj096WatYK80aPUlLPQ==", + "version": "3.24.15", + "resolved": "https://registry.npmjs.org/ig-actions/-/ig-actions-3.24.15.tgz", + "integrity": "sha512-fhMZ1F34nPm/Hir1Is+3hKPJpshgwbxjnQRJb5M+/fs52FfoG7TMbDYWzbKKe6qtwfyrcVD2rPRidjCl6779zQ==", "requires": { "ig-object": "^5.0.2" } @@ -1105,16 +1105,16 @@ } }, "ig-object": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.4.11.tgz", - "integrity": "sha512-WPPQ5C41c6q3tPfa2fBbWE2xcLF7LoGRu2E6Wr/aoA5oxAyl8lAuE7Kqt4TyPwfW9jVI0+ifBztg9e1tR5mG1Q==" + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.4.12.tgz", + "integrity": "sha512-9kZM80Js9/eTwXN9VXwLDC1wDJ7gIAdYU9GIzb5KJmNcLAMaW+zhgFrwFFMrcSfggUuadgnqSrS41E4XLe8JZw==" }, "ig-types": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ig-types/-/ig-types-4.1.2.tgz", - "integrity": "sha512-IYnuBWw7m7qvtW21ggoodFA++v+bHwNMqGRvo9w2IwfI3leVPbOORUC5CjUKb4q/ffGlCcT3gC7w+E/aO8BOTw==", + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/ig-types/-/ig-types-5.0.14.tgz", + "integrity": "sha512-j4oyqZP+xasNYMyWllcZz5kT8vscB58P6smehoBEHxNRPlKMqfZTP4MlxQVZpyAgPH8HCCwGjEYZ7c63FokWFA==", "requires": { - "ig-object": "^5.4.11", + "ig-object": "^5.4.12", "object-run": "^1.0.1" } }, diff --git a/Viewer/package.json b/Viewer/package.json index b1ef8891..aa25b634 100755 --- a/Viewer/package.json +++ b/Viewer/package.json @@ -20,7 +20,7 @@ "dependencies": { "app-module-path": "^1.0.6", "async-json": "0.0.2", - "electron": "^9.3.4", + "electron": "^9.3.5", "exif-reader": "^1.0.3", "exiftool": "^0.0.3", "fs-extra": "^7.0.1", @@ -28,11 +28,11 @@ "generic-walk": "^1.4.0", "glob": "^7.1.6", "guarantee-events": "^1.0.0", - "ig-actions": "^3.24.13", + "ig-actions": "^3.24.15", "ig-argv": "^2.15.0", "ig-features": "^3.4.2", - "ig-object": "^5.4.11", - "ig-types": "^4.1.2", + "ig-object": "^5.4.12", + "ig-types": "^5.0.14", "moment": "^2.29.1", "object-run": "^1.0.1", "requirejs": "^2.3.6",