From 490d45c4893ab382c809624d283e6376857ede15 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Wed, 9 Dec 2015 00:04:09 +0300 Subject: [PATCH] added generic url-history + several features that use it + some fixing and tweaking... Signed-off-by: Alex A. Naanou --- ui (gen4)/css/widget/browse.css | 6 + ui (gen4)/lib/widget/browse.html | 6 +- ui (gen4)/lib/widget/browse.js | 14 +- ui (gen4)/ui.js | 7 +- ui (gen4)/viewer.js | 383 +++++++++++++++++++++++++++++-- 5 files changed, 387 insertions(+), 29 deletions(-) diff --git a/ui (gen4)/css/widget/browse.css b/ui (gen4)/css/widget/browse.css index 856e0c3a..c723a759 100755 --- a/ui (gen4)/css/widget/browse.css +++ b/ui (gen4)/css/widget/browse.css @@ -187,6 +187,12 @@ overflow: hidden; } +.browse-widget .list>div.highlighted { + font-style: italic; +} +.browse-widget .list>div.highlighted:after { + content: '*'; +} .browse-widget:not(.flat) .list div:not(.not-traversable) .text:after { content: "/"; } diff --git a/ui (gen4)/lib/widget/browse.html b/ui (gen4)/lib/widget/browse.html index 5a7e92a1..0f3e5d1d 100755 --- a/ui (gen4)/lib/widget/browse.html +++ b/ui (gen4)/lib/widget/browse.html @@ -161,7 +161,7 @@ TREE.dir_c.dir_b.tree = TREE var use_disabled = true var show_files = false -requirejs(['../keyboard', '../../object', './browse'], function(k, o, br){ +requirejs(['../keyboard', '../object', './browse'], function(k, o, br){ keyboard = k object = o browser = br @@ -207,11 +207,13 @@ requirejs(['../keyboard', '../../object', './browse'], function(k, o, br){ 'option 2', 'option 3', 'option 4', - 'option 5', + 'option 5/moo', 'option 6', 'option 7', ], + path: 'option 5/moo', + fullPathEdit: false, traversable: false, flat: true, diff --git a/ui (gen4)/lib/widget/browse.js b/ui (gen4)/lib/widget/browse.js index fdaf05a7..48f4f8ca 100755 --- a/ui (gen4)/lib/widget/browse.js +++ b/ui (gen4)/lib/widget/browse.js @@ -376,7 +376,11 @@ var BrowserPrototype = { // Call the constructor's .path2list(..).. // // See: BrowserClassPrototype.path2list(..) for docs... - path2list: function(){ + path2list: function(path){ + // if list is flat we do not need to split it, just format... + if(this.options.flat && path && path.constructor !== Array){ + return path == '' || path.length == 0 ? [] : [path] + } return this.constructor.path2list.apply(this, arguments) }, @@ -1941,6 +1945,7 @@ Browser.prototype.__proto__ = widget.Widget.prototype var ListPrototype = Object.create(BrowserPrototype) ListPrototype.options = { + pathPrefix: '', fullPathEdit: false, traversable: false, flat: true, @@ -1978,8 +1983,11 @@ object.makeConstructor('List', // This is a shorthand for: new List(, { data: }) var makeList = -module.makeList = function(elem, list){ - return List(elem, { data: list }) +module.makeList = function(elem, list, path){ + return List(elem, { + data: list, + path: path, + }) } diff --git a/ui (gen4)/ui.js b/ui (gen4)/ui.js index 7567e407..33e6b077 100755 --- a/ui (gen4)/ui.js +++ b/ui (gen4)/ui.js @@ -102,7 +102,10 @@ module.GLOBAL_KEYBOARD = { 'ctrl+shift': 'F5', }, L: 'rotateCCW', - H: 'flipHorizontal', + H: { + default: 'flipHorizontal', + ctrl: 'listURLHistory', + }, V: 'flipVertical', P: { 'ctrl+shift': 'F12', @@ -246,7 +249,7 @@ module.GLOBAL_KEYBOARD = { $(function(){ // list all loaded modules... - console.log('LOADED:', requirejs.s.contexts._.defined) + console.log('MODULES:', requirejs.s.contexts._.defined) // XXX stub action set -- this needs to be auto-generated... window.a = actions.Actions() diff --git a/ui (gen4)/viewer.js b/ui (gen4)/viewer.js index 70aeb973..3a69a8e1 100755 --- a/ui (gen4)/viewer.js +++ b/ui (gen4)/viewer.js @@ -1481,7 +1481,7 @@ module.Journal = ImageGridFeatures.Feature({ clone: [function(full){ return function(res){ res.journal = null - if(full && Object.hasOwnProperty(this, 'journal') && this.journal){ + if(full && this.hasOwnProperty('journal') && this.journal){ res.journal = JSON.parse(JSON.stringify(this.journal)) } } @@ -1490,7 +1490,7 @@ module.Journal = ImageGridFeatures.Feature({ // XXX might be good to add some kind of metadata to journal... journalPush: ['Journal/Add an item to journal', function(){ - this.journal = (Object.hasOwnProperty(this, 'journal') + this.journal = (this.hasOwnProperty('journal') || this.journal) ? this.journal : [] @@ -3449,15 +3449,15 @@ var FileSystemLoaderActions = actions.Actions({ }, clone: [function(full){ - return function(res){ - if(this.base_path){ - res.base_path = this.base_path - } - if(this.loaded_paths){ - res.loaded_paths = JSON.parse(JSON.stringify(this.loaded_paths)) - } + return function(res){ + if(this.base_path){ + res.base_path = this.base_path } - }], + if(this.loaded_paths){ + res.loaded_paths = JSON.parse(JSON.stringify(this.loaded_paths)) + } + } + }], // NOTE: these will remove the trailing '/' (or '\') unless the path // is root... @@ -3595,6 +3595,7 @@ var FileSystemLoaderActions = actions.Actions({ // XXX use the logger... // XXX add a recursive option... // ...might also be nice to add sub-dirs to ribbons... + // XXX make image pattern more generic... loadImages: ['File/Load images', function(path, logger){ if(path == null){ @@ -3603,7 +3604,7 @@ var FileSystemLoaderActions = actions.Actions({ var that = this - glob(path + '/*+(jpg|png)') + glob(path + '/*+(jpg|jpeg|png|JPG|JPEG|PNG)') .on('end', function(lst){ that.loadURLs(lst .map(function(p){ return normalizePath(p) }), path) @@ -3638,7 +3639,7 @@ var FileSystemLoaderActions = actions.Actions({ var base_pattern = RegExp('^'+path) // find images... - glob(path + '/*+(jpg|png)') + glob(path + '/*+(jpg|jpeg|png|JPG|JPEG|PNG)') .on('end', function(lst){ // create a new images chunk... lst = lst @@ -3830,7 +3831,7 @@ module.FileSystemLoaderUI = ImageGridFeatures.Feature({ title: '', doc: '', - tag: 'fs-loader-ui', + tag: 'ui-fs-loader', depends: ['fs-loader'], actions: FileSystemLoaderUIActions, @@ -3838,6 +3839,340 @@ module.FileSystemLoaderUI = ImageGridFeatures.Feature({ +//--------------------------------------------------------------------- +// url history... + +var URLHistoryActions = actions.Actions({ + config: { + 'url-history-push-up-on-open': false, + + // values: + // -1 - no limit. + // 0 - disabled + // 1+ - length of history + 'url-history-length': 100, + }, + + __url_history: null, + + // Format: + // { + // url: { + // open: | , + // check: | , + // }, + // ... + // } + // + // NOTE: last opened url is last... + // NOTE: though functions are supported they are not recommended as + // we can not stringify them to JSON... + get url_history(){ + return this.hasOwnProperty('__url_history') ? this.__url_history : undefined + }, + set url_history(value){ + this.__url_history = value + }, + + + clone: [function(full){ + return function(res){ + res.url_history = null + if(full && this.url_history){ + res.url_history = JSON.parse(JSON.stringify(this.url_history)) + } + } + }], + + pushURLToHistory: ['', + function(url, open, check){ + var l = this.config['url-history-length'] || -1 + + if(l == 0){ + return + } + + this.url_history = this.url_history || {} + + // remove the old value... + if(url in this.url_history && this.config['url-history-push-up-on-open']){ + delete this.url_history[url] + } + + // push url to history... + this.url_history[url] = { + open: open, + check: check, + } + + // update history length... + if(l > 0){ + var k = Object.keys(this.url_history) + while(k.length > l){ + // drop first url in order -- last added... + this.dropURLFromHistory(k[0]) + var k = Object.keys(this.url_history) + } + } + }], + // NOTE: url can be an index, 0 being the last url added to history; + // negative values are also supported. + dropURLFromHistory: ['', + function(url){ + this.url_history = this.url_history || {} + + url = typeof(url) == typeof(123) ? + Object.keys(this.url_history).reverse().slice(url)[0] + : url + + if(url){ + delete this.url_history[url] + } + }], + checkURLFromHistory: ['', + function(url){ + this.url_history = this.url_history || {} + + url = typeof(url) == typeof(123) ? + Object.keys(this.url_history).reverse().slice(url)[0] + : url + + // if we have a check action then use it... + if(url && this.url_history[url] && this.url_history[url].check){ + var check = this.url_history[url].check + + if(check instanceof Function){ + return check(url) + + } else { + return this[check](url) + } + + // no way to check so we do not know... + } else { + return true + } + }], + openURLFromHistory: ['', + function(url){ + this.url_history = this.url_history || {} + + url = typeof(url) == typeof(123) ? + Object.keys(this.url_history).reverse().slice(url)[0] + : url + + if(url && this.url_history[url] && this.url_history[url].open){ + var open = this.url_history[url].open + + if(open instanceof Function){ + return open(url) + + } else { + return this[open](url) + } + } + }], + clearURLHistory: ['', + function(){ this.url_history = null }], +}) + + +var URLHistory = +module.URLHistory = ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'url-history', + + actions: URLHistoryActions, +}) + + +//--------------------------------------------------------------------- + +var URLHistoryLocalStorageActions = actions.Actions({ + config: { + 'url-history-local-storage-key': 'url-history', + }, + + __url_history: null, + + // load url history... + get url_history(){ + // get the attr value... + if(this.hasOwnProperty('__url_history') && this.__url_history){ + return this.__url_history + } + + var key = this.config['url-history-local-storage-key'] + if(key){ + // get the storage value... + // if not local __url_history and we are configured, load from storage... + if(this.config && key){ + var history = localStorage[key] + if(history){ + this.__url_history = JSON.parse(history) + } + } + } + + return this.hasOwnProperty('__url_history') ? this.__url_history : null + }, + set url_history(value){ + this.__url_history = value + + var key = this.config['url-history-local-storage-key'] + if(key){ + localStorage[key] = JSON.stringify(value) + } + }, + + + // Disable localStorage in child... + clone: [function(){ + return function(res){ + res.config['url-history-local-storage-key'] = null + } + }], + + saveURLHistory: ['', + function(){ + var key = this.config['url-history-local-storage-key'] + + if(key == null){ + return + } + + localStorage[key] = + JSON.stringify(this.url_history) + }], +}) + +var URLHistoryLocalStorage = +module.URLHistoryLocalStorage = ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'url-history-local-storage', + depends: [ + 'url-history', + ], + + actions: URLHistoryLocalStorageActions, + + // NOTE: loading is done by the .url_history prop... + handlers: [ + // save... + ['pushURLToHistory dropURLFromHistory', + function(){ + this.saveURLHistory() + }], + // clear... + ['clearURLHistory.pre', + function(){ + delete this.__url_history + + var key = this.config['url-history-local-storage-key'] + if(key){ + delete localStorage[this.config['url-history-local-storage-key']] + } + }], + ], +}) + + +//--------------------------------------------------------------------- + +var URLHistoryUIActions = actions.Actions({ + // XXX BUG: when running from action menu this breaks... + // ...possibly connected with restoring after .preventClosing(..) + // XXX need to highlight/select current... + // XXX need to check items... + listURLHistory: ['File/History', + function(){ + var that = this + var parent = this.preventClosing ? this.preventClosing() : null + var cur = this.base_path + + var o = overlay.Overlay(this.ribbons.viewer, + browse.makeList( + null, + Object.keys(this.url_history).reverse(), + // XXX for some reason this is not selected... + cur) + .open(function(evt, path){ + o.close() + + // close the parent ui... + parent + && parent.close + && parent.close() + + that.openURLFromHistory(path) + })) + .close(function(){ + parent + && parent.focus + && parent.focus() + }) + + // XXX HACK: for some reason arg 3 in the constructor has + // no effect... + cur && o.client + .select(cur) + .addClass('highlighted') + }], +}) + +var URLHistoryUI = +module.URLHistoryUI = ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'ui-url-history', + depends: [ + 'ui', + 'url-history', + ], + + actions: URLHistoryUIActions, +}) + + + +//--------------------------------------------------------------------- + +var pushToHistory = function(action){ + return [action, + function(_, path){ + if(path){ + this.pushURLToHistory(normalizePath(path), action) + } + }] +} + +// XXX add path checking... +var FileSystemURLHistory = +module.FileSystemLoaderURLHistory = ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'fs-url-history', + depends: [ + 'fs-loader', + 'url-history', + ], + + handlers: [ + pushToHistory('loadImages'), + pushToHistory('loadIndex'), + pushToHistory('loadPath'), + //pushToHistory('loadNewImages'), + ], +}) + + + //--------------------------------------------------------------------- // fs writer... @@ -3876,7 +4211,7 @@ var FileSystemWriterActions = actions.Actions({ clone: [function(full){ return function(res){ res.changes = null - if(full && Object.hasOwnProperty(this, 'changes') && this.changes){ + if(full && this.hasOwnProperty('changes') && this.changes){ res.changes = JSON.parse(JSON.stringify(this.changes)) } } @@ -3927,7 +4262,7 @@ var FileSystemWriterActions = actions.Actions({ function(json, full){ json = json || this.json('base') var changes = full ? null - : Object.hasOwnProperty(this, 'changes') ? this.changes + : this.hasOwnProperty('changes') ? this.changes : null return { raw: json, @@ -4121,7 +4456,7 @@ module.FileSystemWriter = ImageGridFeatures.Feature({ ].join(' '), function(_, target){ var changes = this.changes = - Object.hasOwnProperty(this, 'changes') ? + this.hasOwnProperty('changes') ? this.changes : {} @@ -4137,7 +4472,7 @@ module.FileSystemWriter = ImageGridFeatures.Feature({ ].join(' '), function(_, target){ var changes = this.changes = - Object.hasOwnProperty(this, 'changes') ? + this.hasOwnProperty('changes') ? this.changes : {} var images = changes.images = changes.images || [] @@ -4151,7 +4486,7 @@ module.FileSystemWriter = ImageGridFeatures.Feature({ ['tag untag', function(_, tags, gids){ var changes = this.changes = - Object.hasOwnProperty(this, 'changes') ? + this.hasOwnProperty('changes') ? this.changes : {} var images = changes.images = changes.images || [] @@ -4199,10 +4534,10 @@ module.FileSystemWriterUI = ImageGridFeatures.Feature({ title: '', doc: '', - tag: 'fs-writer-ui', + tag: 'ui-fs-writer', depends: [ 'fs-writer', - 'fs-loader-ui', + 'ui-fs-loader', ], actions: FileSystemWriterUIActions, @@ -4235,11 +4570,14 @@ ImageGridFeatures.Feature('viewer-testing', [ 'image-marks', 'image-bookmarks', + 'url-history-local-storage', + 'fs-loader', - 'fs-loader-ui', + 'ui-fs-loader', + 'fs-url-history', 'fs-writer', - 'fs-writer-ui', + 'ui-fs-writer', 'app-control', @@ -4253,6 +4591,7 @@ ImageGridFeatures.Feature('viewer-testing', [ 'ui-image-state-indicator', 'ui-global-state-indicator', 'ui-action-tree', + 'ui-url-history', // experimental and optional features... //'auto-single-image',