refactoring of the ui/render/access -- started split of viewer into a renderer and viewer actions...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-05-16 00:26:37 +03:00
parent 796bd4d8cb
commit 22627468eb
18 changed files with 1095 additions and 460 deletions

View File

@ -132,7 +132,7 @@ var AppControlActions = actions.Actions({
this.ribbons.preventTransitions()
// hide the viewer to hide any animation crimes...
this.ribbons.viewer[0].style.visibility = 'hidden'
this.dom[0].style.visibility = 'hidden'
// XXX async...
// ...this complicates things as we need to do the next
@ -146,7 +146,7 @@ var AppControlActions = actions.Actions({
.ribbons
.restoreTransitions()
that.ribbons.viewer[0].style.visibility = ''
that.dom[0].style.visibility = ''
}, 100)
}
@ -292,7 +292,7 @@ module.AppButtons = core.ImageGridFeatures.Feature({
['start toggleFullScreen',
function(){
var fullscreen = this.toggleFullScreen('?')
var buttons = this.ribbons.viewer.find('.app-buttons')
var buttons = this.dom.find('.app-buttons')
// fullscreen button...
buttons.find('.fullscreen.button')

View File

@ -175,7 +175,7 @@ var ExternalEditorUIActions = actions.Actions({
var path = e.find('.text').last().text()
var txt = e.find('.text').first().text()
var b = overlay.Overlay(that.ribbons.viewer,
var b = overlay.Overlay(that.dom,
browseWalk.makeWalk(null, path,
// XXX
'*+(exe|cmd|ps1|sh)',
@ -291,7 +291,7 @@ var ExternalEditorUIActions = actions.Actions({
make(['Add new editor...'])
.on('open', function(){
closingPrevented = true
var b = overlay.Overlay(that.ribbons.viewer,
var b = overlay.Overlay(that.dom,
browseWalk.makeWalk(
null, '/',
// XXX

View File

@ -458,7 +458,7 @@ var KeyboardActions = actions.Actions({
return that.__keyboard_config
}
},
function(){ return that.ribbons.viewer })
function(){ return that.dom })
return kb },
testKeyboardDoc: ['- Interface/',
@ -718,9 +718,9 @@ var KeyboardActions = actions.Actions({
// NOTE: the target element must be focusable...
var target =
this.__keyboard_event_source =
this.config['keyboard-event-source'] == null ? this.ribbons.viewer
this.config['keyboard-event-source'] == null ? this.dom
: this.config['keyboard-event-source'] == 'window' ? $(window)
: this.config['keyboard-event-source'] == 'viewer' ? this.ribbons.viewer
: this.config['keyboard-event-source'] == 'viewer' ? this.dom
: this.config['keyboard-event-source'] == 'document' ? $(document)
: $(this.config['keyboard-event-source'])

View File

@ -66,10 +66,14 @@ core.ImageGridFeatures.Feature('viewer-testing', [
'ui',
'keyboard',
// XXX BUG?: should this be indicated as a missing dependency???
'missing-feature',
// XXX
'ui-ribbons-render',
'ui-vdom-render',
'ui-react-render',
//'ui-react-render',
//*/
// features...
'ui-cursor',

View File

@ -46,12 +46,12 @@ var BoundsIndicatorsActions = actions.Actions({
bottom: '.bottom-indicator',
}[direction]
var indicator = this.ribbons.viewer.find(cls)
var indicator = this.dom.find(cls)
if(indicator.length == 0){
indicator = $('<div>')
.addClass(cls.replace('.', ''))
.appendTo(this.ribbons.viewer)
.appendTo(this.dom)
}
return indicator
@ -262,7 +262,7 @@ module.CurrentImageIndicator = core.ImageGridFeatures.Feature({
function(){
var fadein = this.config['current-image-indicator-fadein']
this.updateCurrentImageIndicator()
this.ribbons.viewer.find('.current-marker')
this.dom.find('.current-marker')
.css({
display: 'block',
opacity: 0,
@ -302,7 +302,7 @@ module.CurrentImageIndicator = core.ImageGridFeatures.Feature({
&& clearTimeout(this.__current_image_indicator_restore_timeout)
delete this.__current_image_indicator_restore_timeout
this.ribbons.viewer
this.dom
.find('.current-marker')
.velocity({opacity: 0}, { duration: 100 })
}],
@ -312,7 +312,7 @@ module.CurrentImageIndicator = core.ImageGridFeatures.Feature({
this.__current_image_indicator_restore_timeout = setTimeout(function(){
that.updateCurrentImageIndicator()
that.ribbons.viewer
that.dom
.find('.current-marker')
.velocity({opacity: 1}, { duration: 100 })
}, this.config['current-image-indicator-restore-delay'] || 500)
@ -322,12 +322,12 @@ module.CurrentImageIndicator = core.ImageGridFeatures.Feature({
['toggleSingleImage',
function(){
if(this.toggleSingleImage('?') == 'off'){
this.ribbons.viewer.find('.current-marker')
this.dom.find('.current-marker')
.delay(150)
.animate({opacity: 1}, 100)
} else {
this.ribbons.viewer.find('.current-marker')
this.dom.find('.current-marker')
.css({ opacity: 0 })
}
}],
@ -343,7 +343,7 @@ var makeIndicatorHiderOnFastAction = function(hide_timeout){
}
var that = this
var m = this.ribbons.viewer.find('.current-marker')
var m = this.dom.find('.current-marker')
var t = this.config[hide_timeout]
var cur = this.current
@ -431,13 +431,13 @@ module.CurrentImageIndicatorHideOnScreenNav = core.ImageGridFeatures.Feature({
// NOTE: we use .pre events here to see if we have moved...
['prevScreen.post nextScreen.post',
function(){
var m = this.ribbons.viewer.find('.current-marker')
var m = this.dom.find('.current-marker')
m.css({ opacity: 0 })
}],
['focusImage.post',
function(){
var m = this.ribbons.viewer.find('.current-marker')
var m = this.dom.find('.current-marker')
m.css({ opacity: '' })
}],
@ -464,7 +464,7 @@ var updateBaseRibbonIndicator = function(img){
}
if(m.length == 0){
m = this.ribbons.viewer.find('.base-ribbon-marker')
m = this.dom.find('.base-ribbon-marker')
// make the indicator...
if(m.length == 0){
@ -478,7 +478,7 @@ var updateBaseRibbonIndicator = function(img){
// XXX this is wrong -- need to calculate the offset after the move and not now...
if(base.offset().left < 0){
m.css('left', (img.position().left + img.width()/2 - this.ribbons.viewer.width()/2) / scale)
m.css('left', (img.position().left + img.width()/2 - this.dom.width()/2) / scale)
} else {
m.css('left', '')
@ -530,7 +530,7 @@ module.PassiveBaseRibbonIndicator = core.ImageGridFeatures.Feature({
actions: actions.Actions({
togglePassiveBaseRibbonIndicator: ['Interface/Passive base ribbon indicator',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'show-passive-base-ribbon-indicator',
function(state){
this.config['ui-show-passive-base-ribbon-indicator'] = state == 'on' }) ],

View File

@ -153,7 +153,7 @@ module.DirectControlHammer = core.ImageGridFeatures.Feature({
// hide and remove current image indicator...
// NOTE: it will be reconstructed on
// next .focusImage(..)
var m = that.ribbons.viewer
var m = that.dom
.find('.current-marker')
.velocity({opacity: 0}, {
duration: 100,
@ -245,14 +245,14 @@ module.IndirectControl = core.ImageGridFeatures.Feature({
if(state == null){
return (this.ribbons
&& this.ribbons.viewer
&& this.ribbons.viewer.data('hammer'))
&& this.dom
&& this.dom.data('hammer'))
|| 'none'
// on...
} else if(state == 'handling-swipes'){
var that = this
var viewer = this.ribbons.viewer
var viewer = this.dom
// prevent multiple handlers...
if(viewer.data('hammer') != null){
@ -272,7 +272,7 @@ module.IndirectControl = core.ImageGridFeatures.Feature({
// off...
} else {
this.ribbons.viewer
this.dom
.off('swipeleft')
.off('swiperight')
.off('swipeup')

View File

@ -435,9 +435,7 @@ var VirtualDOMRibbonsPrototype = {
// concept, so we build the state on demand...
// XXX get scale from config on initial load...
sync: function(target, size){
var dom = this.dom = this.dom
// get/create the ribbon-set...
|| this.imagegrid.ribbons.getRibbonSet(true)
var dom = this.dom
var state = this.state ? Object.create(this.state) : {}
target && (state.target = target)
@ -447,7 +445,7 @@ var VirtualDOMRibbonsPrototype = {
if(this.vdom == null){
var n = this.vdom = this.makeView(state, true)
var v = vdom.create(n)
dom.replaceWith(v)
this.imagegrid.dom.append(v)
this.dom = v
// patch state...

View File

@ -48,7 +48,7 @@ var ProgressActions = actions.Actions({
// XXX revise styles...
showProgress: ['- Interface/Show progress bar...',
function(text, value, max){
var viewer = this.ribbons.viewer
var viewer = this.dom
var that = this
var msg = text instanceof Array ? text.slice(1).join(': ') : null

View File

@ -27,7 +27,7 @@ var RangeActions = actions.Actions({
makeBrace: ['- Range/',
function(type, gid){
var cls = type == 'open' ? 'brace-open' : 'brace-close'
var r = this.ribbons.viewer.find('.ribbon')
var r = this.dom.find('.ribbon')
var brace = this.ribbons.getRibbon(gid).find('.mark.'+cls)
@ -61,7 +61,7 @@ var RangeActions = actions.Actions({
// go but it sure makes things simpler...
if(range == null){
update = true
this.ribbons.viewer
this.dom
.find('.ribbon .mark.brace')
.remove()
@ -90,7 +90,7 @@ var RangeActions = actions.Actions({
}
if(update){
var r = this.ribbons.viewer.find('.ribbon')
var r = this.dom.find('.ribbon')
// XXX this does not work for non-current images ...
this.ribbons.preventTransitions(r)
@ -103,7 +103,7 @@ var RangeActions = actions.Actions({
// XXX not sure if this is the right way to go...
{browseMode: function(){ return !this.data.__range && 'disabled' }},
function(image){
var r = this.ribbons.viewer.find('.ribbon')
var r = this.dom.find('.ribbon')
delete this.data.__range
this.updateRangeIndicators()
@ -246,7 +246,7 @@ module.Range = core.ImageGridFeatures.Feature({
// show/hide off-screen indicators...
// XXX STUB: should we animate indicators???
['setScale.pre',
['viewScale.pre',
function(scale){
var range = this.data.__range
if(!this.ribbons || !range){
@ -259,7 +259,7 @@ module.Range = core.ImageGridFeatures.Feature({
}],
[[
'focusImage',
'setScale',
'viewScale',
'updateRangeIndicators',
],
function(_, gid){
@ -273,7 +273,7 @@ module.Range = core.ImageGridFeatures.Feature({
return
}
var Wr = this.ribbons.viewer.width()
var Wr = this.dom.width()
var W = (Wr / this.scale) / 2
var a = this.data.getImageOrder(range[0])

View File

@ -7,6 +7,7 @@
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var object = require('lib/object')
var actions = require('lib/actions')
var features = require('lib/features')
@ -14,9 +15,31 @@ var core = require('features/core')
/*********************************************************************/
var ViewerClassPrototype = {
}
var ViewerPrototype = {
sync: function(){
},
}
var Viewer =
module.Viewer =
object.makeConstructor('Viewer',
ViewerClassPrototype,
ViewerPrototype)
/*********************************************************************/
var ReactActions = actions.Actions({
get viewer(){
},
})
var React =

View File

@ -7,18 +7,356 @@
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var object = require('lib/object')
var actions = require('lib/actions')
var features = require('lib/features')
var core = require('features/core')
var ribbons = require('imagegrid/ribbons')
/*********************************************************************/
var RibbonsActions = actions.Actions({
get dom(){
return this.ribbons ? this.ribbons.viewer : undefined },
// NOTE: this expects that ribbons will maintain .parent.images...
// NOTE: when getting rid of ribbons need to also remove the .parent
// reference...
// XXX remove...
get ribbons(){
return this.__ribbons },
set ribbons(ribbons){
this.__ribbons = ribbons
ribbons.parent = this
},
load: [
function(data){
return function(){
// recycle the viewer if one is not given specifically...
var viewer = data.viewer
viewer = viewer == null && this.ribbons != null
? this.dom
: viewer
if(this.ribbons == null){
this.ribbons = ribbons.Ribbons(viewer, this.images)
// XXX is this correct???
this.ribbons.__image_updaters = [this.updateImage.bind(this)]
} else {
this.ribbons.clear()
this.ribbons.images = this.images
}
this.reload()
}
}],
// NOTE: this will pass the .ribbons.updateData(..) a custom ribbon
// updater if one is defined here as .updateRibbon(target) action
//
// XXX HACK: two sins:
// - actions.updateRibbon(..) and ribbons.updateRibbon(..)
// are NOT signature compatible...
// - we depend on the internals of a custom add-on feature
reload: ['Interface/Reload viewer',
function(force){
// full reload...
if(force == 'full'){
//this.stop()
/*
killAllWorkers()
.done(function(){
reload()
})
*/
location.reload()
}
this.ribbons.preventTransitions()
// NOTE: this essentially sets the update threshold to 0...
// XXX this should be a custom arg...
force = force ? 0 : null
return function(){
// see if we've got a custom ribbon updater...
var that = this
var settings = this.updateRibbon != null
// XXX this should be: { updateRibbon: this.updateRibbon.bind(this) }
? { updateRibbon: function(_, ribbon){
return that.updateRibbon(ribbon, null, null, force)
} }
: null
this.ribbons.updateData(this.data, settings)
this
// XXX should this be here???
.refresh()
.focusImage()
// XXX HACK to make browser redraw images...
this.scale = this.scale
this.ribbons.restoreTransitions()
}
}],
// NOTE: this will trigger .updateImage hooks...
refresh: ['Interface/Refresh images without reloading',
function(gids, scale){
gids = gids || '*'
var size = scale != null ?
this.ribbons.getVisibleImageSize('min', scale)
: null
this.ribbons.updateImage(gids, null, size)
}],
clear: [
function(){ this.ribbons && this.ribbons.clear() }],
// XXX do we need clone???
clone: [function(full){
return function(res){
if(this.ribbons){
// NOTE: this is a bit wasteful as .ribbons will clone
// their ref to .images that we will throw away...
res.ribbons = this.ribbons.clone()
res.ribbons.images = res.images
}
}
}],
replaceGid: [
function(from, to){
return function(res){
res && this.ribbons.replaceGid(from, to)
}
}],
// This is called by .ribbons, the goal is to use it to hook into
// image updating from features and extensions...
//
// NOTE: not intended for calling manually, use .refresh(..) instead...
//
// XXX EXPERIMENTAL...
// ...need this to get triggered by .ribbons
// at this point manually triggering this will not do anything...
// XXX problem: need to either redesign this or distinguish from
// other actions as I keep calling it expecting results...
// XXX hide from user action list... (???)
updateImage: ['- Interface/Update image (do not use directly)',
'This is called by .refresh(..) and intended for use as an '
+'trigger for handlers, and not as a user-callable acation.',
core.notUserCallable(function(gid, image){
// This is the image update protocol root function
//
// Not for direct use.
})],
// NOTE: this not used directly, mainly designed as a utility to be
// used for various partial ribbon implementations...
// XXX do we handle off-screen ribbons here???
resizeRibbon: ['- Interface/Resize ribbon to n images',
function(target, size){
size = size
|| (this.config['ribbon-size-screens'] * this.screenwidth)
|| (5 * this.screenwidth)
var data = this.data
var ribbons = this.ribbons
// localize transition prevention...
// NOTE: we can't get ribbon via target directly here as
// the target might not be loaded...
var r_gid = data.getRibbon(target)
if(r_gid == null){
return
}
// NOTE: for the initial load this may be empty...
var r = ribbons.getRibbon(r_gid)
// XXX do we need to for example ignore unloaded (r.length == 0)
// ribbons here, for example not load ribbons too far off
// screen??
ribbons
.preventTransitions(r)
.updateRibbon(
data.getImages(target, size, 'total'),
r_gid,
target)
.restoreTransitions(r, true)
}],
// NOTE: this will align only a single image...
// XXX do we need these low level primitives here???
centerImage: ['- Interface/Center an image in ribbon horizontally',
function(target, align, offset, scale){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
: target
// align current ribbon...
this.ribbons.centerImage(target, align, offset, scale)
}],
centerRibbon: ['- Interface/Center a ribbon vertically',
function(target){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
: target
// align current ribbon...
this.ribbons.centerRibbon(target)
}],
focusImage: [
function(target, list){
return function(){
this.ribbons.focusImage(this.data != null ? this.current : target) } }],
focusRibbon: [
function(target, mode){
mode = mode || this.config['ribbon-focus-mode']
var c = this.data.getRibbonOrder()
var i = this.data.getRibbonOrder(target)
// NOTE: we are not changing the direction here based on
// this.direction as swap will confuse the user...
var direction = c < i ? 'before' : 'after'
if(mode == 'visual'){
var ribbons = this.ribbons
var r = this.data.getRibbon(target)
var t = ribbons.getImageByPosition('current', r)
if(t.length > 1){
t = t.eq(direction == 'before' ? 0 : 1)
}
t = ribbons.getElemGID(t)
this.focusImage(t, r)
}
}],
// Zoom/scale protocol...
resizing: [
core.notUserCallable(function(unit, size, overflow){
// This is a resizing protocol root function.
//
// This will never be used directly, but will wrap protocol user
// functions.
//
// As an example see: .viewScale(..)
var that = this
// stop currently running transition...
this.ribbons.scale(this.ribbons.scale())
// transitionend handler...
if(!this.__resize_handler){
this.__resize_handler = function(){
that.__post_resize
&& that.resizingDone()
delete that.__post_resize
}
}
this.ribbons.getRibbonSet()
.off('transitionend', this.__resize_handler)
.on('transitionend', this.__resize_handler)
// timeout handler...
this.__post_resize && clearTimeout(this.__post_resize)
return function(){
this.__post_resize = setTimeout(
this.__resize_handler,
this.config['resize-done-timeout'] || 300)
}
})],
viewScale: ['- Zoom/',
function(scale){
if(scale == null || scale == '?'){
return this.ribbons != null ? this.ribbons.scale() : null
}
this.resizing.chainCall(this, function(){
this.ribbons
&& scale
&& this.ribbons.scale(scale)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', scale)
}, 'scale', scale)
}],
// NOTE: if this gets a count argument it will fit count images,
// default is one.
// NOTE: this will add .config['fit-overflow'] to odd counts if no
// overflow if passed.
// ...this is done to add ability to control scroll indication.
fitImage: ['Zoom/Fit image',
function(count, overflow){
if(count == '?'){
return this.ribbons != null ?
this.ribbons.getScreenWidthImages()
: null
}
this.resizing.chainCall(this, function(){
if(count != null){
overflow = overflow == false ? 0 : overflow
var o = overflow != null ? overflow
: count % 2 != 1 ? 0
: (this.config['fit-overflow'] || 0)
count += o
}
// XXX .ribbons...
this.ribbons.fitImage(count)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', this.ribbons.getScreenWidthImages(1) / count)
}, 'screenwidth', count, overflow)
}],
// NOTE: this does not account for ribbon spacing...
fitRibbon: ['Zoom/Fit ribbon vertically',
function(count, whole){
if(count == '?'){
return this.ribbons != null ?
this.ribbons.getScreenHeightRibbons()
: null
}
this.resizing.chainCall(this, function(){
// XXX .ribbons...
this.ribbons.fitRibbon(count, whole)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', this.ribbons.getScreenHeightRibbons(1, whole) / count)
}, 'screenheight', count, whole)
}],
ribbonRotation: ['- Interface|Ribbon/',
function(a){
if(arguments.length > 0){
this.ribbons.rotate(a)
} else {
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({
title: '',
@ -29,6 +367,9 @@ module.Ribbons = core.ImageGridFeatures.Feature({
depends: [
// XXX
],
suggested: [
'ui-ribbons-edit-render',
],
actions: RibbonsActions,
@ -37,6 +378,54 @@ module.Ribbons = core.ImageGridFeatures.Feature({
//---------------------------------------------------------------------
// XXX
var PartialRibbonsActions = actions.Actions({
})
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({
title: '',
doc: '',
tag: 'ui-ribbons-edit-render',
depends: [
'edit',
],
actions: RibbonsEditActions,
handlers: [],
})
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })

View File

@ -127,7 +127,7 @@ var SingleImageActions = actions.Actions({
return
}
var viewer = this.ribbons.viewer
var viewer = this.dom
var ribbon = this.ribbons.getRibbon()
var images = viewer.find('.ribbon .image')
@ -239,7 +239,7 @@ var SingleImageActions = actions.Actions({
toggleSingleImage: ['Interface/Single image view',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'single-image-mode',
function(state){
if(state == 'on'){
@ -344,7 +344,7 @@ module.SingleImageView = core.ImageGridFeatures.Feature({
function(){
// prevent this from doing anything while no viewer...
if(!this.ribbons
|| !this.ribbons.viewer
|| !this.dom
|| this.ribbons.getRibbonSet().length == 0){
return
}
@ -510,7 +510,7 @@ module.SingleImageViewLocalStorage = core.ImageGridFeatures.Feature({
return function(){
// prevent this from doing anything while no viewer...
if(!this.ribbons
|| !this.ribbons.viewer
|| !this.dom
|| this.ribbons.getRibbonSet().length == 0){
return
}

View File

@ -145,7 +145,7 @@ var SlideshowActions = actions.Actions({
toggleSlideshow: ['Slideshow/Slideshow quick toggle',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'slideshow-running',
function(state){
// start...

View File

@ -33,7 +33,7 @@ var makeStateIndicatorItem = function(container, type, text){
var item = $('<div>')
.addClass('item '+ type || '')
.attr('text', text)
this.ribbons.viewer.find('.state-indicator-container.'+container)
this.dom.find('.state-indicator-container.'+container)
.append(item)
return item
}
@ -461,11 +461,11 @@ var StatusBarActions = actions.Actions({
// XXX change class...
function(){
// no viewer yet...
if(!this.ribbons || !this.ribbons.viewer){
if(!this.ribbons || !this.dom){
return $()
}
var bar = this.ribbons.viewer.find('.state-indicator-container.global-info')
var bar = this.dom.find('.state-indicator-container.global-info')
if(bar.length == 0){
bar = makeStateIndicator('global-info overlay-info statusbar')
.addClass(this.config['status-bar-mode']
@ -484,7 +484,7 @@ var StatusBarActions = actions.Actions({
.on('mouseout', function(){
bar.find('.info').empty()
})
.appendTo(this.ribbons.viewer)
.appendTo(this.dom)
}
return bar
},
@ -624,7 +624,7 @@ var StatusBarActions = actions.Actions({
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
// XXX do this better...
this.ribbons.viewer.find('.global-info .index .position').focus().click()
this.dom.find('.global-info .index .position').focus().click()
}
}],
editStatusBarRibbon: ['- Interface/Edit ribbon focus position in statusbar',
@ -632,11 +632,11 @@ var StatusBarActions = actions.Actions({
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
// XXX do this better...
this.ribbons.viewer.find('.global-info .ribbon-number').focus().click()
this.dom.find('.global-info .ribbon-number').focus().click()
}],
toggleStatusBarIndexMode: ['Interface/Status bar index mode',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer.find('.global-info .index') },
function(){ return this.dom.find('.global-info .index') },
['normal', 'global'],
function(state){
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
@ -652,7 +652,7 @@ var StatusBarActions = actions.Actions({
// XXX revise...
showStatusBarInfo: ['- Interface/',
function(text){
var bar = this.ribbons.viewer.find('.state-indicator-container.global-info')
var bar = this.dom.find('.state-indicator-container.global-info')
if(text){
bar.find('.info').text(text)

View File

@ -9,6 +9,7 @@
var vdom = require('ext-lib/virtual-dom')
var object = require('lib/object')
var actions = require('lib/actions')
var features = require('lib/features')
@ -16,9 +17,453 @@ var core = require('features/core')
/*********************************************************************/
/*/ XXX
var ViewerClassPrototype = {
}
var ViewerPrototype = {
sync: function(){
},
}
var Viewer =
module.Viewer =
object.makeConstructor('Viewer',
ViewerClassPrototype,
ViewerPrototype)
//*/
//---------------------------------------------------------------------
//
// - take care of DOM construction and update...
// - alignment is done via .centerRibbon(..) / .centerImage(..)
// - preview updates (XXX)
// - update onload (a-la .ribbons._loadImagePreviewURL(..))
var VirtualDOMRibbonsClassPrototype = {
// XXX ???
}
var VirtualDOMRibbonsPrototype = {
// XXX this is a circular ref -- I do not like it...
imagegrid: null,
dom: null,
vdom: null,
// Format:
// {
// count: <count>,
//
// scale: <scale>,
//
// top: <offset>,
//
// ribbons: {
// <gid>: <offset>,
// ...
// },
// }
state: null,
// XXX reuse these from ribbons???
preventTransitions: function(){
},
restoreTransitions: function(){
},
getElemGID: function(){
},
getImage: function(){
},
getVisibleImageSize: function(){
},
focusImage: function(){
},
px2vmin: function(){
},
// constructors...
makeView: function(state, initial){
state = state || {}
var that = this
var ig = this.imagegrid
var target = state.target || ig.current
this.state = this.state || {}
var count = state.count = state.count
|| ig.screenwidth * (ig.config['ribbon-size-screens'] || 9)
var s = state.scale = state.scale
|| ig.scale
var data = ig.data
var images = ig.images
var ribbons = data.ribbon_order
.map(function(gid){
return that.makeRibbon(gid, target, count, state, initial) })
return vdom.h('div.ribbon-set',
{
//key: 'ribbon-set',
style: {
transform: 'scale('+ s +', '+ s +')',
}
}, [
// current image indicator...
vdom.h('div.current-marker'),
// ribbon locator...
vdom.h('div.ribbon-locator',
{
//key: 'ribbon-locator',
},
ribbons),
])
},
// XXX setup handlers (???)
// XXX STUB: make aligning more extensible... (???)
makeRibbon: function(gid, target, count, state, initial){
state = state || {}
var that = this
var ig = this.imagegrid
var current = ig.current
target = target || state.target || current
var size = this.state.tile_size = state.tile_size
|| this.state.tile_size
|| this.getVisibleImageSize('max')
var scale = state.scale = state.scale
|| ig.scale
var data = ig.data
var images = ig.images
// XXX
var ribbons = ig.ribbons
var base = data.base == gid ? '.base' : ''
var imgs = []
this.state = this.state || {}
//this.state.ribbons = this.state.ribbons || {}
// XXX
var size = this.state.tile_size =
this.state.tile_size
|| this.getVisibleImageSize('max')
// calculate offset...
// XXX this accounts for only one offset mode...
// ...make this extensible...
// XXX
var vsize = this.px2vmin(size / scale)
var ref = data.getImage(target, 'before', gid)
var offset = ref == target ? vsize / 2
: ref != null ? vsize
: 0
ref = ref || data.getImage(target, 'after', gid)
// build the images...
//var gids = data.getImages(gid, count, 'total')
var gids = data.getImages(ref, count, 'total')
gids
.forEach(function(gid){
// image...
imgs.push(that.makeImage(gid, size))
// marks...
that.makeImageMarks(gid)
.forEach(function(mark){ imgs.push(mark) })
})
// XXX not sure about this...
var style = initial ? { transform: 'translate3d(120vw, 0, 0)' } : {}
return vdom.h('div.ribbon'+base, {
//key: 'ribbon-'+gid,
// XXX events, hammer, ...???
attributes: {
gid: JSON.stringify(gid)
.replace(/^"(.*)"$/g, '$1'),
},
style: style,
},
imgs)
},
// XXX setup image handlers...
// XXX update image previews...
// XXX update image proportions for rotated images... (???)
makeImage: function(gid, size){
var ig = this.imagegrid
//size = this.state.tile_size = size
size = size
|| this.state.tile_size
|| this.getVisibleImageSize('max')
var data = this.imagegrid.data
var images = this.imagegrid.images || {}
var current = data.current == gid ? '.current' : ''
// resolve group preview cover...
var image = images[gid] || {}
var seen = []
while(image.type == 'group'){
// error, recursive group...
if(seen.indexOf(image.id) >= 0){
image = images.IMAGE_DATA
console.error('Recursive group:', gid)
break
}
seen.push(image.id)
image = that.images[image.cover]
}
var url = ig.images.getBestPreview(gid, size, image, true).url
return vdom.h('div.image'+current, {
// XXX BUG:
// - setting this makes the images some times not change previews...
// - removing this breaks .current class setting...
key: 'image-'+gid,
attributes: {
gid: JSON.stringify(gid)
.replace(/^"(.*)"$/g, '$1'),
orientation: image.orientation,
flipped: image.flipped,
// XXX preview size -- get this onload from image...
//'preview-width': ..,
//'preview-height': ..,
},
style: {
// XXX need to update this onload if changing preview
// of same image...
backgroundImage: 'url("'+ url +'")',
}
})
},
// XXX STUB: make marks handling extensible... (???)
makeImageMarks: function(gid){
var that = this
var marks = []
var tags = this.imagegrid.data.getTags(gid)
// XXX STUB: make this extensible...
tags.indexOf('bookmark') >= 0
&& marks.push('bookmark')
tags.indexOf('selected') >= 0
&& marks.push('selected')
return marks
.map(function(type){
return vdom.h('div.mark.'+(type || ''), {
key: 'mark-'+type+'-'+gid,
attributes: {
gid: JSON.stringify(gid)
.replace(/^"(.*)"$/g, '$1'),
},
})
})
},
// XXX add ability to hook in things like current image marker...
// XXX these need .getImage(..) / .getRibbon(..) / .getRibbonLocator(..)
centerRibbon: function(target){
var ribbon = this.getRibbon(target)
var locator = this.getRibbonLocator()
if(locator.length != 0 && ribbon.length != 0){
var t = ribbon[0].offsetTop
var h = ribbon[0].offsetHeight
locator.transform({ x: 0, y: this.px2vh(-(t + h/2)) + 'vh', z: 0 })
}
return this
},
centerImage: function(target, mode){
target = this.getImage(target)
var ribbon = this.getRibbon(target)
if(ribbon.length != 0){
var l = target[0].offsetLeft
var w = target[0].offsetWidth
var image_offset = mode == 'before' ? 0
: mode == 'after' ? w
: w/2
ribbon.transform({x: -this.px2vmin(l + image_offset) + 'vmin', y: 0, z: 0})
}
return this
},
scale: function(scale){
if(scale){
this.state.scale = scale
this.sync()
} else {
return this.imagegrid.scale
}
},
// XXX not sure how to proceed with these...
setImageHandler: function(evt, handler){
},
setRibbonHandler: function(evt, handler){
},
clear: function(){
this.dom
&& this.dom.remove()
delete this.state
delete this.dom
delete this.vdom
return this
},
// NOTE: virtual-dom architecture is designed around a fast-render-on-demand
// concept, so we build the state on demand...
// XXX get scale from config on initial load...
sync: function(target, size){
var dom = this.dom
var state = this.state ? Object.create(this.state) : {}
target && (state.target = target)
size && (state.count = size)
// build initial state...
if(this.vdom == null){
var n = this.vdom = this.makeView(state, true)
var v = vdom.create(n)
this.imagegrid.dom.append(v)
this.dom = v
// patch state...
} else {
var n = this.makeView(state)
var diff = vdom.diff(this.vdom, n)
vdom.patch(dom, diff)
this.vdom = n
}
return this
},
// XXX should this do a full or partial .clear()???
// XXX BUG: current image indicator resets but does not get shown...
reset: function(){
delete this.dom
delete this.vdom
if(this.state){
delete this.state.tile_size
}
return this
.sync()
},
__init__: function(imagegrid){
this.imagegrid = imagegrid
},
}
var VirtualDOMRibbons =
module.VirtualDOMRibbons =
object.makeConstructor('VirtualDOMRibbons',
VirtualDOMRibbonsClassPrototype,
VirtualDOMRibbonsPrototype)
/*********************************************************************/
var VirtualDomActions = actions.Actions({
get dom(){
return this.__dom },
set dom(value){
this.__dom = value},
get virtualdom(){
return (this.__virtual_dom = this.__virtual_dom || VirtualDOMRibbons(this)) },
load: [
function(data){
return function(){
// recycle the viewer if one is not given specifically...
var viewer = data.viewer
viewer = viewer == null ? this.dom : viewer
if(this.dom == null){
this.dom = viewer
} else {
this.virtualdom.clear()
}
this.reload()
}
}],
reload: ['Interface/Reload viewer',
function(){
this.virtualdom.reset()
this.focusImage()
}],
// XXX this ignores it's args...
refresh: ['Interface/Refresh images without reloading',
function(gids, scale){
this.virtualdom.sync()
this.focusImage()
}],
// XXX
updateRibbon: ['- Interface/Update partial ribbon size',
function(target, w, size, threshold){
target = target instanceof jQuery
? this.virtualdom.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']
|| 9) * w
// XXX DEBUG
//size = 5
// XXX for some reason this does not set the .current class
// on the right image...
this.virtualdom.sync(target, size)
// XXX HACK: this fixes a bug in virtual-dom where .current
// is not synced correctly...
// ...one theory I have is that we change the class
// manually, dom gets diffed and no change is detected
// then the object gets recycled and the .current class
// ends up on a different element...
this.virtualdom.focusImage(target)
this.centerViewer(target)
}],
})
var VirtualDom =
@ -34,7 +479,55 @@ module.VirtualDom = core.ImageGridFeatures.Feature({
actions: VirtualDomActions,
handlers: [],
handlers: [
['clear',
function(){ this.virtualdom.clear() }],
['fitImage toggleSingleImage',
function(){ delete this.virtualdom.state.tile_size }],
// XXX account for fast navigation...
['focusImage.pre',
function(target){
var img = this.virtualdom.getImage(target)
// in-place update...
if(img.length > 0){
// XXX need to account for running out of images and
// not only on the current ribbon...
if(!this.__partial_ribbon_update){
this.__partial_ribbon_update = setTimeout((function(){
delete this.__partial_ribbon_update
this.virtualdom.preventTransitions()
this
.updateRibbon(this.current)
// NOTE: we are doing this manually because we
// are running after the handler is done
// thus missing the base call...
.alignRibbons(null, null, true)
this.virtualdom.restoreTransitions()
}).bind(this), 150)
}
// long-jump...
} else {
if(this.__partial_ribbon_update){
clearTimeout(this.__partial_ribbon_update)
delete this.__partial_ribbon_update
}
this.updateRibbon(target)
}
}],
// marks...
[[
'toggleMark',
'toggleBookmark',
//], function(){ this.updateRibbon() }],
], function(){ this.virtualdom.sync() }],
],
})

View File

@ -45,7 +45,7 @@ function(context, cls, data){
cls = cls instanceof Array ? cls : cls.split(/\s+/g)
// remove old versions...
context.ribbons.viewer.find('.'+ cls.join('.')).remove()
context.dom.find('.'+ cls.join('.')).remove()
// make container...
var controls = $('<div>')
@ -103,7 +103,7 @@ function(context, cls, data){
})
controls
.appendTo(context.ribbons.viewer)
.appendTo(context.dom)
}
// XXX write docs:
@ -121,7 +121,7 @@ function(cls, cfg, parent){
return toggler.Toggler(null,
function(){
parent = parent == null ? this.ribbons.viewer
parent = parent == null ? this.dom
: parent instanceof Function ? parent.call(this)
: parent
return parent.find('.'+ cls.join('.')).length > 0 ? 'on' : 'off'
@ -135,7 +135,7 @@ function(cls, cfg, parent){
&& makeButtonControls(this, cls, config)
} else {
this.ribbons.viewer.find('.'+ cls.join('.')).remove()
this.dom.find('.'+ cls.join('.')).remove()
}
})
}
@ -341,7 +341,7 @@ module.makeUIContainer = function(make){
// is in a state where the window
// is in focus but keys are not
// tracked...
: that.ribbons.viewer.focus()
: that.dom.focus()
})
// Compensate for click focusing the parent dialog when
// a child is created...
@ -427,7 +427,7 @@ var makeDrawer = function(direction){
var that = this
options = options || {}
var parent = options.parentElement
parent = parent ? $(parent) : this.ribbons.viewer
parent = parent ? $(parent) : this.dom
options.direction = direction || 'bottom'
@ -513,7 +513,7 @@ var DialogsActions = actions.Actions({
// element
// null
get modal(){
var modal = this.ribbons.viewer
var modal = this.dom
.find('.modal-widget')
.last()
return modal.data('widget-controller')
@ -540,7 +540,7 @@ var DialogsActions = actions.Actions({
Overlay: ['- Interface/',
makeUIContainer(function(dialog, options){
var that = this
return overlay.Overlay(this.ribbons.viewer, dialog, options)
return overlay.Overlay(this.dom, dialog, options)
// focus top modal on exit...
.on('close', function(){
var o = that.modal
@ -575,7 +575,7 @@ var DialogsActions = actions.Actions({
client: dialog,
dom: $('<div>')
.append(dialog.dom || dialog)
.appendTo(this.ribbons.viewer)
.appendTo(this.dom)
.draggable(),
close: function(func){
if(func){
@ -762,7 +762,7 @@ var DialogsActions = actions.Actions({
toggleOverlayBlur: ['Interface/Dialog overlay blur',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'overlay-blur-enabled',
function(state){ this.config['ui-overlay-blur'] = state }) ],
})
@ -1406,7 +1406,7 @@ module.ContextActionMenu = core.ImageGridFeatures.Feature({
['load',
function(){
var that = this
var viewer = this.ribbons.viewer
var viewer = this.dom
!viewer.data('context-menu')
&& viewer
@ -1528,7 +1528,7 @@ module.Buttons = core.ImageGridFeatures.Feature({
$('.main-buttons.buttons .crop.button sub')
.text(this.crop_stack ? this.crop_stack.length : '') }],
// update zoom button status...
['setScale',
['viewScale',
function(){
$('.secondary-buttons.buttons .zoom.button sub')
.text(Math.round(this.screenwidth)) }],
@ -1897,7 +1897,7 @@ var WidgetTestActions = actions.Actions({
make('c/')
}
var o = overlay.Overlay(this.ribbons.viewer,
var o = overlay.Overlay(this.dom,
browse.makePathList(null, {
'a/*': list,
'b/*': list,
@ -1914,8 +1914,8 @@ var WidgetTestActions = actions.Actions({
// XXX migrate to the dialog framework...
// XXX use this.ribbons.viewer as base...
// XXX BUG: when using this.ribbons.viewer as base some actions leak
// XXX use this.dom as base...
// XXX BUG: when using this.dom as base some actions leak
// between the two viewers...
showTaggedInDrawer: ['- Test/Show tagged in drawer',
function(tag){
@ -1928,7 +1928,7 @@ var WidgetTestActions = actions.Actions({
height: H,
background: 'black',
})
// XXX use this.ribbons.viewer as base...
// XXX use this.dom as base...
// XXX when using viewer zoom and other stuff get leaked...
var widget = drawer.Drawer($('body'),
$('<div>')

View File

@ -22,7 +22,7 @@
* Experimental Features:
* - ui-ribbons-placement
* manage different low level ribbon placement mechanics
* XXX experimental...
* XXX EXPERIMENTAL...
* - auto-single-image
* - auto-ribbon
*
@ -234,21 +234,11 @@ module.ViewerActions = actions.Actions({
'ribbon-align-delay': 50,
},
// Ribbons...
//
// NOTE: this expects that ribbons will maintain .parent.images...
// NOTE: when getting rid of ribbons need to also remove the .parent
// reference...
get ribbons(){
return this.__ribbons },
set ribbons(ribbons){
this.__ribbons = ribbons
ribbons.parent = this
},
// Viewer dom...
dom: null,
// Current image data...
//
// XXX experimental...
get image(){
return this.images && this.images[this.current] },
set image(val){
@ -259,28 +249,21 @@ module.ViewerActions = actions.Actions({
// Scaling...
//
// Normal scale...
// NOTE: .screenwidth / .screenheight are measured in square image blocks...
get scale(){
return this.ribbons != null ? this.ribbons.scale() : null },
return this.viewScale() },
set scale(s){
this.setScale(s) },
// Screen width in image blocks...
//
// NOTE: this will change depending on image block sizing...
// NOTE: this not usable for image blocks of different sizes...
this.viewScale(s) },
get screenwidth(){
return this.ribbons != null ? this.ribbons.getScreenWidthImages() : null },
return this.fitImage('?') },
set screenwidth(n){
this.fitImage(n, false) },
// Screen height in image blocks...
get screenheight(){
return this.ribbons != null ? this.ribbons.getScreenHeightRibbons() : null },
return this.fitRibbon('?') },
set screenheight(n){
this.fitRibbon(n, false) },
// Screen size in image radii on the narrow side of the screen...
// Screen size in image "radii" on the narrow side of the screen...
//
// E.g.
//
@ -289,10 +272,10 @@ module.ViewerActions = actions.Actions({
// min(image.width, image.height)
//
get screenfit(){
if(!this.ribbons || !this.ribbons.viewer){
if(!this.ribbons || !this.dom){
return null
}
var viewer = this.ribbons.viewer
var viewer = this.dom
var W = viewer.width()
var H = viewer.height()
@ -301,7 +284,7 @@ module.ViewerActions = actions.Actions({
: this.screenheight
},
set screenfit(n){
var viewer = this.ribbons.viewer
var viewer = this.dom
var W = viewer.width()
var H = viewer.height()
@ -313,170 +296,12 @@ module.ViewerActions = actions.Actions({
}
},
load: [
function(data){
return function(){
// recycle the viewer if one is not given specifically...
var viewer = data.viewer
viewer = viewer == null && this.ribbons != null
? this.ribbons.viewer
: viewer
if(this.ribbons == null){
this.ribbons = ribbons.Ribbons(viewer, this.images)
// XXX is this correct???
this.ribbons.__image_updaters = [this.updateImage.bind(this)]
} else {
this.ribbons.clear()
this.ribbons.images = this.images
}
this.reload()
}
}],
// NOTE: this will pass the .ribbons.updateData(..) a custom ribbon
// updater if one is defined here as .updateRibbon(target) action
//
// XXX HACK: two sins:
// - actions.updateRibbon(..) and ribbons.updateRibbon(..)
// are NOT signature compatible...
// - we depend on the internals of a custom add-on feature
reload: ['Interface/Reload viewer',
function(force){
// full reload...
if(force == 'full'){
//this.stop()
/*
killAllWorkers()
.done(function(){
reload()
})
*/
location.reload()
}
this.ribbons.preventTransitions()
// NOTE: this essentially sets the update threshold to 0...
// XXX this should be a custom arg...
force = force ? 0 : null
return function(){
// see if we've got a custom ribbon updater...
var that = this
var settings = this.updateRibbon != null
// XXX this should be: { updateRibbon: this.updateRibbon.bind(this) }
? { updateRibbon: function(_, ribbon){
return that.updateRibbon(ribbon, null, null, force)
} }
: null
this.ribbons.updateData(this.data, settings)
this
// XXX should this be here???
.refresh()
.focusImage()
// XXX HACK to make browser redraw images...
this.scale = this.scale
this.ribbons.restoreTransitions()
}
}],
// NOTE: this will trigger .updateImage hooks...
refresh: ['Interface/Refresh images without reloading',
function(gids, scale){
gids = gids || '*'
var size = scale != null ?
this.ribbons.getVisibleImageSize('min', scale)
: null
this.ribbons.updateImage(gids, null, size)
}],
clear: [
function(){ this.ribbons && this.ribbons.clear() }],
clone: [function(full){
return function(res){
if(this.ribbons){
// NOTE: this is a bit wasteful as .ribbons will clone
// their ref to .images that we will throw away...
res.ribbons = this.ribbons.clone()
res.ribbons.images = res.images
}
}
}],
replaceGid: [
function(from, to){
return function(res){
res && this.ribbons.replaceGid(from, to)
}
}],
// This is called by .ribbons, the goal is to use it to hook into
// image updating from features and extensions...
//
// NOTE: not intended for calling manually, use .refresh(..) instead...
//
// XXX experimental...
// ...need this to get triggered by .ribbons
// at this point manually triggering this will not do anything...
// XXX problem: need to either redesign this or distinguish from
// other actions as I keep calling it expecting results...
// XXX hide from user action list... (???)
updateImage: ['- Interface/Update image (do not use directly)',
'This is called by .refresh(..) and intended for use as an '
+'trigger for handlers, and not as a user-callable acation.',
core.notUserCallable(function(gid, image){
// This is the image update protocol root function
//
// Not for direct use.
})],
// NOTE: this not used directly, mainly designed as a utility to be
// used for various partial ribbon implementations...
// XXX do we handle off-screen ribbons here???
resizeRibbon: ['- Interface/Resize ribbon to n images',
function(target, size){
size = size
|| (this.config['ribbon-size-screens'] * this.screenwidth)
|| (5 * this.screenwidth)
var data = this.data
var ribbons = this.ribbons
// localize transition prevention...
// NOTE: we can't get ribbon via target directly here as
// the target might not be loaded...
var r_gid = data.getRibbon(target)
if(r_gid == null){
return
}
// NOTE: for the initial load this may be empty...
var r = ribbons.getRibbon(r_gid)
// XXX do we need to for example ignore unloaded (r.length == 0)
// ribbons here, for example not load ribbons too far off
// screen??
ribbons
.preventTransitions(r)
.updateRibbon(
data.getImages(target, size, 'total'),
r_gid,
target)
.restoreTransitions(r, true)
}],
// General UI stuff...
// NOTE: this is applicable to all uses...
toggleTheme: ['Interface/Theme/Viewer theme',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
function(){ return this.config.themes },
function(state){ this.config.theme = state }) ],
lighterTheme: ['Interface/Theme/Lighter theme',
@ -493,17 +318,65 @@ module.ViewerActions = actions.Actions({
}],
toggleRibbonTheme: ['Interface/Theme/Ribbon theme',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
function(){ return this.config['ribbon-themes'] },
function(state){ this.config['ribbon-theme'] = state }) ],
toggleRibbonImageSepators: ['Interface/Theme/Ribbon image separators',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'ribbon-image-separators',
function(state){ this.config['ribbon-image-separators'] = state }) ],
// ribbon aligning...
// Navigation...
//
// NOTE: these prioritize whole images, i.e. each image will at least
// once be fully shown.
prevScreen: ['Navigate/Screen width back',
function(){
// NOTE: the 0.2 is added to compensate for alignment/scaling
// errors -- 2.99 images wide counts as 3 while 2.5 as 2.
var w = Math.floor(this.screenwidth + 0.2)
w += (w % 2) - 1
this.prevImage(w)
}],
nextScreen: ['Navigate/Screen width forward',
function(){
var w = Math.floor(this.screenwidth + 0.2)
w += (w % 2) - 1
this.nextImage(w)
}],
// Renderer API...
/*/ XXX do we need these here???
centerImage: ['- Interface/Center an image in ribbon horizontally',
function(target, align, offset, scale){
}],
centerRibbon: ['- Interface/Center a ribbon vertically',
function(target){
}],
ribbonRotation: ['- Interface|Ribbon/',
function(a){
}],
viewScale: ['- Zoom/',
function(scale){
}],
fitImage: ['Zoom/Fit image',
function(count, overflow){
}],
fitRibbon: ['Zoom/Fit ribbon vertically',
function(count, whole){
}],
//*/
// ribbon aligning and centering...
centerViewer: ['- Interface/Center the viewer',
function(target){
this
.centerImage(target)
.centerRibbon(target) }],
alignRibbons: ['Interface/Align ribbons',
function(target, scale, now){
if(target == 'now'){
@ -535,6 +408,7 @@ module.ViewerActions = actions.Actions({
// order...
// XXX skip off-screen ribbons (???)
// XXX should the timeout be configurable???
// XXX remove dependency on .ribbons
alignByOrder: ['Interface/Align ribbons by image order',
function(target, scale, now){
if(target == 'now'){
@ -649,34 +523,8 @@ module.ViewerActions = actions.Actions({
//}, 0)
}],
// NOTE: this will align only a single image...
// XXX do we need these low level primitives here???
centerImage: ['- Interface/Center an image in ribbon horizontally',
function(target, align, offset, scale){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
: target
// align current ribbon...
this.ribbons.centerImage(target, align, offset, scale)
}],
centerRibbon: ['- Interface/Center a ribbon vertically',
function(target){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
: target
// align current ribbon...
this.ribbons.centerRibbon(target)
}],
centerViewer: ['- Interface/Center the viewer',
function(target){
this
.centerImage(target)
.centerRibbon(target)
}],
// Viewer/window resize event...
resizingWindow: ['- Interface/',
core.doc`This is called by the window resize event handler...
@ -692,57 +540,13 @@ module.ViewerActions = actions.Actions({
//
// Not for direct use.
})],
focusImage: [
function(target, list){
return function(){
this.ribbons.focusImage(this.data != null ? this.current : target) } }],
focusRibbon: [
function(target, mode){
mode = mode || this.config['ribbon-focus-mode']
var c = this.data.getRibbonOrder()
var i = this.data.getRibbonOrder(target)
// NOTE: we are not changing the direction here based on
// this.direction as swap will confuse the user...
var direction = c < i ? 'before' : 'after'
if(mode == 'visual'){
var ribbons = this.ribbons
var r = this.data.getRibbon(target)
var t = ribbons.getImageByPosition('current', r)
if(t.length > 1){
t = t.eq(direction == 'before' ? 0 : 1)
}
t = ribbons.getElemGID(t)
this.focusImage(t, r)
}
}],
// NOTE: these prioritize whole images, i.e. each image will at least
// once be fully shown.
prevScreen: ['Navigate/Screen width back',
function(){
// NOTE: the 0.2 is added to compensate for alignment/scaling
// errors -- 2.99 images wide counts as 3 while 2.5 as 2.
var w = Math.floor(this.screenwidth + 0.2)
w += (w % 2) - 1
this.prevImage(w)
}],
nextScreen: ['Navigate/Screen width forward',
function(){
var w = Math.floor(this.screenwidth + 0.2)
w += (w % 2) - 1
this.nextImage(w)
}],
// XXX hide from user action list... (???)
// XXX need to check if a transition is running and delay timeout...
// Zoom/scale protocol...
//
// Events...
// NOTE: the implementation needs to call .resizingDone(..) when all
// animations are done...
resizing: ['- Zoom/Scale root protocol action (not for direct use)',
core.doc`Zooming/scaling root action...
@ -785,34 +589,7 @@ module.ViewerActions = actions.Actions({
//
// This will never be used directly, but will wrap protocol user
// functions.
//
// As an example see: .setScale(..)
var that = this
// stop currently running transition...
this.ribbons.scale(this.ribbons.scale())
// transitionend handler...
if(!this.__resize_handler){
this.__resize_handler = function(){
that.__post_resize
&& that.resizingDone()
delete that.__post_resize
}
}
this.ribbons.getRibbonSet()
.off('transitionend', this.__resize_handler)
.on('transitionend', this.__resize_handler)
// timeout handler...
this.__post_resize && clearTimeout(this.__post_resize)
return function(){
this.__post_resize = setTimeout(
this.__resize_handler,
this.config['resize-done-timeout'] || 300)
}
})],
resizingDone: ['- Zoom/scale post-transition protocol action (not for direct use)',
core.doc`Zooming/scaling post-transition action...
@ -835,56 +612,6 @@ module.ViewerActions = actions.Actions({
// be called before the transition is done.
})],
// Zoom/scale protocol actions...
setScale: ['- Zoom/',
function(scale){
this.resizing.chainCall(this, function(){
this.ribbons && scale && this.ribbons.scale(scale)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', scale)
}, 'scale', scale)
}],
fitOrig: ['Zoom/Fit to original scale',
function(){
this.resizing.chainCall(this, function(){
this.ribbons.scale(1)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', 1)
}, 'scale', 1)
}],
// NOTE: if this gets a count argument it will fit count images,
// default is one.
// NOTE: this will add .config['fit-overflow'] to odd counts if no
// overflow if passed.
// ...this is done to add ability to control scroll indication.
fitImage: ['Zoom/Fit image',
function(count, overflow){
this.resizing.chainCall(this, function(){
if(count != null){
overflow = overflow == false ? 0 : overflow
var o = overflow != null ? overflow
: count % 2 != 1 ? 0
: (this.config['fit-overflow'] || 0)
count += o
}
this.ribbons.fitImage(count)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', this.ribbons.getScreenWidthImages(1) / count)
}, 'screenwidth', count, overflow)
}],
// NOTE: this does not account for ribbon spacing...
fitRibbon: ['Zoom/Fit ribbon vertically',
function(count, whole){
this.resizing.chainCall(this, function(){
this.ribbons.fitRibbon(count, whole)
// NOTE: we pass explicit scale here to compensate for animation...
this.refresh('*', this.ribbons.getScreenHeightRibbons(1, whole) / count)
}, 'screenheight', count, whole)
}],
// Zooming...
//
// Zooming is done by multiplying the current scale by .config['zoom-step']
// and rounding to nearest discrete number of images to fit on screen.
zoomIn: ['Zoom/Zoom in',
@ -917,24 +644,16 @@ module.ViewerActions = actions.Actions({
}],
// Scale presets...
//
fitOrig: ['Zoom/Fit to original scale',
function(){ this.viewScale(1) }],
fitMax: ['Zoom/Fit the maximum number of images',
function(){ this.fitImage(this.config['max-screen-images']) }],
function(){ this.screenwidth = this.config['max-screen-images'] }],
fitScreen: ['Zoom/Fit image to screen',
function(){ this.screenfit = 1 }],
// ribbon rotation...
// Ribbon rotation...
//
ribbonRotation: ['- Interface|Ribbon/',
function(a){
if(arguments.length > 0){
this.ribbons.rotate(a)
} else {
return this.ribbons.rotate() || 0
}
}],
// Rotate ribbon CW/CCW...
//
// Rotate ribbon (default step)
@ -957,7 +676,7 @@ module.ViewerActions = actions.Actions({
function(){ this.ribbonRotation(0) }],
// XXX experimental: not sure if this is the right way to go...
// XXX EXPERIMENTAL: not sure if this is the right way to go...
// XXX make this play nice with crops...
// ...should this be a crop???
toggleRibbonList: ['Interface|Ribbon/Ribbons as images view',
@ -992,6 +711,7 @@ module.Viewer = core.ImageGridFeatures.Feature({
'base',
'workspace',
'introspection',
'ui-render',
],
suggested: [
// XXX is this the right way???
@ -1196,7 +916,7 @@ core.ImageGridFeatures.Feature({
// manage the .crop-mode css class...
['crop uncrop',
function(){
this.ribbons.viewer[this.cropped ?
this.dom[this.cropped ?
'addClass'
: 'removeClass']('crop-mode')
}],
@ -1546,11 +1266,11 @@ module.Cursor = core.ImageGridFeatures.Feature({
actions: actions.Actions({
toggleHiddenCursor: ['Interface/Cursor hidden',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'cursor-hidden',
function(state){
var that = this
var viewer = this.ribbons.viewer
var viewer = this.dom
if(state == 'on'){
var x, y
@ -1619,12 +1339,12 @@ module.Cursor = core.ImageGridFeatures.Feature({
//
toggleAutoHideCursor: ['Interface/Cursor auto-hide',
toggler.CSSClassToggler(
function(){ return this.ribbons.viewer },
function(){ return this.dom },
'cursor-autohide',
function(state){
var that = this
var viewer = this.ribbons.viewer
var viewer = this.dom
// NOTE: this is handled by the keyboard feature...
var kb_target = this.__keyboard_event_source || $(window)
@ -1654,7 +1374,7 @@ module.Cursor = core.ImageGridFeatures.Feature({
: -1
if(timeout && timeout > 0){
m_timer = setTimeout(function(){
var viewer = that.ribbons.viewer
var viewer = that.dom
// auto-hide is off -- restore...
if(!viewer.hasClass('cursor-autohide')){
@ -1680,7 +1400,7 @@ module.Cursor = core.ImageGridFeatures.Feature({
// avoid this from delaying the keyboard handler...
kb_timer = setTimeout(function(){
kb_timer = null
var viewer = that.ribbons.viewer
var viewer = that.dom
// get key...
var key = keyboard.normalizeKey(
@ -1842,7 +1562,7 @@ var ControlActions = actions.Actions({
// nothing to do...
null
: (this.ribbons
&& this.ribbons.viewer
&& this.dom
//&& this.ribbons.getRibbon().data('hammer') ? 'handling-click' : 'none' },
&& this.ribbons.getRibbon().hasClass('clickable')) ?
'handling-click'
@ -1978,7 +1698,7 @@ var ControlActions = actions.Actions({
var cl = central && central.offset().left
var w = central && central.outerWidth(true)
var W = this.ribbons.viewer.width()
var W = this.dom.width()
var vmin = Math.min(
document.body.offsetWidth,
document.body.offsetHeight)
@ -2044,7 +1764,7 @@ var ControlActions = actions.Actions({
// nothing to do...
null
: (this.ribbons
&& this.ribbons.viewer
&& this.dom
&& this.ribbons.getRibbon().hasClass('draggable')) ?
'handling-pan'
: 'none' },
@ -2281,8 +2001,8 @@ var ControlActions = actions.Actions({
// nothing to do...
null
: (this.ribbons
&& this.ribbons.viewer
&& this.ribbons.viewer.hasClass('mouse-wheel-scroll')) ?
&& this.dom
&& this.dom.hasClass('mouse-wheel-scroll')) ?
'handling-mouse-wheel'
: 'none' },
'handling-mouse-wheel',
@ -2318,7 +2038,7 @@ var ControlActions = actions.Actions({
var rgid = this.ribbons.getElemGID(r)
// XXX vertical scroll...
this.ribbons.viewer
this.dom
.on('wheel', function(){
})
@ -2357,7 +2077,7 @@ var ControlActions = actions.Actions({
// on...
if(state == 'on'){
this.ribbons.viewer.addClass('mouse-wheel-scroll')
this.dom.addClass('mouse-wheel-scroll')
// NOTE: we are resetting this to avoid multiple setting
// handlers...
this.off('updateRibbon', setup)
@ -2368,7 +2088,7 @@ var ControlActions = actions.Actions({
// off...
} else {
this.ribbons.viewer.removeClass('mouse-wheel-scroll')
this.dom.removeClass('mouse-wheel-scroll')
this.off('updateRibbon', setup)
this.data.ribbon_order.forEach(function(gid){
@ -2390,13 +2110,13 @@ var ControlActions = actions.Actions({
return state ?
null
: (this.ribbons
&& this.ribbons.viewer
&& this.ribbons.viewer.data('hammer')) ?
&& this.dom
&& this.dom.data('hammer')) ?
'handling-swipes'
: 'none' },
'handling-swipes',
function(state){
var viewer = this.ribbons.viewer
var viewer = this.dom
// on...
if(state == 'on'){
@ -2520,7 +2240,7 @@ module.Control = core.ImageGridFeatures.Feature({
}],
// if panned image is off screen, center it...
['setScale',
['viewScale',
function(){
var that = this
Object.keys(this.data.ribbons).forEach(function(r){
@ -2617,7 +2337,7 @@ module.PreviewFilters = core.ImageGridFeatures.Feature({
var cls = filters[state]
var classes = Object.values(filters)
.filter(function(c){ return c != cls })
this.ribbons.viewer
this.dom
.find('.'+ classes.join(', .'))
.removeClass(classes.join(' '))
@ -2641,7 +2361,7 @@ module.PreviewFilters = core.ImageGridFeatures.Feature({
/*********************************************************************/
// XXX experimental...
// XXX EXPERIMENTAL...
// ...not sure if this is the right way to go...
// XXX need to get the minimal size and not the width as results will

View File

@ -102,22 +102,27 @@ $(function(){
m = Object.keys(m).filter(function(e){ return m[e] != null })
console.log('Modules (%d):', m.length, m)
try {
// setup actions...
window.ig =
window.ImageGrid =
viewer.ImageGridFeatures
.setup([
'viewer-testing',
// setup actions...
window.ig =
window.ImageGrid =
viewer.ImageGridFeatures
.setup([
'viewer-testing',
'demo',
'demo',
// XXX this is not for production...
'experiments',
// XXX this is not for production...
'experiments',
//'-commandline',
//'-ui-partial-ribbons',
])
//'-commandline',
//'-ui-partial-ribbons',
])
} catch(err){
console.error(err)
return
}
// used to switch experimental actions on (set to true) or off (unset or false)...
@ -130,6 +135,9 @@ $(function(){
&& console.warn('Features excluded (%d):',
ig.features.excluded.length,
ig.features.excluded)
Object.keys(ig.features.missing).length > 0
&& console.warn('Features disabled (%d):',
ig.features.missing)
ig.features.disabled.length > 0
&& console.log('Features disabled (%d):',
ig.features.disabled.length,