diff --git a/ui (gen4)/features/keyboard.js b/ui (gen4)/features/keyboard.js index 11ff37eb..aff11612 100755 --- a/ui (gen4)/features/keyboard.js +++ b/ui (gen4)/features/keyboard.js @@ -256,8 +256,7 @@ module.GLOBAL_KEYBOARD = { // modes... - //Enter: 'toggleSingleImage', - Enter: 'debounce: 500 "toggleSingleImage" -- Toggle single image mode (debounced)', + Enter: '@500 toggleSingleImage', S: 'slideshowDialog', @@ -513,6 +512,120 @@ var KeyboardActions = actions.Actions({ function(){ return that.dom }) return kb }, + debounce: ['- Interface/', + core.doc`Debounce action call... + + Debounce call an action... + .debounce(action, ...) + .debounce(timeout, action, ...) + .debounce(tag, action, ...) + .debounce(timeout, tag, action, ...) + + Debounce call a function... + .debounce(tag, func, ...) + .debounce(timeout, tag, func, ...) + + NOTE: when using a tag, it must not resolve to and action, i.e. + this[tag] must not be callable... + NOTE: this ignores action return value and returns this... + `, + function(...args){ + // parse the args... + var timeout = typeof(args[0]) == typeof(123) ? + args.shift() + : (this.config['debounce-action-timeout'] || 200) + // NOTE: this[tag] must not be callable, otherwise we treat it + // as an action... + var tag = (args[0] instanceof Function + || this[args[0]] instanceof Function) ? + args[0] + : args.shift() + var action = args.shift() + + // when debouncing a function a tag is required... + if(tag instanceof Function){ + throw new TypeError('debounce: when passing a function a tag is required.') + } + + var attr = '__debounce_'+ tag + + // repeated call... + if(this[attr]){ + this[attr +'_retriggered'] = true + + // setup and first call... + } else { + // NOTE: we are ignoring the return value here so as to + // make the first and repeated call uniform... + var context = this + ;(action instanceof Function ? + action + : action.split('.') + .reduce(function(res, e){ + context = res + return res[e] + }, this)) + .call(context, ...args) + + this[attr] = setTimeout(function(){ + delete this[attr] + + // retrigger... + if(this[attr +'_retriggered']){ + delete this[attr +'_retriggered'] + + tag == action ? + this.debounce(timeout, action, ...args) + : this.debounce(timeout, tag, action, ...args) + } + }.bind(this), timeout) + } + }], + + // Add debounce support to keyboard handling... + // + // NOTE: these are not actions to make the call as light as possible... + parseStringHandler: function(txt, ...rest){ + var debounce = 0 + var scale = { + ms: 1, + s: 1000, + m: 1000 * 60, + h: 1000 * 60 * 60, + } + txt = txt.replace(/^\s*@([^\s]*)\s*/, + function(_, time){ + var unit = time + .replace(/(\d+|\d*\.\d+)(h|m|s|ms)/, '$2') + unit = unit == time ? 'ms' : unit + debounce = parseFloat(time) * scale[unit] + return '' + }) + + return debounce > 0 ? + Object.assign( + this.parseStringAction(txt, ...rest), + {debounce: debounce}) + : this.parseStringAction(txt, ...rest) + }, + callKeyboardHandler: function(data, context){ + context = context || this + var meth = data.action + .split('.') + .reduce(function(res, e){ + context = res + return res[e] + }, this) + return data.debounce ? + // debounce... + this.debounce( + data.debounce, + 'tag:'+data.action, + meth.bind(context), ...data.arguments) + // direct call... + : meth.call(context, ...data.arguments) }, + + testKeyboardDoc: ['- Interface/', core.doc`Self-test action keyboard configuration.`, {self_test: true}, @@ -529,7 +642,8 @@ var KeyboardActions = actions.Actions({ // XXX we should also check if code is a key (i.e. alias)... - var a = keyboard.parseActionCall(code, that) + //var a = keyboard.parseActionCall(code, that) + var a = that.parseStringHandler(code, that) // skip aliases that look like actions (namely ':') and bad actions... if(a.action == ''){ return @@ -691,7 +805,8 @@ var KeyboardActions = actions.Actions({ // - .no_default // - .stop_propagation var normalizeHandler = function(action){ - var a = keyboard.parseActionCall(action.doc || action, that) + //var a = keyboard.parseActionCall(action.doc || action, that) + var a = that.parseStringHandler(action.doc || action, that) return a.action in that ? a.action +(a.arguments.length > 0 ? @@ -838,56 +953,6 @@ var KeyboardActions = actions.Actions({ this.config['keyboard-repeat-pause-check'] > 0 && this.keyboard.pauseRepeat && this.keyboard.pauseRepeat() }], - - debounce: ['- Interface/', - core.doc`Debounce action call... - - .debounce(action, ...) - .debounce(timeout, action, ...) - .debounce(timeout, tag, action, ...) - - NOTE: when using a tag, it must not resolve to and action, i.e. - this[tag] must not be callable... - NOTE: this ignores action return value and returns this... - `, - function(...args){ - // parse the args... - var timeout = typeof(args[0]) == typeof(123) ? - args.shift() - : (this.config['debounce-action-timeout'] || 200) - // NOTE: this[tag] must not be callable, otherwise we treat it - // as an action... - var tag = this[args[0]] instanceof Function ? - args[0] - : args.shift() - var action = args.shift() - - var attr = '__debounce_'+ tag - - // repeated call... - if(this[attr]){ - this[attr +'_retriggered'] = true - - // setup and first call... - } else { - // NOTE: we are ignoring the return value here so as to - // make the first and repeated call uniform... - this[action](...args) - - this[attr] = setTimeout(function(){ - delete this[attr] - - // retrigger... - if(this[attr +'_retriggered']){ - delete this[attr +'_retriggered'] - - tag == action ? - this.debounce(timeout, action, ...args) - : this.debounce(timeout, tag, action, ...args) - } - }.bind(this), timeout) - } - }], }) var Keyboard = @@ -913,7 +978,8 @@ module.Keyboard = core.ImageGridFeatures.Feature({ this.__keyboard_config = this.keybindings || GLOBAL_KEYBOARD // string action call parser... - this.parseStringHandler = this.parseStringAction + //this.parseStringHandler = this.parseStringAction + //this.parseStringHandler = this.parseStringActionWithDebounce this.toggleKeyboardHandling('on') }], @@ -1069,7 +1135,8 @@ var KeyboardUIActions = actions.Actions({ var c = 0 Object.keys(keys[mode] || {}).forEach(function(action){ - var o = keyboard.parseActionCall(action, that) + //var o = keyboard.parseActionCall(action, that) + var o = that.parseStringHandler(action, that) if(getKeyText){ var doc = '' @@ -1419,7 +1486,8 @@ var KeyboardUIActions = actions.Actions({ ['⋯', function(evt, elem){ code = code || '' // highlight the current action... - var a = keyboard.parseActionCall(code, that) + //var a = keyboard.parseActionCall(code, that) + var a = that.parseStringHandler(code, that) var p = a.action in that ? that.getDocPath(a.action) : '' @@ -1444,6 +1512,9 @@ var KeyboardUIActions = actions.Actions({ .on('edit-commit', function(evt, text){ code = text }) + // XXX should we edit/view this separately??? + //make(['Debounce:', that.parseStringHandler(code).debounce || '']) + make('---') make.EditableList(keys, { diff --git a/ui (gen4)/lib/keyboard.js b/ui (gen4)/lib/keyboard.js index ffa8211d..311cf839 100755 --- a/ui (gen4)/lib/keyboard.js +++ b/ui (gen4)/lib/keyboard.js @@ -536,6 +536,18 @@ var KeyboardPrototype = { // XXX revise name... parseStringHandler: parseActionCall, + // call keyboard handler... + // + callKeyboardHandler: function(data, context){ + // call the handler... + return data.action + .split('.') + .reduce(function(res, k){ + context = res + return res[k] + }, context) + .apply(context, h.arguments) }, + // utils... event2key: KeyboardClassPrototype.event2key, @@ -1193,7 +1205,9 @@ function makeKeyboardHandler(keyboard, unhandled, actions){ // action call syntax... // XXX should this be a Keyboard thing or a context thing??? } else if(actions.parseStringHandler || kb.parseStringHandler){ - var h = (actions.parseStringHandler || kb.parseStringHandler)(handler, actions) + var h = actions.parseStringHandler ? + actions.parseStringHandler(handler, actions) + : kb.parseStringHandler(handler, actions) var path = h ? h.action.split('.') : [] if(path.length > 0 && path[0] in actions){ @@ -1204,13 +1218,9 @@ function makeKeyboardHandler(keyboard, unhandled, actions){ && evt.preventDefault() // call the handler... - var context = actions - res = path - .reduce(function(res, k){ - context = res - return res[k] - }, actions) - .apply(context, h.arguments) + res = actions.callKeyboardHandler ? + actions.callKeyboardHandler(h, actions) + : kb.callKeyboardHandler(h, actions) evt && h.stop_propagation diff --git a/ui (gen4)/package-lock.json b/ui (gen4)/package-lock.json index ee98434b..472926ee 100755 --- a/ui (gen4)/package-lock.json +++ b/ui (gen4)/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": { @@ -1141,11 +1141,11 @@ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, "ig-actions": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/ig-actions/-/ig-actions-3.19.3.tgz", - "integrity": "sha512-UPhN/RVUGNJ9PSV/zrgZNDDm5ySPYvcrRTheHZ5QW/UtdKtEI0CGTbmTVGuEAXH/Ag23KexJnUUlnSrtU5Jsjw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/ig-actions/-/ig-actions-3.20.0.tgz", + "integrity": "sha512-TcKAJg3ZtbZC7IiaNW/cMgwDkEt5xrr8hsTsoH0UTA9gQSIlnp6TACxcTW/gOS9lQiK82yDYHMrmH12Yn6+SFg==", "requires": { - "ig-object": "^1.0.0" + "ig-object": "^1.0.7" } }, "ig-features": {