From 8276ea76894cf88eca7c4720049f236c92a731dd Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Fri, 29 Apr 2016 17:59:17 +0300 Subject: [PATCH] moved sort to a separate module + some refactoring... Signed-off-by: Alex A. Naanou --- ui (gen4)/features/all.js | 1 + ui (gen4)/features/base.js | 320 +----------------- ui (gen4)/features/sort.js | 460 ++++++++++++++++++++++++++ ui (gen4)/features/ui-single-image.js | 3 +- ui (gen4)/features/ui.js | 95 +----- 5 files changed, 464 insertions(+), 415 deletions(-) create mode 100755 ui (gen4)/features/sort.js diff --git a/ui (gen4)/features/all.js b/ui (gen4)/features/all.js index da32c3fe..d4f27560 100755 --- a/ui (gen4)/features/all.js +++ b/ui (gen4)/features/all.js @@ -12,6 +12,7 @@ define(function(require){ var module = {} var core = require('features/core') require('features/base') +require('features/sort') require('features/location') require('features/history') require('features/app') diff --git a/ui (gen4)/features/base.js b/ui (gen4)/features/base.js index 9eb97be8..4021b25e 100755 --- a/ui (gen4)/features/base.js +++ b/ui (gen4)/features/base.js @@ -552,325 +552,6 @@ module.Base = core.ImageGridFeatures.Feature({ }) - -//--------------------------------------------------------------------- -// Sort... - -var SortActions = -module.SortActions = actions.Actions({ - config: { - // this can be: - // - sort mode name - as set in .config['sort-mode'] key - // Example: 'Date' - // - explicit sort method - as set in .config['sort-mode'] value - // Example: 'metadata.createDate birthtime' - 'default-sort': 'Date', - - // Format: - // The value is a space separated string of methods. - // A method is either a sort method defined in .__sort_methods__ - // or a dot-separated image attribute path. - // - // NOTE: 'Date' is descending by default - // NOTE: .toggleImageSort('?') may also show 'Manual' when - // .data.manual_order is present. - // NOTE: 'Manual' mode is set after .shiftImageLeft(..)/.shiftImageRight(..) - // are called or when restoring a pre-existing .data.manual_order - // via .toggleImageSort('Manual') - // - // XXX need a natural way to reverse these... - 'sort-methods': { - 'none': '', - // NOTE: this is descending by default... - 'Date': 'metadata.createDate birthtime reverse', - 'File date': 'birthtime reverse', - 'Name (XP-style)': 'name-leading-sequence name path', - 'File sequence number': 'name-sequence name path', - 'Name': 'name path', - // XXX sequence number with overflow... - //'File sequence number with overflow': 'name-leading-sequence name path', - }, - }, - - // Custom sort methods... - // - // Format: - // { - // : function(a, b){ ... }, - // ... - // } - // - // NOTE: the cmp function is called in the actions context. - // - // XXX sequence number with overflow... - __sort_methods__: { - 'name-leading-sequence': function(a, b){ - a = this.images.getImageNameLeadingSeq(a) - a = typeof(a) == typeof('str') ? 0 : a - b = this.images.getImageNameLeadingSeq(b) - b = typeof(b) == typeof('str') ? 0 : b - - return a - b - }, - 'name-sequence': function(a, b){ - a = this.images.getImageNameSeq(a) - a = typeof(a) == typeof('str') ? 0 : a - b = this.images.getImageNameSeq(b) - b = typeof(b) == typeof('str') ? 0 : b - - return a - b - }, - }, - // Sort using the default sort method - // .sortImages() - // NOTE: the actual sort method used is set via - // .config['default-sort'] - // - // Sort using a specific method(s): - // .sortImages() - // .sortImages(, ) - // - // .sortImages(' ..') - // .sortImages(' ..', ) - // - // .sortImages([, ..]) - // .sortImages([, ..], ) - // NOTE: can either be one of: - // 1) method name (key) from .config['sort-methods'] - // 2) a space separated string of methods or attribute paths - // as in .config['sort-methods']'s values. - // for more info se doc for: .config['sort-methods'] - // NOTE: if it is needed to reverse the method by default just - // add 'reverse' to it's string. - // - // Update current sort order: - // .sortImages('update') - // NOTE: unless the sort order (.data.order) is changed manually - // this will have no effect. - // NOTE: this is designed to facilitate manual sorting of - // .data.order - // - // Reverse image order: - // .sortImages('reverse') - // - // - // NOTE: reverse is calculated by oddity -- if an odd number indicated - // then the result is reversed, otherwise it is not. - // e.g. adding: - // 'metadata.createDate birthtime' + ' reverse' - // will reverse the result's order while: - // 'metadata.createDate birthtime reverse' + ' reverese' - // will cancel reversal. - // NOTE: with empty images this will not do anything. - // - // XXX cache order??? - // XXX would be nice to be able to sort a list of gids or a section - // of images... - // XXX sorting with partial images will throw the images that do not - // exist or the ones that do not have the right attrs all over - // the place... - sortImages: ['- Edit|Sort/Sort images', - function(method, reverse){ - var that = this - - if(method == 'reverse'){ - method = 'update' - reverse = true - } - - reverse = reverse == null ? false - : reverse == 'reverse' - || reverse - - // special case: 'update' - method = method == 'update' ? [] : method - - // defaults... - method = method - || this.config['sort-methods'][this.config['default-sort']] - || this.config['default-sort'] - || 'birthtime' - - // expand method names... - // XXX should this be recursive??? - method = typeof(method) == typeof('str') ? - method - .split(/ +/g) - .map(function(m){ - return that.config['sort-methods'][m] || m }) - .join(' ') - : method - method = typeof(method) == typeof('str') ? method.split(/ +/g) : method - - // get the reverse arity... - var i = method.indexOf('reverse') - while(i >=0){ - reverse = !reverse - - method.splice(i, 1) - i = method.indexOf('reverse') - } - - // can't sort if we know nothing about .images - if(method && method.length > 0 && (!this.images || this.images.length == 0)){ - return - } - - // build the compare routine... - method = method - // remove duplicate methods... - .unique() - .map(function(m){ - return SortActions.__sort_methods__[m] - || (that.__sort_methods__ && that.__sort_methods__[m]) - // sort by attr path... - || (function(){ - var p = m.split(/\./g) - var _get = function(obj){ - if(obj == null){ - return null - } - for(var i=0; i 0 && this.images){ - this.data.order = this.data.order.slice() - reverse ? - this.data.order.sort(cmp.bind(this)).reverse() - : this.data.order.sort(cmp.bind(this)) - - // just reverse... - } else if(method.length <= 0 && reverse) { - this.data.order.reverse() - } - - this.data.updateImagePositions() - }], - - // XXX should this be a dialog with ability to edit modes??? - // - toggle reverse sort - // XXX currently this will not toggle past 'none' - toggleImageSort: ['- Edit|Sort/Toggle image sort method', - toggler.Toggler(null, - function(){ return this.data.sort_method || 'none' }, - function(){ - return Object.keys(this.config['sort-methods']) - .concat((this.data.manual_order - || this.data.sort_method == 'Manual') ? ['Manual'] : [])}, - // prevent setting 'none' as mode... - function(mode){ - return !!this.images - && (mode != 'none' - || (mode == 'Manual' && this.data.manual_order)) }, - // XXX need to refactor the toggler a bit to make the - // signature simpler... - function(mode, _, reverse){ - reverse = reverse == 'reverse' || reverse - - // save manual order... - if(this.data.sort_method == 'Manual'){ - this.data.manual_order = this.data.order.slice() - } - - // special case: manual order... - // XXX this does not use .sortImages(..) thus this does not update... - if(mode == 'Manual'){ - this.data.order = this.data.manual_order.slice() - this.sortImages('update' + (reverse ? ' reverse' : '')) - - } else { - this.sortImages(mode + (reverse ? ' reverse' : '')) - } - - this.data.sort_method = mode - })], - - // Store/load sort data: - // .data.sort_method - current sort mode (optional) - // .data.manual_order - manual sort order (optional) - load: [function(data){ - return function(){ - if(data.data && data.data.sort_method){ - this.data.sort_method = data.data.sort_method - } - - if(data.data && data.data.manual_order){ - this.data.manual_order = data.data.manual_order - } - } - }], - json: [function(){ - return function(res){ - if(this.data.sort_method){ - res.data.sort_method = this.data.sort_method - } - - if(this.data.manual_order){ - res.data.manual_order = this.data.manual_order - - } else if(this.toggleImageSort('?') == 'Manual'){ - res.data.manual_order = this.data.order - } - } - }], -}) - -var Sort = -module.Sort = core.ImageGridFeatures.Feature({ - title: '', - - tag: 'sort', - depends: [ - 'base', - ], - - actions: SortActions, - - handlers: [ - ['shiftImageRight shiftImageLeft', - function(){ - this.data.sort_method = 'Manual' - }], - ], -}) - - - //--------------------------------------------------------------------- // Tags... @@ -1250,6 +931,7 @@ core.ImageGridFeatures.Feature('base-full', [ 'sort', 'crop', 'image-group', + 'tasks', ]) diff --git a/ui (gen4)/features/sort.js b/ui (gen4)/features/sort.js new file mode 100755 index 00000000..c7478003 --- /dev/null +++ b/ui (gen4)/features/sort.js @@ -0,0 +1,460 @@ +/********************************************************************** +* +* +* +**********************************************************************/ + +define(function(require){ var module = {} + +//var DEBUG = DEBUG != null ? DEBUG : true + +var actions = require('lib/actions') +var features = require('lib/features') +var toggler = require('lib/toggler') + +var core = require('features/core') + +var overlay = require('lib/widget/overlay') +var browse = require('lib/widget/browse') + + + +/*********************************************************************/ + +var SortActions = +module.SortActions = actions.Actions({ + config: { + // Default sort method... + // + // this can be: + // - sort mode name - as set in .config['sort-mode'] key + // Example: 'Date' + // - explicit sort method - as set in .config['sort-mode'] value + // Example: 'metadata.createDate birthtime' + 'default-sort': 'Date', + + // Default sort order... + // + // can be: 'default', 'reverse') + 'default-sort-order': 'default', + + // Sort methods... + // + // Format: + // The value is a space separated string of methods. + // A method is either a sort method defined in .__sort_methods__ + // or a dot-separated image attribute path. + // + // NOTE: 'Date' is descending by default + // NOTE: .toggleImageSort('?') may also show 'Manual' when + // .data.manual_order is present. + // NOTE: 'Manual' mode is set after .shiftImageLeft(..)/.shiftImageRight(..) + // are called or when restoring a pre-existing .data.manual_order + // via .toggleImageSort('Manual') + // + // XXX need a natural way to reverse these... + 'sort-methods': { + 'none': '', + // NOTE: this is descending by default... + 'Date': 'metadata.createDate birthtime reverse', + 'File date': 'birthtime reverse', + 'Name (XP-style)': 'name-leading-sequence name path', + 'File sequence number': 'name-sequence name path', + 'Name': 'name path', + // XXX sequence number with overflow... + //'File sequence number with overflow': 'name-leading-sequence name path', + }, + }, + + toggleDefaultSortOrder: ['- Edit|Sort/Default sort order', + core.makeConfigToggler('default-sort-order', ['default', 'reverse'])], + + // Custom sort methods... + // + // Format: + // { + // : function(a, b){ ... }, + // ... + // } + // + // NOTE: the cmp function is called in the actions context. + // + // XXX add sequence number with overflow... + __sort_methods__: { + 'name-leading-sequence': function(a, b){ + a = this.images.getImageNameLeadingSeq(a) + a = typeof(a) == typeof('str') ? 0 : a + b = this.images.getImageNameLeadingSeq(b) + b = typeof(b) == typeof('str') ? 0 : b + + return a - b + }, + 'name-sequence': function(a, b){ + a = this.images.getImageNameSeq(a) + a = typeof(a) == typeof('str') ? 0 : a + b = this.images.getImageNameSeq(b) + b = typeof(b) == typeof('str') ? 0 : b + + return a - b + }, + }, + // Sort images... + // + // Sort using the default sort method + // .sortImages() + // NOTE: the actual sort method used is set via + // .config['default-sort'] and .config['default-sort-order'] + // + // Sort using a specific method(s): + // .sortImages() + // .sortImages(, ) + // + // .sortImages(' ..') + // .sortImages(' ..', ) + // + // .sortImages([, ..]) + // .sortImages([, ..], ) + // NOTE: can either be one of: + // 1) method name (key) from .config['sort-methods'] + // 2) a space separated string of methods or attribute paths + // as in .config['sort-methods']'s values. + // for more info se doc for: .config['sort-methods'] + // NOTE: if it is needed to reverse the method by default just + // add 'reverse' to it's string. + // + // Update current sort order: + // .sortImages('update') + // NOTE: unless the sort order (.data.order) is changed manually + // this will have no effect. + // NOTE: this is designed to facilitate manual sorting of + // .data.order + // + // Reverse image order: + // .sortImages('reverse') + // + // + // NOTE: reverse is calculated by oddity -- if an odd number indicated + // then the result is reversed, otherwise it is not. + // e.g. adding: + // 'metadata.createDate birthtime' + ' reverse' + // will reverse the result's order while: + // 'metadata.createDate birthtime reverse' + ' reverese' + // will cancel reversal. + // NOTE: with empty images this will not do anything. + // + // XXX would be nice to be able to sort a list of gids or a section + // of images... + // XXX sorting with partial images will throw the images that do not + // exist or the ones that do not have the right attrs all over + // the place... + // ...should we pool the "unknowns" to one side with their + // current order?? + // XXX should this handle manual sort order??? + sortImages: ['- Edit|Sort/Sort images', + function(method, reverse){ + var that = this + + if(method == 'reverse'){ + method = 'update' + reverse = true + } + + reverse = reverse == null ? false + : reverse == 'reverse' + || reverse + + // special case: 'update' + method = method == 'update' ? [] : method + + // defaults... + method = method + || ((this.config['default-sort'] || 'birthtime') + + (this.config['default-sort-order'] == 'reverse' ? ' reverse' : '')) + + // set sort method in data... + this.data.sort_method = typeof(method) == typeof('str') ? method : method.join(' ') + + // expand method names... + // XXX should this be recursive??? + method = typeof(method) == typeof('str') ? + method + .split(/ +/g) + .map(function(m){ + return that.config['sort-methods'][m] || m }) + .join(' ') + : method + method = typeof(method) == typeof('str') ? method.split(/ +/g) : method + + // get the reverse arity... + var i = method.indexOf('reverse') + while(i >=0){ + reverse = !reverse + + method.splice(i, 1) + i = method.indexOf('reverse') + } + + // can't sort if we know nothing about .images + if(method && method.length > 0 && (!this.images || this.images.length == 0)){ + return + } + + // build the compare routine... + method = method + // remove duplicate methods... + .unique() + .map(function(m){ + return SortActions.__sort_methods__[m] + || (that.__sort_methods__ && that.__sort_methods__[m]) + // sort by attr path... + || (function(){ + var p = m.split(/\./g) + var _get = function(obj){ + if(obj == null){ + return null + } + for(var i=0; i 0 && this.images){ + this.data.order = this.data.order.slice() + reverse ? + this.data.order.sort(cmp.bind(this)).reverse() + : this.data.order.sort(cmp.bind(this)) + + // just reverse... + } else if(method.length <= 0 && reverse) { + this.data.order.reverse() + } + + this.data.updateImagePositions() + }], + + // Toggle sort modes... + // + // This is similar to sort images but it will also maintain + // .data.manual_order state. + // + // NOTE: a state can be passed appended with reverse, e.g. + // .toggleImageSort('Date') and .toggleImageSort('Date reverse') + // both will set the sort method to 'Date' but the later will + // also reverse it. + // + // XXX should we merge manual order handling with .sortImages(..)??? + // XXX currently this will not toggle past 'none' + toggleImageSort: ['- Edit|Sort/Toggle image sort method', + toggler.Toggler(null, + function(){ return (this.data && this.data.sort_method) || 'none' }, + function(){ + return Object.keys(this.config['sort-methods']) + .concat((this.data + && (this.data.manual_order + || this.data.sort_method == 'Manual')) ? + ['Manual'] + : [])}, + // prevent setting 'none' as mode... + function(mode){ + return !!this.images + && (mode != 'none' + || (mode == 'Manual' && this.data.manual_order)) }, + // XXX need to refactor the toggler a bit to make the + // signature simpler... (???) + function(mode, _, reverse){ + reverse = reverse == 'reverse' || reverse + + // save manual order... + if(this.data.sort_method == 'Manual'){ + this.data.manual_order = this.data.order.slice() + } + + // special case: manual order... + if(mode == 'Manual'){ + this.data.order = this.data.manual_order.slice() + this.sortImages('update' + (reverse ? ' reverse' : '')) + this.data.sort_method = mode + + } else { + this.sortImages(mode + (reverse ? ' reverse' : '')) + } + })], + + // Store/load sort data: + // .data.sort_method - current sort mode (optional) + // .data.manual_order - manual sort order (optional) + load: [function(data){ + return function(){ + if(data.data && data.data.sort_method){ + this.data.sort_method = data.data.sort_method + } + + if(data.data && data.data.manual_order){ + this.data.manual_order = data.data.manual_order + } + } + }], + json: [function(){ + return function(res){ + if(this.data.sort_method){ + res.data.sort_method = this.data.sort_method + } + + if(this.data.manual_order){ + res.data.manual_order = this.data.manual_order + + } else if(this.toggleImageSort('?') == 'Manual'){ + res.data.manual_order = this.data.order + } + } + }], +}) + +var Sort = +module.Sort = core.ImageGridFeatures.Feature({ + title: '', + + tag: 'sort', + depends: [ + 'base', + ], + suggested: [ + 'ui-sort', + ], + + actions: SortActions, + + handlers: [ + ['shiftImageRight shiftImageLeft', + function(){ + this.data.sort_method = 'Manual' + }], + ], +}) + + + +//--------------------------------------------------------------------- + +var SortUIActions = actions.Actions({ + // XXX should we be able to edit modes??? + sortDialog: ['Edit|Sort/Sort images...', + function(){ + var that = this + + var dfl = this.config['default-sort'] + + // XXX might be a good idea to make this generic... + var _makeTogglHandler = function(toggler){ + return function(){ + var txt = $(this).find('.text').first().text() + that[toggler]() + o.client.update() + .then(function(){ o.client.select(txt) }) + that.toggleSlideshow('?') == 'on' + && o.close() + } + } + + var o = overlay.Overlay(this.ribbons.viewer, + browse.makeLister(null, function(path, make){ + var cur = that.toggleImageSort('?') + + that.toggleImageSort('??').forEach(function(mode){ + // skip 'none'... + if(mode == 'none'){ + return + } + make(mode) + .on('open', function(){ + that.toggleImageSort(null, mode, + that.config['default-sort-order'] == 'reverse') + o.close() + }) + .addClass(mode == cur ? 'highlighted' : '') + .addClass(mode == dfl ? 'default' : '') + }) + + // Commands... + make('---') + + make('Reverse images') + .on('open', function(){ + that.reverseImages() + o.close() + }) + /* + make('Reverse ribbons') + .on('open', function(){ + that.reverseRibbons() + o.close() + }) + */ + + // Settings... + make('---') + + make(['Default order: ', that.config['default-sort-order'] || 'ascending']) + .on('open', _makeTogglHandler('toggleDefaultSortOrder')) + .addClass('item-value-view') + })) + + // select the current order... + o.client.select('"' + this.toggleImageSort('?') + '"') + + return o + }] +}) + +var SortUI = +module.SortUI = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'ui-sort', + depends: [ + 'ui', + ], + + actions: SortUIActions, +}) + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ +return module }) diff --git a/ui (gen4)/features/ui-single-image.js b/ui (gen4)/features/ui-single-image.js index 4f680999..596fd1fc 100755 --- a/ui (gen4)/features/ui-single-image.js +++ b/ui (gen4)/features/ui-single-image.js @@ -69,13 +69,12 @@ var core = require('features/core') // . | | . - drag enabled (XXX not implemented) // . | | . - next/prev image keeps drag position // . +---------------+ . -// . . +// . . - use tiles instead very large images (XXX ???) // + - - - - - - - - - + // // // NOTE: this in part does the same job as .ribbons.correctImageProportionsForRotation(..) // -// XXX might be a good idea to use tiles for zoomed in images... // XXX should this be an action??? function updateImageProportions(){ var that = this diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js index 5f65cd4e..629bc39d 100755 --- a/ui (gen4)/features/ui.js +++ b/ui (gen4)/features/ui.js @@ -19,9 +19,6 @@ var ribbons = require('ribbons') var core = require('features/core') var base = require('features/base') -var overlay = require('lib/widget/overlay') -var browse = require('lib/widget/browse') - /*********************************************************************/ @@ -336,6 +333,7 @@ module.ViewerActions = actions.Actions({ // ...navigate by proximity (closest to center) rather than by // order... // XXX skip off-screen ribbons (???) + // XXX should the timeout be configurable??? alignByOrder: ['Interface/Align ribbons by image order', function(target, scale, now){ if(target == 'now'){ @@ -753,10 +751,6 @@ module.Viewer = core.ImageGridFeatures.Feature({ 'base', 'workspace', ], - suggested: [ - 'ui-sort', - // XXX add base ui features here... - ], actions: ViewerActions, @@ -820,93 +814,6 @@ module.Viewer = core.ImageGridFeatures.Feature({ /*********************************************************************/ // User interfaces for different base features... -var SortUIActions = actions.Actions({ - config: { - 'default-sort-order': 'default', - }, - - toggleDefaultSortOrder: ['- Edit|Sort/Default sort order', - core.makeConfigToggler('default-sort-order', ['default', 'reverse'])], - sortDialog: ['Edit|Sort/Sort images...', - function(){ - var that = this - - // XXX might be a good idea to make this generic... - var _makeTogglHandler = function(toggler){ - return function(){ - var txt = $(this).find('.text').first().text() - that[toggler]() - o.client.update() - .then(function(){ o.client.select(txt) }) - that.toggleSlideshow('?') == 'on' - && o.close() - } - } - - var o = overlay.Overlay(this.ribbons.viewer, - browse.makeLister(null, function(path, make){ - var cur = that.toggleImageSort('?') - - that.toggleImageSort('??').forEach(function(mode){ - // skip 'none'... - if(mode == 'none'){ - return - } - make(mode + (cur == mode ? ' *' : '')) - .on('open', function(){ - that.toggleImageSort(null, mode, - that.config['default-sort-order'] == 'reverse') - o.close() - }) - }) - - // Commands... - make('---') - - make('Reverse images') - .on('open', function(){ - that.reverseImages() - o.close() - }) - /* - make('Reverse ribbons') - .on('open', function(){ - that.reverseRibbons() - o.close() - }) - */ - - // Settings... - make('---') - - make(['Default order: ', that.config['default-sort-order'] || 'ascending']) - .on('open', _makeTogglHandler('toggleDefaultSortOrder')) - .addClass('item-value-view') - })) - - // select the current order... - o.client.select('"' + this.toggleImageSort('?') + ' *"') - - return o - }] -}) - -var SortUI = -module.SortUI = core.ImageGridFeatures.Feature({ - title: '', - doc: '', - - tag: 'ui-sort', - depends: [ - 'ui', - ], - - actions: SortUIActions, -}) - - - -//--------------------------------------------------------------------- // XXX tag dialogs... // XXX