diff --git a/ui (gen4)/features/filesystem.js b/ui (gen4)/features/filesystem.js index 138dc663..2f76ae69 100755 --- a/ui (gen4)/features/filesystem.js +++ b/ui (gen4)/features/filesystem.js @@ -46,9 +46,11 @@ if(typeof(process) != 'undefined'){ /*********************************************************************/ -// fs reader/loader... +// Loader... +// NOTE: this will also manage .location.from +// // XXX revise base path mechanics... // .loaded_paths var FileSystemLoaderActions = actions.Actions({ @@ -89,20 +91,11 @@ var FileSystemLoaderActions = actions.Actions({ } }], - // XXX is this a hack??? // XXX need a more generic form... checkPath: ['- File/', function(path){ return fse.existsSync(path) }], - - loadSaveHistoryList: ['- File/', - function(path){ - path = path || this.location.path - - return file.loadSaveHistoryList(path) - }], - // NOTE: when passed no path this will not do anything... // NOTE: this will add a .from field to .location, this will indicate // the date starting from which saves are loaded. @@ -414,17 +407,37 @@ module.FileSystemLoader = core.ImageGridFeatures.Feature({ suggested: [ 'ui-fs-loader', 'fs-url-history', + 'fs-save-history', ], actions: FileSystemLoaderActions, isApplicable: function(){ return this.runtime == 'node' || this.runtime == 'nw' }, + + handlers: [ + // save/resore .savecomments + // + ['json', + function(res){ + if(this.savecomments != null){ + res.savecomments = JSON.parse(JSON.stringify(this.savecomments)) + } + }], + ['load', + function(_, data){ + if(data.savecomments != null){ + this.savecomments = data.savecomments + } + }], + + ], }) //--------------------------------------------------------------------- +// Loader UI... // XXX would need to delay the original action while the user is // browsing... @@ -473,70 +486,8 @@ var FileSystemLoaderUIActions = actions.Actions({ showNonTraversable: true, showDisabled: true, }, - - // if set true, if unsaved changes present when opening a save - // history state, save the changes... - 'auto-save-on-save-history-open': true, }, - // Save comments... - // - // Format: - // { - // // comment staged for next .saveIndex(..)... - // 'current': , - // - // : , - // ... - // } - savecomments: null, - - - // Comment a save... - // - // Comment current save... - // .setSaveComment(comment) - // -> actions - // - // Reset current save comment... - // .setSaveComment(null) - // -> actions - // - // Comment specific save... - // .setSaveComment(save, comment) - // -> actions - // - // Reset specific save comment... - // .setSaveComment(save, null) - // -> actions - // - // NOTE: "save" is the save format as returned by file.groupByDate(..), - // or .loadSaveHistoryList(..) - // ...normally it is Date.timeStamp() compatible string. - setSaveComment: ['- File/Comment a save', - function(save, comment){ - var comments = this.savecomments = this.savecomments || {} - - // no explicit save given -- stage a comment for next save... - if(comment === undefined){ - comment = save - save = 'current' - } - - if(comment === undefined){ - return - - } else if(comment == null){ - delete comments[save] - - } else { - comments[save] = comment - } - - this.markChanged('savecomments') - }], - - // FS browser... // // XXX should the loader list be nested or open in overlay (as-is now)??? @@ -664,57 +615,220 @@ var FileSystemLoaderUIActions = actions.Actions({ return o })], +}) + + +// XXX is this a good name??? +var FileSystemLoaderUI = +module.FileSystemLoaderUI = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'ui-fs-loader', + depends: [ + 'ui', + 'fs-loader' + ], + + actions: FileSystemLoaderUIActions, +}) + + + +//--------------------------------------------------------------------- +// Save History... + +var FileSystemSaveHistoryActions = actions.Actions({ + // Save comments... + // + // Format: + // { + // // comment staged for next .saveIndex(..)... + // 'current': , + // + // : , + // ... + // } + savecomments: null, + + // Comment a save... + // + // Comment current save... + // .setSaveComment(comment) + // -> actions + // + // Reset current save comment... + // .setSaveComment(null) + // -> actions + // + // Comment specific save... + // .setSaveComment(save, comment) + // -> actions + // + // Reset specific save comment... + // .setSaveComment(save, null) + // -> actions + // + // NOTE: "save" is the save format as returned by file.groupByDate(..), + // or .loadSaveHistoryList(..) + // ...normally it is Date.timeStamp() compatible string. + setSaveComment: ['- File/Comment a save', + function(save, comment){ + var comments = this.savecomments = this.savecomments || {} + + // no explicit save given -- stage a comment for next save... + if(comment === undefined){ + comment = save + save = 'current' + } + + if(comment === undefined){ + return + + } else if(comment == null){ + delete comments[save] + + } else { + comments[save] = comment + } + + this.markChanged('savecomments') + }], + + loadSaveHistoryList: ['- File/', + function(path){ + path = path || this.location.path + + return file.loadSaveHistoryList(path) + }], +}) + + +var FileSystemSaveHistory = +module.FileSystemSaveHistory = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'fs-save-history', + depends: [ + 'fs-loader' + ], + suggested: [ + 'ui-fs-save-history', + ], + + actions: FileSystemSaveHistoryActions, + + handlers: [ + // Prepare comments for writing... + // + // NOTE: defining this here enables us to actually post-bind to + // an action that is defined later or may not even be + // available. + ['prepareIndexForWrite', + function(res){ + var changed = this.changes == null + || this.changes.savecomments + + if(changed){ + var comments = res.raw.savecomments || {} + + // set the 'current' comment to the correct date... + if(comments.current){ + comments[res.date] = comments.current + delete comments.current + } + + res.prepared.savecomments = comments + } + }], + // replace .savecomments['current'] with .location.from... + // + // NOTE: this will also drop any unsaved changes from browsing + // history... + ['saveIndex', + function(res){ + var that = this + var comments = this.savecomments + + if(comments && comments.current){ + res + .then(function(){ + comments[that.location.from] = comments.current + delete comments.current + }) + } + + delete this.unsaved_index + }], + ] +}) + + + +//--------------------------------------------------------------------- +// Save History UI... + +// XXX add comment editing... +// XXX should this also list journal stuff or have the ability for extending??? +var FileSystemSaveHistoryUIActions = actions.Actions({ + // Saved original index state before loading a state from history... + // + unsaved_index: null, // List save history dialog... // - // NOTE: for multiple indexes this will show the combined history - // and selecting a postion will load all the participating - // indexes to that specific date or closest earlier state. - // NOTE: this will show no history if .location.method is not loadIndex.. - // NOTE: this will drop all unsaved changes when loading a state (XXX) - // NOTE: this will set changes to all when loading a different state + // .location.from - set to timestamp of save state when + // selecting a non-top state. + // NOTE: this may be set to last save + // state. + // .location.historic - set to true when at a non-top state. + // + // For multiple indexes this will show the combined history and + // selecting a postion will load all the participating indexes to + // that specific date or closest earlier state. + // + // Unsaved changes will be saved to .unsaved_index when switching + // from current to a historic state. + // + // NOTE: this will show no history if .location.method is not 'loadIndex'.. + // NOTE: this will set changes to all when loading a historic state // that the latest and to non otherwise.... // // XXX add comment editing... - // XXX need to handle saves (saveIndex(..) and friends) when loaded - // a specific history position... - // ...in theory saving and old index will create an incremental - // save which should not damage the history and can be fixed - // either by removing the actual .json files or simply loading - // from a previous position and re-saving... (XXX test) - // XXX should this also list journal stuff or have the ability for - // extending??? - // XXX should this save the unsaved changes when switching to a version - // or discard them (current behavior) - // ...saving might be logical... + // XXX might be a good idea to show a diff of some kind or at least + // what .changed when writing a save... listSaveHistory: ['File/History...', widgets.makeUIDialog(function(){ var that = this + var _makeTitle = function(title, date, a){ + title = [title] + date = date || 'current' + a = a || that + + var comment = a.savecomments && a.savecomments[date] + //title.push(comment || '') + comment && title.push(comment) + + // XXX is this the best format??? + return title.join(' - ') + } + var o = browse.makeLister(null, function(path, make){ var dialog = this var from = that.location.from - from = from && Date.fromTimeStamp(from).toShortDate() - if(that.changes !== false && !that.location.historic){ - var title = ['Unsaved state'] - - var comment = that.savecomments && that.savecomments['current'] - //title.push(comment || '') - comment && title.push(comment) - - // XXX is this the best format??? - title = title.join(' - ') - - make(title) + if(that.changes !== false){ + make(_makeTitle('Current state (unsaved)', 'current')) make('---') } // only search for history if we have an index loaded... if(that.location.method != 'loadIndex'){ - make('No history...', null, true) + make('No history...', {disabled: true}) // select the 'Unsaved' item... dialog.select() @@ -749,36 +863,21 @@ var FileSystemLoaderUIActions = actions.Actions({ // Special case: unsaved state... if(that.unsaved_index){ - var title = ['Unsaved state'] + var unsaved = that.unsaved_index - var comment = that.savecomments && that.savecomments['current'] - //title.push(comment || '') - comment && title.push(comment) - - // XXX is this the best format??? - title = title.join(' - ') - - make(title) + make(_makeTitle('Original state (unsaved)', 'current', unsaved)) .on('open', function(){ - var location = that.location - - that.load(that.unsaved_index) + that.load(unsaved) delete that.unsaved_index - - delete location.historic - delete location.from - - that.__location = location }) - make('---') - - // Special case: top save state is the default... - // NOTE: no need to mark anything for change... - } else { + // Special case: top save state is the default, + // no need to mark anything for change, but only + // if nothing changed... + } else if(that.changes === false){ var first = list.shift() - first && make(Date.fromTimeStamp(first).toShortDate()) + first && make(_makeTitle(Date.fromTimeStamp(first).toShortDate(), first)) .on('open', function(){ that.loadIndex(that.location.path, first) }) @@ -788,35 +887,32 @@ var FileSystemLoaderUIActions = actions.Actions({ .forEach(function(d){ var txt = Date.fromTimeStamp(d).toShortDate() - // get the save name... - var title = [txt] - var comment = that.savecomments && that.savecomments[d] - //title.push(comment || '') - comment && title.push(comment) - - // XXX is this the best format??? - title = title.join(' - ') - - make(title) + make(_makeTitle(Date.fromTimeStamp(d).toShortDate(), d)) .attr('timestamp', d) .on('open', function(){ // auto save... - if(that.config['auto-save-on-save-history-open' ] - && that.changes !== false + if(that.changes !== false && !that.location.historic){ - // XXX should we use a crop for this??? that.unsaved_index = that.json() } - // NOTE: this will drop all unsaved changes... that.loadIndex(that.location.path, d) .then(function(){ that.markChanged('all') + that.location.historic = true + + // remove 'current' comments + // from loaded state... + // + // NOTE: the original 'current' + // comment is saved to + // .unsaved_index + delete that.savecomments.current }) }) // mark the current loaded position... - .addClass(txt == from ? 'selected highlighted' : '') + .addClass(d == from ? 'selected highlighted' : '') }) make.done() @@ -836,66 +932,23 @@ var FileSystemLoaderUIActions = actions.Actions({ }) -// XXX is this a good name??? -var FileSystemLoaderUI = -module.FileSystemLoaderUI = core.ImageGridFeatures.Feature({ +var FileSystemSaveHistoryUI = +module.FileSystemSaveHistoryUI = core.ImageGridFeatures.Feature({ title: '', doc: '', - tag: 'ui-fs-loader', + tag: 'ui-fs-save-history', depends: [ 'ui', - 'fs-loader' + 'fs-save-history', ], - actions: FileSystemLoaderUIActions, + actions: FileSystemSaveHistoryUIActions, handlers: [ - // save/resore .savecomments - // - ['json', - function(res){ - if(this.savecomments != null){ - res.savecomments = JSON.parse(JSON.stringify(this.savecomments)) - } - }], - ['load', - function(_, data){ - if(data.savecomments != null){ - this.savecomments = data.savecomments - } - }], - // Prepare comments for writing... - // - // NOTE: defining this here enables us to actually post-bind to - // an action that is defined later or may not even be - // available. - ['prepareIndexForWrite', - function(res){ - var changed = this.changes == null - || this.changes.savecomments - - if(changed){ - var comments = res.raw.savecomments || {} - - // set the 'current' comment to the correct date... - if(comments.current){ - comments[res.date] = comments.current - delete comments.current - } - - res.prepared.savecomments = comments - } - }], - // replace .savecomments['current'] with .location.from... ['saveIndex', - function(){ - var comments = this.savecomments - - if(comments && comments.current){ - comments[this.location.from] = comments.current - delete comments.current - } + function(res){ + delete this.unsaved_index }], ] }) @@ -903,6 +956,7 @@ module.FileSystemLoaderUI = core.ImageGridFeatures.Feature({ //--------------------------------------------------------------------- +// URL History... var pushToHistory = function(action, to_top, checker){ return [action, @@ -945,6 +999,7 @@ module.FileSystemLoaderURLHistory = core.ImageGridFeatures.Feature({ //--------------------------------------------------------------------- +// URL History UI... // Opening the url via .browsePath(..) if url is in history will move // it to top of list... @@ -973,7 +1028,7 @@ module.FileSystemLoaderURLHistoryUI = core.ImageGridFeatures.Feature({ //--------------------------------------------------------------------- -// fs writer... +// Writer... var FileSystemWriterActions = actions.Actions({ config: { @@ -1201,19 +1256,19 @@ var FileSystemWriterActions = actions.Actions({ // XXX get real base path... //path = path || this.location.path +'/'+ this.config['index-dir'] - var indes = this.prepareIndexForWrite() + var index = this.prepareIndexForWrite() return file.writeIndex( index.prepared, // XXX should we check if index dir is present in path??? //path, path +'/'+ this.config['index-dir'], - inex.date, + index.date, this.config['index-filename-template'], logger || this.logger) .then(function(){ that.location.method = 'loadIndex' - that.location.from = date + that.location.from = index.date }) }], @@ -1492,11 +1547,11 @@ module.FileSystemWriter = core.ImageGridFeatures.Feature({ function(res, path){ // NOTE: if saving to a different path than loaded do not // drop the .changes flags... - if(path && path == this.location.path){ + if(!path || path == this.location.path){ //this.markChanged('none') var that = this res.then(function(){ - this.markChanged('none') + that.markChanged('none') }) } }], @@ -1577,11 +1632,14 @@ module.FileSystemWriter = core.ImageGridFeatures.Feature({ this.markChanged.apply(this, changes) }], + ] }) //--------------------------------------------------------------------- +// Writer UI... + // XXX add writer UI feature... // - save as.. (browser) // - save if not base path present (browser) @@ -1831,6 +1889,7 @@ core.ImageGridFeatures.Feature('fs', [ ]) + /********************************************************************** * vim:set ts=4 sw=4 : */ return module }) diff --git a/ui (gen4)/features/location.js b/ui (gen4)/features/location.js index 0d302665..99eeeb97 100755 --- a/ui (gen4)/features/location.js +++ b/ui (gen4)/features/location.js @@ -43,6 +43,7 @@ var LocationActions = actions.Actions({ // to open the dir (open parent + select current) and not // within the dir __location: null, + get location(){ this.__location = this.__location || {} @@ -71,21 +72,20 @@ var LocationActions = actions.Actions({ // got an object... } else { - var path = value.path - var method = value.method - var cur = value.current + value = JSON.parse(JSON.stringify(value)) + + var path = value.path = value.path + + value.method = value.method + value.current = value.current } // normalize path if it's not root... if(path != '/' && path != '\\'){ - path = util.normalizePath(path) + value.path = util.normalizePath(path) } - this.__location = { - path: path, - method: method, - current: cur, - } + this.__location = value var res = this[value.method || 'loadIndex'](path) @@ -108,6 +108,21 @@ module.Location = core.ImageGridFeatures.Feature({ tag: 'location', actions: LocationActions, + + handlers: [ + ['json', + function(res){ + if(this.location){ + res.location = JSON.parse(JSON.stringify(this.location)) + } + }], + ['load', + function(_, data){ + if(data.location){ + this.__location = data.location + } + }], + ], }) diff --git a/ui (gen4)/file.js b/ui (gen4)/file.js index 7f30c42d..ebbec806 100755 --- a/ui (gen4)/file.js +++ b/ui (gen4)/file.js @@ -667,6 +667,30 @@ function(){ // XXX make this merge if we locate more than one index... var buildIndex = module.buildIndex = function(index, base_path){ + var res = {} + + // we'll handle these in a special way... + var special = [ + 'data', + 'tags', + 'bookmarked', + 'marked', + 'current', + 'images', + ] + + // copy the rest as-is... + for(var k in index){ + if(special.indexOf(k) > -1){ + continue + } + + res[k] = index[k] + } + + + // now do the special stuff... + var d = data.Data.fromJSON(index.data) // buildup the data object... @@ -703,10 +727,10 @@ module.buildIndex = function(index, base_path){ img.forEach(function(_, img){ img.base_path = base_path }) } - return { - data: d, - images: img, - } + res.data = d + res.images = img + + return res }