/********************************************************************** * * * **********************************************************************/ 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 widgets = require('features/ui-widgets') 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 keep-position reverse', 'File date': 'birthtime keep-position reverse', 'Name (XP-style)': 'name-leading-sequence name path keep-position', 'File sequence number': 'name-sequence name path keep-position', 'Name': 'name path keep-position', // 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 }, // this will sort items via their index... 'keep-position': function(a, b){ a = this.data.order.indexOf(a) b = this.data.order.indexOf(b) return a - b }, 'dummy': function(){ return 0 }, }, // 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: if a sort method name contains a space it must be quoted either // in '"'s or in "'"s. // 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(/'([^']*)'|"([^"]*)"| +/) .filter(function(e){ return e && e.trim() != '' && !/['"]/.test(e) }) .map(function(m){ return that.config['sort-methods'][m] || m }) .join(' ') : method method = typeof(method) == typeof('str') ? method.split(/'([^']*)'|"([^"]*)"| +/) .filter(function(e){ return e && e.trim() != '' && !/['"]/.test(e) }) : 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 = reverse ? this.data.order.slice().sort(cmp.bind(this)).reverse() : this.data.order.slice().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 && (this.data.sort_method .split(/'([^']*)'|"([^"]*)"| +/) .filter(function(e){ return e && e.trim() != '' && !/['"]/.test(e) })[0])) || '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...', widgets.makeUIDialog(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.update() .then(function(){ o.select(txt) }) that.toggleSlideshow('?') == 'on' && o.parent.close() } } var o = browse.makeLister(null, function(path, make){ var lister = this 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') lister.parent.close() }) .addClass(mode == cur ? 'highlighted selected' : '') .addClass(mode == dfl ? 'default' : '') }) // Commands... make('---') make('Reverse images') .on('open', function(){ that.reverseImages() lister.parent.close() }) /* make('Reverse ribbons') .on('open', function(){ that.reverseRibbons() lister.parent.close() }) */ // Settings... make('---') make(['Default order: ', that.config['default-sort-order'] || 'ascending']) .on('open', _makeTogglHandler('toggleDefaultSortOrder')) .addClass('item-value-view') }) 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 })