some refactoring, tweaking and fixes...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-04-18 23:15:48 +03:00
parent c455c21230
commit 2d19efaef7
5 changed files with 354 additions and 275 deletions

View File

@ -20,6 +20,7 @@ require('features/history')
require('features/app')
require('features/peer')
require('features/ui')
require('features/ui-partial-ribbons-precache')
require('features/ui-partial-ribbons')
require('features/ui-partial-ribbons-2')
require('features/ui-single-image')

View File

@ -21,15 +21,21 @@ var core = require('features/core')
var PartialRibbonsActions = actions.Actions({
config: {
// Number of screen widths to load...
'ribbon-size-screens': 7,
// the amount of screen widths to keep around the current image...
// Amount of screen widths to keep around the current image...
'ribbon-update-threshold': 1.2,
// the oversize multiplier limit when we resize the ribbon down...
// Oversize multiplier limit when we resize the ribbon down...
'ribbon-resize-threshold': 2,
// Sets size of ribbons in single image mode...
'ribbons-resize-single-image': 13,
},
// XXX preload???
updateRibbon: ['- Interface/Update partial ribbon size',
function(target, w, size, threshold){
target = target instanceof jQuery
@ -73,15 +79,52 @@ var PartialRibbonsActions = actions.Actions({
var na = gids.slice(gids.indexOf(target)+1).length
var pa = gids.slice(0, gids.indexOf(target)).length
//console.log(`-- loaded: ${loaded} size: ${size}`)
// full resize...
if(threshold == 0
// ribbon not loaded...
if(r.length == 0
|| img.length == 0
// ribbon shorter than we expect...
|| (loaded < size && na + pa > loaded)
// ribbon too long...
|| loaded > size * threshold){
console.log('RESIZE')
this.resizeRibbon(target, size)
//*/
/*/ XXX long jump condition......
if(img.length != 0
&& (r.length == 0
// ribbon shorter than we expect...
|| (loaded < size && na + pa > loaded)
// ribbon too long...
|| loaded > size * threshold)){
console.log('RESIZE')
this.resizeRibbon(target, size)
// image is off screen -- align off then animate...
// 1) initial state
// T <- [---|---x---|---------------]
// 2) load new state but align off screen
// [-------T-------|-------|---]
// 3) animate
// [---|---T---|---------------]
// XXX this makes the draw worse...
} else if(img.length == 0 ){
console.log('LONG-JUMP')
r.length == 0 ?
// ribbon not loaded...
this.resizeRibbon(target, size)
// simply update...
: this.ribbons
.preventTransitions(r)
.updateRibbonInPlace(
gids,
r_gid,
data.getImageOrder(this.current) > data.getImageOrder(target) ?
gids[gids.length - w]
: gids[w])
.restoreTransitions(r, true)
//*/
// in-place update...
// passed threshold on the right...
@ -90,6 +133,7 @@ var PartialRibbonsActions = actions.Actions({
|| (pl < update_threshold && pa > pl)
// loaded more than we need by threshold...
|| nl + pl + 1 > size + update_threshold){
console.log('UPDATE')
r.length == 0 ?
// ribbon not loaded...
this.resizeRibbon(target, size)
@ -113,36 +157,6 @@ var PartialRibbonsActions = actions.Actions({
.restoreTransitions(r, true)
}
}],
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)
}],
})
var PartialRibbons =
@ -157,6 +171,9 @@ module.PartialRibbons = core.ImageGridFeatures.Feature({
depends: [
'ui',
],
suggested: [
'ui-partial-ribbons-precache',
],
actions: PartialRibbonsActions,
@ -172,7 +189,13 @@ module.PartialRibbons = core.ImageGridFeatures.Feature({
}],
['resizing.post',
function(_, unit, size){
if(unit == 'scale'){
// keep constant size in single image...
if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){
this.updateRibbon(
'current',
this.config['ribbons-resize-single-image'] || 13)
} else if(unit == 'scale'){
this.updateRibbon('current', this.screenwidth / size || 1)
} else if(unit == 'screenwidth'){

View File

@ -0,0 +1,245 @@
/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var actions = require('lib/actions')
var features = require('lib/features')
var core = require('features/core')
/*********************************************************************/
var PreCacheActions = actions.Actions({
config: {
// How many non-adjacent images to preload...
'preload-radius': 5,
// Sources to preload...
'preload-sources': ['bookmark', 'selected'],
},
// NOTE: this will not work from chrome when loading from a local fs...
// XXX experimental...
startCacheWorker: ['Interface/',
function(){
// a worker is started already...
if(this.cacheWorker != null){
return
}
var b = new Blob([[
'addEventListener(\'message\', function(e) {',
' var urls = e.data',
' urls = urls.constructor !== Array ? [urls] : urls',
' var l = urls.length',
' urls.forEach(function(url){',
' var xhr = new XMLHttpRequest()',
' xhr.responseType = \'blob\'',
/*
' xhr.onload = xhr.onerror = function(){',
' l -= 1',
' if(l <= 0){',
' postMessage({status: \'done.\', urls: urls})',
' }',
' }',
*/
' xhr.open(\'GET\', url, true)',
' xhr.send()',
' })',
'}, false)',
].join('\n')])
var url = URL.createObjectURL(b)
this.cacheWorker = new Worker(url)
this.cacheWorker.url = url
}],
stopCacheWorker: ['Interface/',
function(){
if(this.cacheWorker){
this.cacheWorker.terminate()
URL.revokeObjectURL(this.cacheWorker.url)
delete this.cacheWorker
}
}],
// Pre-load images...
//
// Sources supported:
// <tag> - pre-load images tagged with <tag>
// (default: ['bookmark', 'selected'])
// <ribbon-gid> - pre-cache from a specific ribbon
// 'ribbon' - pre-cache from current ribbon
// 'order' - pre-cache from images in order
//
// NOTE: workers when loaded from file:// in a browser context
// will not have access to local images...
//
// XXX need a clear strategy to run this...
// XXX might be a good idea to make the worker queue the lists...
// ...this will need careful prioritization logic...
// - avoid loading the same url too often
// - load the most probable urls first
// - next targets
// - next/prev
// .preCacheJumpTargets(target, 'ribbon', this.screenwidth)
// - next/prev marked/bookmarked/order
// .preCacheJumpTargets(target, 'marked')
// .preCacheJumpTargets(target, 'bookmarked')
// .preCacheJumpTargets(target, 'order')
// - next/prev screen
// .preCacheJumpTargets(target, 'ribbon',
// this.config['preload-radius'] * this.screenwidth)
// - next/prev ribbon
// .preCacheJumpTargets(target, this.data.getRibbon(target, 1))
// .preCacheJumpTargets(target, this.data.getRibbon(target, -1))
// - next blocks
// - what resize ribbon does...
// XXX coordinate this with .resizeRibbon(..)
// XXX make this support an explicit list of gids....
// XXX should this be here???
preCacheJumpTargets: ['- Interface/Pre-cache potential jump target images',
function(target, sources, radius, size){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
// NOTE: data.getImage(..) can return null at start or end
// of ribbon, thus we need to account for this...
: (this.data.getImage(target)
|| this.data.getImage(target, 'after'))
sources = sources || this.config['preload-sources'] || ['bookmark', 'selected']
sources = sources.constructor !== Array ? [sources] : sources
radius = radius || this.config['preload-radius'] || 9
var that = this
// get preview...
var _getPreview = function(c){
return that.images[c]
&& that.images.getBestPreview(c, size, true).url
}
// get a set of paths...
// NOTE: we are also ordering the resulting gids by their
// distance from target...
var _get = function(i, lst, source, radius, oddity, step){
var found = oddity
var max = source.length
for(var j = i+step; (step > 0 && j < max) || (step < 0 && j >= 0); j += step){
var c = source[j]
if(c == null || that.images[c] == null){
continue
}
// build the URL...
lst[found] = _getPreview(c)
found += 2
if(found >= radius*2){
break
}
}
}
// run the actual preload...
var _run = function(){
sources.forEach(function(tag){
// order...
if(tag == 'order'){
var source = that.data.order
// current ribbon...
}else if(tag == 'ribbon'){
var source = that.data.ribbons[that.data.getRibbon()]
// ribbon-gid...
} else if(tag in that.data.ribbons){
var source = that.data.ribbons[tag]
// nothing tagged then nothing to do...
} else if(that.data.tags == null
|| that.data.tags[tag] == null
|| that.data.tags[tag].length == 0){
return
// tag...
} else {
var source = that.data.tags[tag]
}
size = size || that.ribbons.getVisibleImageSize()
var i = that.data.order.indexOf(target)
var lst = []
// get the list of URLs before and after current...
_get(i ,lst, source, radius, 0, 1)
_get(i, lst, source, radius, 1, -1)
// get target preview in case the target is not loaded...
var p = _getPreview(that.data.getImage(target))
p && lst.splice(0, 0, p)
// web worker...
if(that.cacheWorker != null){
that.cacheWorker.postMessage(lst)
// async inline...
} else {
// do the actual preloading...
lst.forEach(function(url){
var img = new Image()
img.src = url
})
}
})
}
if(that.cacheWorker != null){
_run()
} else {
setTimeout(_run, 0)
}
}],
})
var PreCache =
module.PreCache = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-partial-ribbons-precache',
depends: [
'ui',
],
actions: PreCacheActions,
handlers: [
['focusImage.post',
function(_, target){
this.preCacheJumpTargets(target) }],
/*/
['resizing.pre',
function(unit, size){
this.preCacheJumpTargets() }],
//*/
],
})
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })

View File

@ -24,212 +24,18 @@ var core = require('features/core')
// XXX updateRibbon(..) is not signature compatible with data.updateRibbon(..)
var PartialRibbonsActions = actions.Actions({
config: {
// number of screen widths to load...
// Number of screen widths to load...
'ribbon-size-screens': 7,
// number of screen widths to edge to trigger reload...
// Number of screen widths to edge to trigger reload...
'ribbon-resize-threshold': 1.5,
// timeout before a non-forced ribbon size update happens after
// Timeout before a non-forced ribbon size update happens after
// the action...
// NOTE: if set to null, the update will be sync...
'ribbon-update-timeout': 120,
// how many non-adjacent images to preload...
'preload-radius': 5,
// sources to preload...
'preload-sources': ['bookmark', 'selected'],
},
// NOTE: this will not work from chrome when loading from a local fs...
// XXX experimental...
startCacheWorker: ['Interface/',
function(){
// a worker is started already...
if(this.cacheWorker != null){
return
}
var b = new Blob([[
'addEventListener(\'message\', function(e) {',
' var urls = e.data',
' urls = urls.constructor !== Array ? [urls] : urls',
' var l = urls.length',
' urls.forEach(function(url){',
' var xhr = new XMLHttpRequest()',
' xhr.responseType = \'blob\'',
/*
' xhr.onload = xhr.onerror = function(){',
' l -= 1',
' if(l <= 0){',
' postMessage({status: \'done.\', urls: urls})',
' }',
' }',
*/
' xhr.open(\'GET\', url, true)',
' xhr.send()',
' })',
'}, false)',
].join('\n')])
var url = URL.createObjectURL(b)
this.cacheWorker = new Worker(url)
this.cacheWorker.url = url
}],
stopCacheWorker: ['Interface/',
function(){
if(this.cacheWorker){
this.cacheWorker.terminate()
URL.revokeObjectURL(this.cacheWorker.url)
delete this.cacheWorker
}
}],
// Pre-load images...
//
// Sources supported:
// <tag> - pre-load images tagged with <tag>
// (default: ['bookmark', 'selected'])
// <ribbon-gid> - pre-cache from a specific ribbon
// 'ribbon' - pre-cache from current ribbon
// 'order' - pre-cache from images in order
//
// NOTE: workers when loaded from file:// in a browser context
// will not have access to local images...
//
// XXX need a clear strategy to run this...
// XXX might be a good idea to make the worker queue the lists...
// ...this will need careful prioritization logic...
// - avoid loading the same url too often
// - load the most probable urls first
// - next targets
// - next/prev
// .preCacheJumpTargets(target, 'ribbon', this.screenwidth)
// - next/prev marked/bookmarked/order
// .preCacheJumpTargets(target, 'marked')
// .preCacheJumpTargets(target, 'bookmarked')
// .preCacheJumpTargets(target, 'order')
// - next/prev screen
// .preCacheJumpTargets(target, 'ribbon',
// this.config['preload-radius'] * this.screenwidth)
// - next/prev ribbon
// .preCacheJumpTargets(target, this.data.getRibbon(target, 1))
// .preCacheJumpTargets(target, this.data.getRibbon(target, -1))
// - next blocks
// - what resize ribbon does...
// XXX coordinate this with .resizeRibbon(..)
// XXX make this support an explicit list of gids....
// XXX should this be here???
preCacheJumpTargets: ['- Interface/Pre-cache potential jump target images',
function(target, sources, radius, size){
target = target instanceof jQuery
? this.ribbons.getElemGID(target)
// NOTE: data.getImage(..) can return null at start or end
// of ribbon, thus we need to account for this...
: (this.data.getImage(target)
|| this.data.getImage(target, 'after'))
sources = sources || this.config['preload-sources'] || ['bookmark', 'selected']
sources = sources.constructor !== Array ? [sources] : sources
radius = radius || this.config['preload-radius'] || 9
var that = this
// get preview...
var _getPreview = function(c){
return that.images[c]
&& that.images.getBestPreview(c, size, true).url
}
// get a set of paths...
// NOTE: we are also ordering the resulting gids by their
// distance from target...
var _get = function(i, lst, source, radius, oddity, step){
var found = oddity
var max = source.length
for(var j = i+step; (step > 0 && j < max) || (step < 0 && j >= 0); j += step){
var c = source[j]
if(c == null || that.images[c] == null){
continue
}
// build the URL...
lst[found] = _getPreview(c)
found += 2
if(found >= radius*2){
break
}
}
}
// run the actual preload...
var _run = function(){
sources.forEach(function(tag){
// order...
if(tag == 'order'){
var source = that.data.order
// current ribbon...
}else if(tag == 'ribbon'){
var source = that.data.ribbons[that.data.getRibbon()]
// ribbon-gid...
} else if(tag in that.data.ribbons){
var source = that.data.ribbons[tag]
// nothing tagged then nothing to do...
} else if(that.data.tags == null
|| that.data.tags[tag] == null
|| that.data.tags[tag].length == 0){
return
// tag...
} else {
var source = that.data.tags[tag]
}
size = size || that.ribbons.getVisibleImageSize()
var i = that.data.order.indexOf(target)
var lst = []
// get the list of URLs before and after current...
_get(i ,lst, source, radius, 0, 1)
_get(i, lst, source, radius, 1, -1)
// get target preview in case the target is not loaded...
var p = _getPreview(that.data.getImage(target))
p && lst.splice(0, 0, p)
// web worker...
if(that.cacheWorker != null){
that.cacheWorker.postMessage(lst)
// async inline...
} else {
// do the actual preloading...
lst.forEach(function(url){
var img = new Image()
img.src = url
})
}
})
}
if(that.cacheWorker != null){
_run()
} else {
setTimeout(_run, 0)
}
}],
// NOTE: this will force sync resize if one of the following is true:
// - the target is not loaded
// - we are less than screen width from the edge
@ -312,39 +118,6 @@ var PartialRibbonsActions = actions.Actions({
}
}
}],
// 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
// 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
}
// localize transition prevention...
// 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),
r_gid,
target)
.restoreTransitions(r, true)
}]
})
// NOTE: I do not fully understand it yet, but PartialRibbons must be
@ -370,6 +143,9 @@ module.PartialRibbons = core.ImageGridFeatures.Feature({
depends: [
'ui'
],
suggested: [
'ui-partial-ribbons-precache',
],
actions: PartialRibbonsActions,
@ -384,14 +160,15 @@ module.PartialRibbons = core.ImageGridFeatures.Feature({
this.updateRibbon(target)
}],
['focusImage.post',
function(_, target){
this.preCacheJumpTargets(target)
}],
['resizing.pre',
function(unit, size){
if(unit == 'scale'){
// keep constant size in single image...
if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){
this.updateRibbon(
'current',
this.config['ribbons-resize-single-image'] || 13)
} else if(unit == 'scale'){
this.updateRibbon('current', this.screenwidth / size || 1)
} else if(unit == 'screenwidth'){
@ -412,8 +189,6 @@ module.PartialRibbons = core.ImageGridFeatures.Feature({
this.updateRibbon('current', nw)
}
//this.preCacheJumpTargets()
}],
],
})

View File

@ -426,6 +426,41 @@ module.ViewerActions = actions.Actions({
})],
// 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',