refactoring and several bugfixes...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2014-11-09 18:54:53 +03:00
parent b420c007bc
commit 16dd6c92a6
6 changed files with 191 additions and 100 deletions

View File

@ -37,7 +37,7 @@ module.exitFullscreen = function() {
window.toggleFullscreenMode =
module.toggleFullscreenMode = makeCSSClassToggler(
module.toggleFullscreenMode = CSSClassToggler(
document.body,
'.full-screen-mode',
function(action){

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
},
})