From 16dd6c92a62302bc5f66f6f95b55c4630d1741e2 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 9 Nov 2014 18:54:53 +0300 Subject: [PATCH] refactoring and several bugfixes... Signed-off-by: Alex A. Naanou --- ui (gen4)/browser.js | 2 +- ui (gen4)/lib/actions.js | 26 ++++- ui (gen4)/lib/jli.js | 19 +++- ui (gen4)/ribbons.js | 25 +++-- ui (gen4)/ui.js | 1 + ui (gen4)/viewer.js | 218 +++++++++++++++++++++++++-------------- 6 files changed, 191 insertions(+), 100 deletions(-) diff --git a/ui (gen4)/browser.js b/ui (gen4)/browser.js index 45991c90..17350ec3 100755 --- a/ui (gen4)/browser.js +++ b/ui (gen4)/browser.js @@ -37,7 +37,7 @@ module.exitFullscreen = function() { window.toggleFullscreenMode = -module.toggleFullscreenMode = makeCSSClassToggler( +module.toggleFullscreenMode = CSSClassToggler( document.body, '.full-screen-mode', function(action){ diff --git a/ui (gen4)/lib/actions.js b/ui (gen4)/lib/actions.js index c0c3d38d..17fd1f86 100755 --- a/ui (gen4)/lib/actions.js +++ b/ui (gen4)/lib/actions.js @@ -9,6 +9,7 @@ define(function(require){ var module = {} + /*********************************************************************/ // Actions // @@ -157,9 +158,12 @@ if(typeof(args2array) != 'function'){ // registered from the current object and up to the base action set // will be fired. // -// - an action will return the last deepest action's return, if that -// return is null, then the action set is returned. +// - an action will return the deepest (root) action's return, if that +// returns is undefined, then the action set is returned instead. // +// NOTE: of the root handler is instance of Toggler (jli) and the action +// is called with '?' as argument, then the toggler will be called +// with the argument and return the result bypassing the handlers. // NOTE: actions once defined do not depend on the inheritance hierarchy, // other than the .getHandlers(..) method. If this method is not // found in the inheritance chain (i.e. the link to MetaActions) @@ -173,7 +177,6 @@ if(typeof(args2array) != 'function'){ // NOTE: by default an action will return 'this', i.e. the action set // object the action was called from. // -// XXX do we need to return something from an action ever? // XXX add more metadata/docs: // .section // .category @@ -211,6 +214,18 @@ function Action(name, doc, ldoc, func){ // method can be referenced more than once, both with the // same as well as under different names... var handlers = getHandlers.call(this, name) + // .map(function(h){ return h.apply(that, args) }) + + // special case: if the root handler is a toggler and we call + // it with '?' then do not call the handlers... + // XXX might be good to make this modular/configurable... + if(handlers.slice(-1)[0] instanceof Toggler + && args.length == 1 + && args[0] == '?'){ + return handlers.slice(-1)[0].apply(this, args) + } + + handlers = handlers .map(function(h){ return h.apply(that, args) }) // XXX use the last return as result... @@ -224,7 +239,7 @@ function Action(name, doc, ldoc, func){ handlers.reverse().forEach(function(h, i){ // function... if(h instanceof Function){ - var r = h.call(that) + var r = h.apply(that, args) res = i == 0 ? r : res // deferred... @@ -234,7 +249,8 @@ function Action(name, doc, ldoc, func){ } }) - return res || this + // XXX is this the right way to go? + return res !== undefined ? res : this //return this } meth.__proto__ = this.__proto__ diff --git a/ui (gen4)/lib/jli.js b/ui (gen4)/lib/jli.js index e960fe9f..9cbce79d 100755 --- a/ui (gen4)/lib/jli.js +++ b/ui (gen4)/lib/jli.js @@ -100,7 +100,7 @@ var USE_3D_TRANSFORM = true // NOTE: if the pre-callback explicitly returns false, then the change will // not be made. // -// XXX revize/update this doc for makeToggler(..) +// XXX revize/update this doc for Toggler(..) // Make a generic toggler function/method... @@ -124,7 +124,7 @@ var USE_3D_TRANSFORM = true // later is enough, but as strict mode is not stable enough (sometimes // works and sometimes does not), we can not reliably pass the element // via 'this'. -function makeToggler(elem, state_accessor, states, callback_a, callback_b){ +function Toggler(elem, state_accessor, states, callback_a, callback_b){ // normalize states... states = typeof(states) == typeof('str') ? ['none', states] : states // normalize the callbacks... @@ -246,13 +246,17 @@ function makeToggler(elem, state_accessor, states, callback_a, callback_b){ 'If "?" is given, this will return the current state.' } + func.__proto__ = Toggler.prototype + func.constructor = Toggler + return func } +Toggler.prototype.__proto__ = Function.prototype // XXX this should be drop-in compatible with createCSSClassToggler(..) // test and replace... -function makeCSSClassToggler(elem, classes, callback_a, callback_b){ +function CSSClassToggler(elem, classes, callback_a, callback_b){ // normalize the states... classes = typeof(classes) == typeof('str') ? ['none', classes] : classes // remove the dot from class names... @@ -268,7 +272,7 @@ function makeCSSClassToggler(elem, classes, callback_a, callback_b){ }).join(' ') }) - return makeToggler( + var toggler = Toggler( elem, function(state){ 'use strict' @@ -298,8 +302,15 @@ function makeCSSClassToggler(elem, classes, callback_a, callback_b){ classes, callback_a, callback_b) + + toggler.__proto__ = CSSClassToggler.prototype + toggler.constructor = CSSClassToggler + + return toggler } +CSSClassToggler.prototype.__proto__ = Toggler.prototype + /* diff --git a/ui (gen4)/ribbons.js b/ui (gen4)/ribbons.js index 7ac412ab..ac6819b4 100755 --- a/ui (gen4)/ribbons.js +++ b/ui (gen4)/ribbons.js @@ -206,22 +206,21 @@ module.RibbonsPrototype = { scale = scale || this.getScale() dim = dim == null ? 'width' : dim var img = this.viewer.find('.image') - if(dim == 'height'){ - return img.outerHeight(true) * scale - } else if(dim == 'width'){ - return img.outerWidth(true) * scale - } else if(dim == 'max'){ - return Math.max(img.outerHeight(true), img.outerWidth(true)) * scale - } else if(dim == 'min'){ - return Math.min(img.outerHeight(true), img.outerWidth(true)) * scale - } + + return dim == 'height' ? img.outerHeight(true) * scale + : dim == 'width' ? img.outerWidth(true) * scale + : dim == 'max' ? + Math.max(img.outerHeight(true), img.outerWidth(true)) * scale + : dim == 'min' ? + Math.min(img.outerHeight(true), img.outerWidth(true)) * scale + : null }, - getScreenWidthImages: function(scale){ + getScreenWidthImages: function(scale, min){ var scale = scale == null ? 1 : scale/this.getScale() var W = this.viewer.width() - var w = this.getVisibleImageSize('width') * scale + var w = this.getVisibleImageSize(min ? 'min' : 'width') * scale return W/w }, @@ -1604,11 +1603,11 @@ module.RibbonsPrototype = { // overflows either in height nor width. // // XXX might be usefull to set origin before scaling... - fitImage: function(n){ + fitImage: function(n, min){ n = n || 1 // NOTE: this is width oriented... - var scale = this.getScreenWidthImages(1) / n + var scale = this.getScreenWidthImages(1, min) / n // check bounds... var H = this.viewer.height() diff --git a/ui (gen4)/ui.js b/ui (gen4)/ui.js index efef2b86..8717dd5a 100755 --- a/ui (gen4)/ui.js +++ b/ui (gen4)/ui.js @@ -189,6 +189,7 @@ $(function(){ viewer.PartialRibbons.setup(a) viewer.AlignRibbonsToImageOrder.setup(a) //viewer.AlignRibbonsToFirstImage.setup(a) + viewer.SingleImageView.setup(a) viewer.ShiftAnimation.setup(a) viewer.BoundsIndicators.setup(a) viewer.CurrentImageIndicator.setup(a) diff --git a/ui (gen4)/viewer.js b/ui (gen4)/viewer.js index 7caf6322..168bfddc 100755 --- a/ui (gen4)/viewer.js +++ b/ui (gen4)/viewer.js @@ -542,6 +542,9 @@ actions.Actions(Client, { // updater if one is defined here as .updateRibbon(target) // XXX actions.updateRibbon(..) and ribbons.updateRibbon(..) are NOT // signature compatible... + // ...I'll fix this as/if I need to, right now there is no point to + // spend time and effort on unifying the interface when the common + // use-cases are not known + it seems quite logical as-is right now. reload: ['Reload viewer', function(){ this.ribbons.preventTransitions() @@ -577,11 +580,6 @@ actions.Actions(Client, { function(){ toggleFullscreenMode() }], - toggleSingleImage: ['Toggle single image view', - // XXX this is wrong!!! - makeCSSClassToggler( - function(){ return this.ribbons.viewer }, - 'single-image-mode') ], // XXX revise this... showDevTools: ['', function(){ @@ -591,7 +589,7 @@ actions.Actions(Client, { }], toggleTheme: ['', - makeCSSClassToggler( + CSSClassToggler( function(){ return this.ribbons.viewer }, [ 'gray', @@ -792,13 +790,12 @@ actions.Actions(Client, { function(){ this.fitImage(this.config['max-screen-images']) }], - // XXX + // XXX the question with these is how to make these relatively + // similar across platforms... fitSmall: ['Show small image', function(){ }], - // XXX fitNormal: ['Show normal image', function(){ }], - // XXX fitScreen: ['Fit image to screen', function(){ }], @@ -947,53 +944,58 @@ actions.Actions({ w = w || this.screenwidth - // get config data... - size = size - || this.config['ribbon-size-screens'] - || 5 - threshold = threshold + threshold = (threshold || this.config['ribbon-resize-threshold'] - || 1 - - // normalize to image count... - var s = size * w - var t = threshold * w + || 1) * w // next/prev loaded... var nl = this.ribbons.getImage(target).nextAll('.image').length var pl = this.ribbons.getImage(target).prevAll('.image').length // next/prev available... - var na = this.data.getImages(target, s/2, 'after').length - 1 - var pa = this.data.getImages(target, s/2, 'before').length - 1 + var na = this.data.getImages(target, size/2, 'after').length - 1 + var pa = this.data.getImages(target, size/2, 'before').length - 1 // do the update... // the target is not loaded... if(this.ribbons.getImage(target).length == 0 // passed threshold on the right... - || (nl < t && na > nl) + || (nl < threshold && na > nl) // passed threshold on the left... - || (pl < t && pa > pl) + || (pl < threshold && pa > pl) // loaded more than we need by threshold... - || nl + pl + 1 > s + t){ + || nl + pl + 1 > size + threshold){ - // NOTE: we can't get ribbon via target directly here as - // the target might not be loaded... - var r_gid = this.data.getRibbon(target) + // get config data and normalize... + size = (size + || this.config['ribbon-size-screens'] + || 5) * w - // localize transition prevention... - var r = this.ribbons.getRibbon(r_gid) + this.resizeRibbon(target, size) + } + }], + resizeRibbon: ['Resize ribbon to n images', + function(target, size){ + size = size + || (this.config['ribbon-size-screens'] * this.screenwidth) + || (5 * this.screenwidth) - if(r.length > 0){ - this.ribbons - .preventTransitions(r) - .updateRibbon( - this.data.getImages(target, s), - r_gid, - target) - .restoreTransitions(r, true) - } + // NOTE: we can't get ribbon via target directly here as + // the target might not be loaded... + var r_gid = this.data.getRibbon(target) + + // localize transition prevention... + var r = this.ribbons.getRibbon(r_gid) + + if(r.length > 0){ + this.ribbons + .preventTransitions(r) + .updateRibbon( + this.data.getImages(target, size), + r_gid, + target) + .restoreTransitions(r, true) } }] }) @@ -1012,17 +1014,24 @@ module.PartialRibbons = Feature({ tag: 'ui-partial-ribbons', // number of screen widths to load... - size: 5, + size: 7, // number of screen widths to edge to trigger reload... - threshold: 1, + threshold: 1.5, setup: function(actions){ var feature = this - actions.mixin(PartialRibbonsActions) + + if(!('ribbon-size-screens' in actions.config)){ + actions.config['ribbon-size-screens'] = this.size + } + if(!('ribbon-resize-threshold' in actions.config)){ + actions.config['ribbon-resize-threshold'] = this.threshold + } return actions + .mixin(PartialRibbonsActions) .on('focusImage.pre centerImage.pre', this.tag, function(target){ this.updateRibbon(target) }) @@ -1053,6 +1062,50 @@ module.PartialRibbons = Feature({ +//--------------------------------------------------------------------- +var SingleImageActions = +module.SingleImageActions = +actions.Actions({ + toggleSingleImage: ['Toggle single image view', + // XXX this is wrong!!! + CSSClassToggler( + function(){ return this.ribbons.viewer }, + 'single-image-mode') ], +}) + + +var SingleImageView = +module.SingleImageView = Feature({ + tag: 'ui-single-image-view', + + // XXX + setup: function(actions){ + return actions + .mixin(SingleImageActions) + .on('toggleSingleImage.post fitImgae.post', this.tag, function(){ + // XXX set image proportions... + if(this.toggleSingleImage('?') == 'on'){ + console.log('!!!! single image: on') + // XXX + + // restore original image size... + } else { + console.log('!!!! single image: off') + this.ribbons.viewer.find('.image').css({ + width: '', + height: '' + }) + } + }) + }, + remove: function(actions){ + actions.mixout(SingleImageActions) + return actions.off('*', this.tag) + }, +}) + + + //--------------------------------------------------------------------- // XXX this should also define up/down navigation behavior e.g. what to // focus on next/prev ribbon... @@ -1062,12 +1115,8 @@ module.AlignRibbonsToImageOrder = Feature({ setup: function(actions){ return actions - // XXX this can be either pre or post... - .on('focusImage.post', this.tag, function(target){ - this.alignByOrder(target) - }) - // normalize the initial state... - //.focusImage() + .on('focusImage.post', this.tag, + function(){ this.alignByOrder() }) }, }) @@ -1080,12 +1129,8 @@ module.AlignRibbonsToFirstImage = Feature({ setup: function(actions){ return actions - // XXX this can be either pre or post... - .on('focusImage.post', this.tag, function(target){ - this.alignByFirst(target) - }) - // normalize the initial state... - //.focusImage() + .on('focusImage.post', this.tag, + function(){ this.alignByFirst() }) }, }) @@ -1237,7 +1282,7 @@ module.BoundsIndicators = Feature({ }) }, remove: function(actions){ - actions.viewer.find('.' + this.tag).remove() + actions.ribbons.viewer.find('.' + this.tag).remove() return actions.off('*', this.tag) }, }) @@ -1262,12 +1307,12 @@ module.CurrentImageIndicator = Feature({ updateMarker: function(actions, target, update_border){ var scale = actions.ribbons.getScale() var cur = actions.ribbons.getImage(target) - var ribbon = actions.ribbons.getRibbon() + var ribbon = actions.ribbons.getRibbon(target) var ribbon_set = actions.ribbons.viewer.find('.ribbon-set') var marker = ribbon.find('.current-marker') - // no marker found... + // no marker found -- either in different ribbon or not created yet... if(marker.length == 0){ // get marker globally... marker = actions.ribbons.viewer.find('.current-marker') @@ -1292,29 +1337,37 @@ module.CurrentImageIndicator = Feature({ } } + // NOTE: we will update only the attrs that need to be updated... + var css = {} + var w = cur.outerWidth(true) var h = cur.outerHeight(true) - var border = Math.max(this.min_border, this.border / scale) - - // set border right away... - if(update_border == 'before'){ - marker.css({ borderWidth: border }) - - // set border with a delay... - } else { - setTimeout(function(){ - marker.css({ borderWidth: border }) - }, this.border_timeout) + // keep size same as the image... + if(marker.outerWidth() != w || marker.outerHeight() != h){ + css.width = w + css.height = h } - return marker.css({ - left: cur[0].offsetLeft, + // update border... + if(update_border !== false){ + var border = Math.max(this.min_border, this.border / scale) - // keep size same as the image... - width: w, - height: h, - }) + // set border right away... + if(update_border == 'before'){ + css.borderWidth = border + + // set border with a delay... + } else { + setTimeout(function(){ + marker.css({ borderWidth: border }) + }, this.border_timeout) + } + } + + css.left = cur[0].offsetLeft + + return marker.css(css) }, setup: function(actions){ @@ -1323,7 +1376,7 @@ module.CurrentImageIndicator = Feature({ return actions // move marker to current image... .on( 'focusImage.post', this.tag, - function(target){ that.updateMarker(this, target) }) + function(){ that.updateMarker(this) }) // prevent animations when focusing ribbons... .on('focusRibbon.pre', this.tag, function(){ @@ -1333,6 +1386,17 @@ module.CurrentImageIndicator = Feature({ this.ribbons.restoreTransitions(m) } }) + // this is here to compensate for position change on ribbon + // resize... + .on('resizeRibbon.post', this.tag, + function(target, s){ + var m = this.ribbons.viewer.find('.current-marker') + if(m.length != 0){ + this.ribbons.preventTransitions(m) + that.updateMarker(this, target, false) + this.ribbons.restoreTransitions(m, true) + } + }) // Change border size in the appropriate spot in the animation: // - before animation when scaling up // - after when scaling down @@ -1364,7 +1428,7 @@ module.CurrentImageIndicator = Feature({ //.focusImage() }, remove: function(actions){ - actions.viewer.find('.' + this.tag).remove() + actions.ribbons.viewer.find('.' + this.tag).remove() return actions.off('*', this.tag) }, }) @@ -1380,7 +1444,7 @@ module.ImageStateIndicator = Feature({ setup: function(actions){ }, remove: function(actions){ - actions.viewer.find('.' + this.tag).remove() + actions.ribbons.viewer.find('.' + this.tag).remove() return actions.off('*', this.tag) }, }) @@ -1396,7 +1460,7 @@ module.GlobalStateIndicator = Feature({ setup: function(actions){ }, remove: function(actions){ - actions.viewer.find('.' + this.tag).remove() + actions.ribbons.viewer.find('.' + this.tag).remove() return actions.off('*', this.tag) }, })