moved sort to a separate module + some refactoring...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2016-04-29 17:59:17 +03:00
parent 7a7641c2fc
commit 8276ea7689
5 changed files with 464 additions and 415 deletions

View File

@ -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')

View File

@ -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:
// {
// <method-name>: 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(<method>)
// .sortImages(<method>, <reverse>)
//
// .sortImages('<method> ..')
// .sortImages('<method> ..', <reverse>)
//
// .sortImages([<method>, ..])
// .sortImages([<method>, ..], <reverse>)
// NOTE: <method> 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<p.length; i++){
obj = obj[p[i]]
if(obj === undefined){
return null
}
}
return obj
}
return function(a, b){
a = _get(this.images[a])
b = _get(this.images[b])
if(a == b){
return 0
} else if(a < b){
return -1
} else {
return +1
}
}})()
})
// prepare the cmp function...
var cmp = method.length == 1 ?
method[0]
// chain compare -- return first non equal (0) result...
: function(a, b){
var res = 0
for(var i=0; i < method.length; i++){
res = method[i].call(that, a, b)
if(res != 0){
return res
}
}
return res
}
// do the sort (in place)...
if(method && method.length > 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',
])

460
ui (gen4)/features/sort.js Executable file
View File

@ -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:
// {
// <method-name>: 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(<method>)
// .sortImages(<method>, <reverse>)
//
// .sortImages('<method> ..')
// .sortImages('<method> ..', <reverse>)
//
// .sortImages([<method>, ..])
// .sortImages([<method>, ..], <reverse>)
// NOTE: <method> 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<p.length; i++){
obj = obj[p[i]]
if(obj === undefined){
return null
}
}
return obj
}
return function(a, b){
a = _get(this.images[a])
b = _get(this.images[b])
if(a == b){
return 0
} else if(a < b){
return -1
} else {
return +1
}
}})()
})
// prepare the cmp function...
var cmp = method.length == 1 ?
method[0]
// chain compare -- return first non equal (0) result...
: function(a, b){
var res = 0
for(var i=0; i < method.length; i++){
res = method[i].call(that, a, b)
if(res != 0){
return res
}
}
return res
}
// do the sort (in place)...
if(method && method.length > 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 })

View File

@ -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

View File

@ -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