diff --git a/ui (gen4)/features/all.js b/ui (gen4)/features/all.js index a92b960b..ba3d8745 100755 --- a/ui (gen4)/features/all.js +++ b/ui (gen4)/features/all.js @@ -26,7 +26,6 @@ require('features/ui-virtual-dom') require('features/ui-react') // XXX require('features/ui-partial-ribbons-precache') -require('features/ui-partial-ribbons') require('features/ui-partial-ribbons-2') require('features/ui-partial-ribbons-vdom') require('features/ui-single-image') diff --git a/ui (gen4)/features/meta.js b/ui (gen4)/features/meta.js index f5d220d9..dbd4e23a 100755 --- a/ui (gen4)/features/meta.js +++ b/ui (gen4)/features/meta.js @@ -71,6 +71,7 @@ core.ImageGridFeatures.Feature('viewer-testing', [ // XXX 'ui-ribbons-render', + 'ui-partial-ribbons-render', 'ui-vdom-render', //'ui-react-render', //*/ diff --git a/ui (gen4)/features/ui-partial-ribbons.js b/ui (gen4)/features/ui-partial-ribbons.js deleted file mode 100755 index 7cbc5e91..00000000 --- a/ui (gen4)/features/ui-partial-ribbons.js +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************************************** -* -* -* -**********************************************************************/ -((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) -(function(require){ var module={} // make module AMD/node compatible... -/*********************************************************************/ - -var actions = require('lib/actions') -var features = require('lib/features') - -var core = require('features/core') - - - -/*********************************************************************/ - -// NOTE: this is split out to an action so as to enable ui elements to -// adapt to ribbon size changes... -// -// XXX try using .ribbons.resizeRibbon(..) for basic tasks... -// XXX try a strategy: load more in the direction of movement by an offset... -// XXX updateRibbon(..) is not signature compatible with data.updateRibbon(..) -var PartialRibbonsActions = actions.Actions({ - config: { - // Number of screen widths to load... - 'ribbon-size-screens': 7, - - // Number of screen widths to edge to trigger reload... - 'ribbon-resize-threshold': 1.5, - - // Timeout before a non-forced ribbon size update happens after - // the action... - // NOTE: if set to null, the update will be sync... - 'ribbon-update-timeout': 120, - }, - - // NOTE: this will force sync resize if one of the following is true: - // - the target is not loaded - // - we are less than screen width from the edge - // - threshold is set to 0 - // XXX this is not signature compatible with data.updateRibbon(..) - // XXX do not do anything for off-screen ribbons... - updateRibbon: ['- Interface/Update partial ribbon size', - function(target, w, size, threshold){ - target = target instanceof jQuery - ? this.ribbons.getElemGID(target) - // NOTE: data.getImage(..) can return null at start or end - // of ribbon, thus we need to account for this... - : (this.data.getImage(target) - || this.data.getImage(target, 'after')) - - w = w || this.screenwidth - - // get config data and normalize... - size = (size - || this.config['ribbon-size-screens'] - || 5) * w - threshold = threshold == 0 ? threshold - : (threshold - || this.config['ribbon-resize-threshold'] - || 1) * w - - var timeout = this.config['ribbon-update-timeout'] - - // next/prev loaded... - var img = this.ribbons.getImage(target) - var nl = img.nextAll('.image:not(.clone)').length - var pl = img.prevAll('.image:not(.clone)').length - - // next/prev available... - // NOTE: we subtract 1 to remove the current and make these - // compatible with: nl, pl - var na = this.data.getImages(target, size, 'after').length - 1 - var pa = this.data.getImages(target, size, 'before').length - 1 - - // do the update... - // no threshold means force load... - if(threshold == 0 - // the target is not loaded... - || img.length == 0 - // passed hard threshold on the right... - || (nl < w && na > nl) - // passed hard threshold on the left... - || (pl < w && pa > pl)){ - - this.resizeRibbon(target, size) - - // do a late resize... - // loaded more than we need (crop?)... - } else if(na + pa < nl + pl - // passed threshold on the right... - || (nl < threshold && na > nl) - // passed threshold on the left... - || (pl < threshold && pa > pl) - // loaded more than we need by threshold... - || nl + pl + 1 > size + threshold){ - - return function(){ - // sync update... - if(timeout == null){ - this.resizeRibbon(target, size) - - // async update... - } else { - // XXX need to check if we are too close to the edge... - var that = this - //setTimeout(function(){ that.resizeRibbon(target, size) }, 0) - if(this.__update_timeout){ - clearTimeout(this.__update_timeout) - } - this.__update_timeout = setTimeout(function(){ - delete that.__update_timeout - that.resizeRibbon(target, size) - }, timeout) - } - } - } - }], -}) - -// NOTE: I do not fully understand it yet, but PartialRibbons must be -// setup BEFORE RibbonAlignToFirst, otherwise the later will break -// on shifting an image to a new ribbon... -// To reproduce: -// - setupe RibbonAlignToFirst first -// - go to top ribbon -// - shift image up -// XXX The two should be completely independent.... (???) -var PartialRibbons = -module.PartialRibbons = core.ImageGridFeatures.Feature({ - title: 'Partial Ribbons', - doc: core.doc`Maintains partially loaded ribbons, this enables very large - image sets to be handled efficiently.`, - - // NOTE: partial ribbons needs to be setup first... - // ...the reasons why things break otherwise is not too clear. - priority: 'high', - - tag: 'ui-partial-ribbons', - exclusive: ['ui-partial-ribbons'], - depends: [ - 'ui' - ], - suggested: [ - 'ui-partial-ribbons-precache', - ], - - - actions: PartialRibbonsActions, - - handlers: [ - ['focusImage.pre centerImage.pre', - function(target, list){ - // NOTE: we have to do this as we are called BEFORE the - // actual focus change happens... - // XXX is there a better way to do this??? - target = list != null ? target = this.data.getImage(target, list) : target - - this.updateRibbon(target) - }], - ['resizing.pre', - function(unit, size){ - // keep constant size in single image... - if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){ - this.updateRibbon( - 'current', - this.config['ribbons-resize-single-image'] || 13) - - } else if(unit == 'scale'){ - this.updateRibbon('current', this.screenwidth / size || 1) - - } else if(unit == 'screenwidth'){ - this.updateRibbon('current', size || 1) - - } else if(unit == 'screenheight'){ - size = size || 1 - - // convert target height in ribbons to width in images... - // NOTE: this does not account for compensation that - // .updateRibbon(..) makes for fitting whole image - // counts, this is a small enough error so as not - // to waste time on... - var s = this.ribbons.scale() - var h = this.ribbons.getScreenHeightRibbons() - var w = this.ribbons.getScreenWidthImages() - var nw = w / (h/size) - - this.updateRibbon('current', nw) - } - }], - ], -}) - - - -/********************************************************************** -* vim:set ts=4 sw=4 : */ return module }) diff --git a/ui (gen4)/features/ui-ribbons.js b/ui (gen4)/features/ui-ribbons.js index 9fb0cb21..646e547b 100755 --- a/ui (gen4)/features/ui-ribbons.js +++ b/ui (gen4)/features/ui-ribbons.js @@ -18,8 +18,88 @@ var ribbons = require('imagegrid/ribbons') /*********************************************************************/ +// helpers... -var RibbonsActions = actions.Actions({ +// XXX make this compatible with multiple images... +// XXX for muptiple targets this will just do a .reload()... +var updateImagePosition = +function updateImagePosition(actions, target){ + var s = actions.ribbons.getRibbonLocator() + + if(s.length == 0){ + return + } + + target = target || actions.current + target = target instanceof jQuery + ? actions.ribbons.getElemGID(target) + : target + + var source_ribbon = actions.ribbons.getElemGID(actions.ribbons.getRibbon(target)) + var source_order = actions.data.getImageOrder(target) + + return function(){ + actions.ribbons.preventTransitions(s) + var end = function(){ + // XXX not sure why this does not work without a setTimeout(..) + //actions.ribbons.restoreTransitions(s, true) + setTimeout(function(){ + actions.ribbons.restoreTransitions(s, true) }, 0) } + + // XXX hack??? + if(target instanceof Array){ + actions.reload() + return end() + } + + var target_ribbon = actions.data.getRibbon(target) + + // nothing changed... + if(source_ribbon == target_ribbon + && actions.data.getImageOrder(target) == source_order){ + return end() + } + + // place image at position... + var to = actions.data.getImage(target, 'next') + if(to != null){ + actions.ribbons.placeImage(target, to, 'before') + + } else { + // place image after position... + to = actions.data.getImage(target, 'prev') + if(to != null){ + actions.ribbons.placeImage(target, to, 'after') + + // new ribbon... + } else { + to = actions.data.getRibbon(target) + + if(actions.ribbons.getRibbon(to).length == 0){ + actions.ribbons + .placeRibbon(to, actions.data.getRibbonOrder(target)) + } + + actions.ribbons.placeImage(target, to) + } + } + + if(actions.data.getImages(source_ribbon).length == 0){ + actions.ribbons.getRibbon(source_ribbon).remove() + } + + actions.focusImage() + + return end() + } +} + + + +/*********************************************************************/ + +var RibbonsActions = +actions.Actions({ get dom(){ return this.ribbons ? this.ribbons.viewer : undefined }, @@ -351,14 +431,12 @@ var RibbonsActions = actions.Actions({ return this.ribbons.rotate() || 0 } }], - - // XXX move all the stuff from UI that binds actions to ribbons... - // XXX }) var Ribbons = -module.Ribbons = core.ImageGridFeatures.Feature({ +module.Ribbons = +core.ImageGridFeatures.Feature({ title: '', doc: '', @@ -379,53 +457,309 @@ module.Ribbons = core.ImageGridFeatures.Feature({ //--------------------------------------------------------------------- -// XXX -var PartialRibbonsActions = actions.Actions({ +var RibbonsEditActions = +actions.Actions({ + setBaseRibbon: [ + function(target){ + var r = this.data.getRibbon(target) + r = r == null ? this.ribbons.getRibbon(target) : r + this.ribbons.setBaseRibbon(r) + }], + shiftImageLeft: [ + function(target){ this.ribbons.placeImage(target, -1) }], + shiftImageRight: [ + function(target){ this.ribbons.placeImage(target, 1) }], + shiftRibbonUp: [ + function(target){ + target = this.ribbons.getRibbon(target) + var i = this.ribbons.getRibbonOrder(target) + if(i > 0){ + this.ribbons.placeRibbon(target, i-1) + } + }], + shiftRibbonDown: [ + function(target){ + target = this.ribbons.getRibbon(target) + var i = this.ribbons.getRibbonOrder(target) + if(i < this.data.ribbon_order.length-1){ + this.ribbons.placeRibbon(target, i+1) + } + }], + + // basic image editing... + // + // XXX should we have .rotate(..) and .flip(..) generic actions??? + rotateCW: [ + function(target){ this.ribbons.rotateCW(target) }], + rotateCCW: [ + function(target){ this.ribbons.rotateCCW(target) }], + flipVertical: [ + function(target){ this.ribbons.flipVertical(target, 'view') }], + flipHorizontal: [ + function(target){ this.ribbons.flipHorizontal(target, 'view') }], + + // tags... + tag: [ + function(tags, gids){ + gids = gids != null && gids.constructor !== Array ? [gids] : gids + return function(){ + //this.ribbons.updateImage(gids) + this.refresh(gids) + } + }], + untag: [ + function(tags, gids){ + gids = gids != null && gids.constructor !== Array ? [gids] : gids + return function(){ + //this.ribbons.updateImage(gids) + this.refresh(gids) + } + }], }) -var PartialRibbons = -module.PartialRibbons = core.ImageGridFeatures.Feature({ - title: '', - doc: '', - - tag: 'ui-partial-ribbons-render', - exclusive: ['ui-render'], - depends: [ - // XXX this will need to reuse part of the actions defined in Ribbons... - ], - suggested: [ - 'ui-ribbons-edit-render', - ], - - actions: PartialRibbonsActions, - - handlers: [], -}) - - -//--------------------------------------------------------------------- - -var RibbonsEditActions = actions.Actions({ -}) - - var RibbonsEdit = -module.RibbonsEdit = core.ImageGridFeatures.Feature({ +module.RibbonsEdit = +core.ImageGridFeatures.Feature({ title: '', doc: '', tag: 'ui-ribbons-edit-render', depends: [ 'edit', + 'tags', + 'sort', + 'crop', + 'image-group', + 'ui-ribbons-render', ], actions: RibbonsEditActions, - handlers: [], + handlers: [ + [[ + 'shiftImageTo.pre', + 'shiftImageUp.pre', + 'shiftImageDown.pre', + ], + function(target){ + return updateImagePosition(this, target) }], + + + // manage the .crop-mode css class... + ['crop uncrop', + function(){ + this.dom[this.cropped ? + 'addClass' + : 'removeClass']('crop-mode') + }], + + // reloading and updating... + [[ + 'sortImages', + 'alignToRibbon', + 'group', + 'ungroup', + 'groupTo', + 'groupMarked', + 'expandGroup', + 'collapseGroup', + 'crop', + 'uncrop', + ], + function(target){ return this.reload(true) }], + [[ + 'reverseImages', + 'reverseRibbons', + 'cropGroup', + ], + function(target){ return this.reload() }], + ], }) +/*********************************************************************/ +// Partial ribbons... + +// XXX try using .ribbons.resizeRibbon(..) for basic tasks... +// XXX try a strategy: load more in the direction of movement by an offset... +// XXX updateRibbon(..) is not signature compatible with data.updateRibbon(..) +var PartialRibbonsActions = +actions.Actions({ + config: { + // Number of screen widths to load... + 'ribbon-size-screens': 7, + + // Number of screen widths to edge to trigger reload... + 'ribbon-resize-threshold': 1.5, + + // Timeout before a non-forced ribbon size update happens after + // the action... + // NOTE: if set to null, the update will be sync... + 'ribbon-update-timeout': 120, + }, + + // NOTE: this will force sync resize if one of the following is true: + // - the target is not loaded + // - we are less than screen width from the edge + // - threshold is set to 0 + // XXX this is not signature compatible with data.updateRibbon(..) + // XXX do not do anything for off-screen ribbons... + updateRibbon: ['- Interface/Update partial ribbon size', + function(target, w, size, threshold){ + target = target instanceof jQuery + ? this.ribbons.getElemGID(target) + // NOTE: data.getImage(..) can return null at start or end + // of ribbon, thus we need to account for this... + : (this.data.getImage(target) + || this.data.getImage(target, 'after')) + + w = w || this.screenwidth + + // get config data and normalize... + size = (size + || this.config['ribbon-size-screens'] + || 5) * w + threshold = threshold == 0 ? threshold + : (threshold + || this.config['ribbon-resize-threshold'] + || 1) * w + + var timeout = this.config['ribbon-update-timeout'] + + // next/prev loaded... + var img = this.ribbons.getImage(target) + var nl = img.nextAll('.image:not(.clone)').length + var pl = img.prevAll('.image:not(.clone)').length + + // next/prev available... + // NOTE: we subtract 1 to remove the current and make these + // compatible with: nl, pl + var na = this.data.getImages(target, size, 'after').length - 1 + var pa = this.data.getImages(target, size, 'before').length - 1 + + // do the update... + // no threshold means force load... + if(threshold == 0 + // the target is not loaded... + || img.length == 0 + // passed hard threshold on the right... + || (nl < w && na > nl) + // passed hard threshold on the left... + || (pl < w && pa > pl)){ + + this.resizeRibbon(target, size) + + // do a late resize... + // loaded more than we need (crop?)... + } else if(na + pa < nl + pl + // passed threshold on the right... + || (nl < threshold && na > nl) + // passed threshold on the left... + || (pl < threshold && pa > pl) + // loaded more than we need by threshold... + || nl + pl + 1 > size + threshold){ + + return function(){ + // sync update... + if(timeout == null){ + this.resizeRibbon(target, size) + + // async update... + } else { + // XXX need to check if we are too close to the edge... + var that = this + //setTimeout(function(){ that.resizeRibbon(target, size) }, 0) + if(this.__update_timeout){ + clearTimeout(this.__update_timeout) + } + this.__update_timeout = setTimeout(function(){ + delete that.__update_timeout + that.resizeRibbon(target, size) + }, timeout) + } + } + } + }], +}) + +// NOTE: I do not fully understand it yet, but PartialRibbons must be +// setup BEFORE RibbonAlignToFirst, otherwise the later will break +// on shifting an image to a new ribbon... +// To reproduce: +// - setupe RibbonAlignToFirst first +// - go to top ribbon +// - shift image up +// XXX The two should be completely independent.... (???) +var PartialRibbons = +module.PartialRibbons = +core.ImageGridFeatures.Feature({ + title: 'Partial Ribbons', + doc: core.doc`Maintains partially loaded ribbons, this enables very large + image sets to be handled efficiently.`, + + // NOTE: partial ribbons needs to be setup first... + // ...the reasons why things break otherwise is not too clear. + priority: 'high', + + tag: 'ui-partial-ribbons', + exclusive: ['ui-partial-ribbons'], + depends: [ + 'ui' + ], + suggested: [ + 'ui-partial-ribbons-precache', + ], + + + actions: PartialRibbonsActions, + + handlers: [ + ['focusImage.pre centerImage.pre', + function(target, list){ + // NOTE: we have to do this as we are called BEFORE the + // actual focus change happens... + // XXX is there a better way to do this??? + target = list != null ? target = this.data.getImage(target, list) : target + + this.updateRibbon(target) + }], + ['resizing.pre', + function(unit, size){ + // keep constant size in single image... + if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){ + this.updateRibbon( + 'current', + this.config['ribbons-resize-single-image'] || 13) + + } else if(unit == 'scale'){ + this.updateRibbon('current', this.screenwidth / size || 1) + + } else if(unit == 'screenwidth'){ + this.updateRibbon('current', size || 1) + + } else if(unit == 'screenheight'){ + size = size || 1 + + // convert target height in ribbons to width in images... + // NOTE: this does not account for compensation that + // .updateRibbon(..) makes for fitting whole image + // counts, this is a small enough error so as not + // to waste time on... + var s = this.ribbons.scale() + var h = this.ribbons.getScreenHeightRibbons() + var w = this.ribbons.getScreenWidthImages() + var nw = w / (h/size) + + this.updateRibbon('current', nw) + } + }], + ], +}) + + + + /********************************************************************** * vim:set ts=4 sw=4 : */ return module }) diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js index ede1c8f5..0d988bb9 100755 --- a/ui (gen4)/features/ui.js +++ b/ui (gen4)/features/ui.js @@ -47,101 +47,6 @@ var base = require('features/base') /*********************************************************************/ - -var reloadAfter = -module.reloadAfter = -function(force, callback){ - return function(){ - return function(){ - // NOTE: this may seem like cheating, but .reload() should - // be very efficient, reusing all of the items loaded... - this.reload(force) - - callback && callback.apply(this, arguments) - } - } -} - - -// XXX make this compatible with multiple images... -// XXX for muptiple targets this will just do a .reload()... -var updateImagePosition = -module.updateImagePosition = -function updateImagePosition(actions, target){ - var s = actions.ribbons.getRibbonLocator() - - if(s.length == 0){ - return - } - - target = target || actions.current - target = target instanceof jQuery - ? actions.ribbons.getElemGID(target) - : target - - var source_ribbon = actions.ribbons.getElemGID(actions.ribbons.getRibbon(target)) - var source_order = actions.data.getImageOrder(target) - - return function(){ - actions.ribbons.preventTransitions(s) - var end = function(){ - // XXX not sure why this does not work without a setTimeout(..) - //actions.ribbons.restoreTransitions(s, true) - setTimeout(function(){ - actions.ribbons.restoreTransitions(s, true) }, 0) } - - // XXX hack??? - if(target instanceof Array){ - actions.reload() - return end() - } - - var target_ribbon = actions.data.getRibbon(target) - - // nothing changed... - if(source_ribbon == target_ribbon - && actions.data.getImageOrder(target) == source_order){ - return end() - } - - // place image at position... - var to = actions.data.getImage(target, 'next') - if(to != null){ - actions.ribbons.placeImage(target, to, 'before') - - } else { - // place image after position... - to = actions.data.getImage(target, 'prev') - if(to != null){ - actions.ribbons.placeImage(target, to, 'after') - - // new ribbon... - } else { - to = actions.data.getRibbon(target) - - if(actions.ribbons.getRibbon(to).length == 0){ - actions.ribbons - .placeRibbon(to, actions.data.getRibbonOrder(target)) - } - - actions.ribbons.placeImage(target, to) - } - } - - if(actions.data.getImages(source_ribbon).length == 0){ - actions.ribbons.getRibbon(source_ribbon).remove() - } - - actions.focusImage() - - return end() - } -} - - - -/*********************************************************************/ - // Viewer (widget/interface)... // // Workspaces: @@ -153,11 +58,11 @@ function updateImagePosition(actions, target){ // // NOTE: this uses the base feature API but does not need it imported... // -// XXX split this into read and write actions... // XXX need a way to neutrally scale images and store that scale... // - fit N images/ribbons is neutral but might mean different things // depending on image and viewer proportions // - .scale is a bad way to go... +// XXX remove dependency on .ribbons var ViewerActions = module.ViewerActions = actions.Actions({ config: { @@ -714,8 +619,6 @@ module.Viewer = core.ImageGridFeatures.Feature({ 'ui-render', ], suggested: [ - // XXX is this the right way??? - 'ui-edit', ], actions: ViewerActions, @@ -780,150 +683,6 @@ module.Viewer = core.ImageGridFeatures.Feature({ }) -//--------------------------------------------------------------------- -// Viewer edit actions... - -// XXX Q: should this be further split into groups and tags??? -var ViewerEditActions = -module.ViewerEditActions = -actions.Actions({ - config: { - }, - - setBaseRibbon: [ - function(target){ - var r = this.data.getRibbon(target) - r = r == null ? this.ribbons.getRibbon(target) : r - this.ribbons.setBaseRibbon(r) - }], - - shiftImageLeft: [ - function(target){ this.ribbons.placeImage(target, -1) }], - shiftImageRight: [ - function(target){ this.ribbons.placeImage(target, 1) }], - - /* - // XXX how should these animate??? - travelImageUp: [ - function(){ - }], - travelImageDown: [ - function(){ - }], - */ - - shiftRibbonUp: [ - function(target){ - target = this.ribbons.getRibbon(target) - var i = this.ribbons.getRibbonOrder(target) - if(i > 0){ - this.ribbons.placeRibbon(target, i-1) - } - }], - shiftRibbonDown: [ - function(target){ - target = this.ribbons.getRibbon(target) - var i = this.ribbons.getRibbonOrder(target) - if(i < this.data.ribbon_order.length-1){ - this.ribbons.placeRibbon(target, i+1) - } - }], - - reverseImages: [ reloadAfter() ], - reverseRibbons: [ reloadAfter() ], - sortImages: [ reloadAfter(true) ], - - // basic image editing... - // - // XXX should we have .rotate(..) and .flip(..) generic actions??? - rotateCW: [ - function(target){ this.ribbons.rotateCW(target) }], - rotateCCW: [ - function(target){ this.ribbons.rotateCCW(target) }], - flipVertical: [ - function(target){ this.ribbons.flipVertical(target, 'view') }], - flipHorizontal: [ - function(target){ this.ribbons.flipHorizontal(target, 'view') }], - - // XXX this needs an interactive mode -- mark A, mark B, align between - alignToRibbon: [ reloadAfter(true) ], - - - // tags... - tag: [ - function(tags, gids){ - gids = gids != null && gids.constructor !== Array ? [gids] : gids - return function(){ - //this.ribbons.updateImage(gids) - this.refresh(gids) - } - }], - untag: [ - function(tags, gids){ - gids = gids != null && gids.constructor !== Array ? [gids] : gids - return function(){ - //this.ribbons.updateImage(gids) - this.refresh(gids) - } - }], - - - // group stuff... - group: [ reloadAfter(true) ], - ungroup: [ reloadAfter(true) ], - groupTo: [ reloadAfter(true) ], - groupMarked: [ reloadAfter(true) ], - expandGroup: [ reloadAfter(true) ], - collapseGroup: [ reloadAfter(true) ], - - - // XXX BUG? reloadAfter() here does not remove some images... - crop: [ reloadAfter(true) ], - // XXX BUG? reloadAfter() produces an align error... - uncrop: [ reloadAfter(true) ], - // XXX might be a good idea to do this in a new viewer in an overlay... - cropGroup: [ reloadAfter() ], -}) - -var ViewerEdit = -module.ViewerEdit = -core.ImageGridFeatures.Feature({ - title: 'Graphical User Interface', - - tag: 'ui-edit', - - depends: [ - 'edit', - 'tags', - 'sort', - 'crop', - 'image-group', - 'ui', - ], - - actions: ViewerEditActions, - - handlers: [ - [[ - 'shiftImageTo.pre', - 'shiftImageUp.pre', - 'shiftImageDown.pre', - ], - function(target){ - return updateImagePosition(this, target) }], - - - // manage the .crop-mode css class... - ['crop uncrop', - function(){ - this.dom[this.cropped ? - 'addClass' - : 'removeClass']('crop-mode') - }], - ], -}) - - /*********************************************************************/ // User interfaces for different base features...