mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 02:10:08 +00:00
minor tweaks + more introspection + moved partial ribbons to a separate module...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
abe5277f72
commit
19809ac771
@ -17,6 +17,7 @@ require('features/location')
|
||||
require('features/history')
|
||||
require('features/app')
|
||||
require('features/ui')
|
||||
require('features/ui-partial-ribbons')
|
||||
require('features/ui-single-image')
|
||||
require('features/ui-chrome')
|
||||
require('features/keyboard')
|
||||
|
||||
@ -925,6 +925,7 @@ module.ImageGroup = core.ImageGridFeatures.Feature({
|
||||
|
||||
// full features base...
|
||||
core.ImageGridFeatures.Feature('base-full', [
|
||||
'introspection',
|
||||
'base',
|
||||
'tags',
|
||||
'sort',
|
||||
|
||||
@ -226,6 +226,42 @@ module.LifeCycle = ImageGridFeatures.Feature({
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Introspection...
|
||||
|
||||
// Indicate that an action is not intended for direct use...
|
||||
//
|
||||
// NOTE: this will not do anything but mark the action.
|
||||
var notUserCallable =
|
||||
module.notUserCallable = function(func){
|
||||
func.__not_user_callable__ = true
|
||||
return func
|
||||
}
|
||||
|
||||
|
||||
var IntrospectionActions = actions.Actions({
|
||||
// user-callable actions...
|
||||
get useractions(){
|
||||
return this.actions.filter(this.isUserCallable.bind(this)) },
|
||||
|
||||
// check if action is callable by user...
|
||||
isUserCallable: ['- System/',
|
||||
actions.doWithRootAction(function(action){
|
||||
return action.__not_user_callable__ != true })],
|
||||
})
|
||||
|
||||
|
||||
var Introspection =
|
||||
module.Introspection = ImageGridFeatures.Feature({
|
||||
title: '',
|
||||
|
||||
tag: 'introspection',
|
||||
|
||||
actions: IntrospectionActions,
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Workspace...
|
||||
//
|
||||
|
||||
423
ui (gen4)/features/ui-partial-ribbons.js
Executable file
423
ui (gen4)/features/ui-partial-ribbons.js
Executable file
@ -0,0 +1,423 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
define(function(require){ var module = {}
|
||||
|
||||
//var DEBUG = DEBUG != null ? DEBUG : true
|
||||
|
||||
var actions = require('lib/actions')
|
||||
var features = require('lib/features')
|
||||
|
||||
var core = require('features/core')
|
||||
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
// NOTE: this is split out to an action so as to enable ui elements to
|
||||
// adapt to ribbon size changes...
|
||||
//
|
||||
// XXX try using .ribbons.resizeRibbon(..) for basic tasks...
|
||||
// XXX try a strategy: load more in the direction of movement by an offset...
|
||||
// XXX updateRibbon(..) is not signature compatible with data.updateRibbon(..)
|
||||
var PartialRibbonsActions = actions.Actions({
|
||||
config: {
|
||||
// number of screen widths to load...
|
||||
'ribbon-size-screens': 7,
|
||||
|
||||
// number of screen widths to edge to trigger reload...
|
||||
'ribbon-resize-threshold': 1.5,
|
||||
|
||||
// timeout before a non-forced ribbon size update happens after
|
||||
// the action...
|
||||
// NOTE: if set to null, the update will be sync...
|
||||
'ribbon-update-timeout': 120,
|
||||
|
||||
// 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
|
||||
// - threshold is set to 0
|
||||
// XXX this is not signature compatible with data.updateRibbon(..)
|
||||
// XXX do not do anything for off-screen ribbons...
|
||||
updateRibbon: ['- Interface/Update partial ribbon size',
|
||||
function(target, w, size, threshold){
|
||||
target = target instanceof jQuery
|
||||
? this.ribbons.getElemGID(target)
|
||||
// NOTE: data.getImage(..) can return null at start or end
|
||||
// of ribbon, thus we need to account for this...
|
||||
: (this.data.getImage(target)
|
||||
|| this.data.getImage(target, 'after'))
|
||||
|
||||
w = w || this.screenwidth
|
||||
|
||||
// get config data and normalize...
|
||||
size = (size
|
||||
|| this.config['ribbon-size-screens']
|
||||
|| 5) * w
|
||||
threshold = threshold == 0 ? threshold
|
||||
: (threshold
|
||||
|| this.config['ribbon-resize-threshold']
|
||||
|| 1) * w
|
||||
|
||||
var timeout = this.config['ribbon-update-timeout']
|
||||
|
||||
// next/prev loaded...
|
||||
var img = this.ribbons.getImage(target)
|
||||
var nl = img.nextAll('.image:not(.clone)').length
|
||||
var pl = img.prevAll('.image:not(.clone)').length
|
||||
|
||||
// next/prev available...
|
||||
// NOTE: we subtract 1 to remove the current and make these
|
||||
// compatible with: nl, pl
|
||||
var na = this.data.getImages(target, size, 'after').length - 1
|
||||
var pa = this.data.getImages(target, size, 'before').length - 1
|
||||
|
||||
// do the update...
|
||||
// no threshold means force load...
|
||||
if(threshold == 0
|
||||
// the target is not loaded...
|
||||
|| img.length == 0
|
||||
// passed hard threshold on the right...
|
||||
|| (nl < w && na > nl)
|
||||
// passed hard threshold on the left...
|
||||
|| (pl < w && pa > pl)){
|
||||
|
||||
this.resizeRibbon(target, size)
|
||||
|
||||
// do a late resize...
|
||||
// loaded more than we need (crop?)...
|
||||
} else if(na + pa < nl + pl
|
||||
// passed threshold on the right...
|
||||
|| (nl < threshold && na > nl)
|
||||
// passed threshold on the left...
|
||||
|| (pl < threshold && pa > pl)
|
||||
// loaded more than we need by threshold...
|
||||
|| nl + pl + 1 > size + threshold){
|
||||
|
||||
return function(){
|
||||
// sync update...
|
||||
if(timeout == null){
|
||||
this.resizeRibbon(target, size)
|
||||
|
||||
// async update...
|
||||
} else {
|
||||
// XXX need to check if we are too close to the edge...
|
||||
var that = this
|
||||
//setTimeout(function(){ that.resizeRibbon(target, size) }, 0)
|
||||
if(this.__update_timeout){
|
||||
clearTimeout(this.__update_timeout)
|
||||
}
|
||||
this.__update_timeout = setTimeout(function(){
|
||||
delete that.__update_timeout
|
||||
that.resizeRibbon(target, size)
|
||||
}, timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
// 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
|
||||
// setup BEFORE RibbonAlignToFirst, otherwise the later will break
|
||||
// on shifting an image to a new ribbon...
|
||||
// To reproduce:
|
||||
// - setupe RibbonAlignToFirst first
|
||||
// - go to top ribbon
|
||||
// - shift image up
|
||||
// XXX The two should be completely independent.... (???)
|
||||
var PartialRibbons =
|
||||
module.PartialRibbons = core.ImageGridFeatures.Feature({
|
||||
title: 'Partial Ribbons',
|
||||
doc: 'Maintains partially loaded ribbons, this enables very lage '
|
||||
+'image sets to be hadled eficiently.',
|
||||
|
||||
// NOTE: partial ribbons needs to be setup first...
|
||||
// ...the reasons why things break otherwise is not too clear.
|
||||
priority: 'high',
|
||||
|
||||
tag: 'ui-partial-ribbons',
|
||||
depends: ['ui'],
|
||||
|
||||
|
||||
actions: PartialRibbonsActions,
|
||||
|
||||
handlers: [
|
||||
['focusImage.pre centerImage.pre',
|
||||
function(target, list){
|
||||
// NOTE: we have to do this as we are called BEFORE the
|
||||
// actual focus change happens...
|
||||
// XXX is there a better way to do this???
|
||||
target = list != null ? target = this.data.getImage(target, list) : target
|
||||
|
||||
this.updateRibbon(target)
|
||||
}],
|
||||
['focusImage.post',
|
||||
function(_, target){
|
||||
this.preCacheJumpTargets(target)
|
||||
}],
|
||||
|
||||
['resizing.pre',
|
||||
function(unit, size){
|
||||
if(unit == 'scale'){
|
||||
this.updateRibbon('current', this.screenwidth / size || 1)
|
||||
|
||||
} else if(unit == 'screenwidth'){
|
||||
this.updateRibbon('current', size || 1)
|
||||
|
||||
} else if(unit == 'screenheight'){
|
||||
size = size || 1
|
||||
|
||||
// convert target height in ribbons to width in images...
|
||||
// NOTE: this does not account for compensation that
|
||||
// .updateRibbon(..) makes for fitting whole image
|
||||
// counts, this is a small enough error so as not
|
||||
// to waste time on...
|
||||
var s = this.ribbons.scale()
|
||||
var h = this.ribbons.getScreenHeightRibbons()
|
||||
var w = this.ribbons.getScreenWidthImages()
|
||||
var nw = w / (h/size)
|
||||
|
||||
this.updateRibbon('current', nw)
|
||||
}
|
||||
|
||||
//this.preCacheJumpTargets()
|
||||
}],
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 : */
|
||||
return module })
|
||||
@ -351,11 +351,15 @@ module.ViewerActions = actions.Actions({
|
||||
// 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 (This will do nothing)',
|
||||
'This will be called by .refresh(..) and intended for use as an '
|
||||
+'trigger for handlers, and not as a callable acation.',
|
||||
function(gid, image){ }],
|
||||
// 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.
|
||||
})],
|
||||
|
||||
|
||||
// General UI stuff...
|
||||
@ -596,6 +600,11 @@ module.ViewerActions = actions.Actions({
|
||||
// - a compliant action must pass the sizing unit, value and
|
||||
// overflow to the wrapping action.
|
||||
//
|
||||
// Supported units:
|
||||
// - scale
|
||||
// - screenwidth
|
||||
// - screenheight
|
||||
//
|
||||
// Example:
|
||||
// actionName: ['...',
|
||||
// function(value){
|
||||
@ -614,16 +623,22 @@ module.ViewerActions = actions.Actions({
|
||||
// This will enable clients to attach to a single in/out point.
|
||||
//
|
||||
// NOTE: not intended for direct use...
|
||||
resizing: ['- Zoom/', 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: .setScale(..)
|
||||
}],
|
||||
//
|
||||
// XXX hide from user action list... (???)
|
||||
resizing: ['- Zoom/Zoom/scale root protocol action (not for direct use)',
|
||||
'This is called by zoom/scale protocol compliant actions and '
|
||||
+'intended for use as an trigger for handlers, and not as '
|
||||
+'a user-callable acation.',
|
||||
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: .setScale(..)
|
||||
})],
|
||||
|
||||
// Root zoom/sclae actions...
|
||||
// Zoom/scale protocol actions...
|
||||
//
|
||||
// XXX need to account for animations...
|
||||
setScale: ['- Zoom/',
|
||||
@ -839,6 +854,7 @@ module.Viewer = core.ImageGridFeatures.Feature({
|
||||
'lifecycle',
|
||||
'base',
|
||||
'workspace',
|
||||
'introspection',
|
||||
],
|
||||
|
||||
actions: ViewerActions,
|
||||
@ -1142,411 +1158,6 @@ module.URLHash = core.ImageGridFeatures.Feature({
|
||||
/*********************************************************************/
|
||||
// Ribbons...
|
||||
|
||||
// NOTE: this is split out to an action so as to enable ui elements to
|
||||
// adapt to ribbon size changes...
|
||||
//
|
||||
// XXX try using .ribbons.resizeRibbon(..) for basic tasks...
|
||||
// XXX try a strategy: load more in the direction of movement by an offset...
|
||||
// XXX updateRibbon(..) is not signature compatible with data.updateRibbon(..)
|
||||
var PartialRibbonsActions = actions.Actions({
|
||||
config: {
|
||||
// number of screen widths to load...
|
||||
'ribbon-size-screens': 7,
|
||||
|
||||
// number of screen widths to edge to trigger reload...
|
||||
'ribbon-resize-threshold': 1.5,
|
||||
|
||||
// timeout before a non-forced ribbon size update happens after
|
||||
// the action...
|
||||
// NOTE: if set to null, the update will be sync...
|
||||
'ribbon-update-timeout': 120,
|
||||
|
||||
// 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
|
||||
// - threshold is set to 0
|
||||
// XXX this is not signature compatible with data.updateRibbon(..)
|
||||
// XXX do not do anything for off-screen ribbons...
|
||||
updateRibbon: ['- Interface/Update partial ribbon size',
|
||||
function(target, w, size, threshold){
|
||||
target = target instanceof jQuery
|
||||
? this.ribbons.getElemGID(target)
|
||||
// NOTE: data.getImage(..) can return null at start or end
|
||||
// of ribbon, thus we need to account for this...
|
||||
: (this.data.getImage(target)
|
||||
|| this.data.getImage(target, 'after'))
|
||||
|
||||
w = w || this.screenwidth
|
||||
|
||||
// get config data and normalize...
|
||||
size = (size
|
||||
|| this.config['ribbon-size-screens']
|
||||
|| 5) * w
|
||||
threshold = threshold == 0 ? threshold
|
||||
: (threshold
|
||||
|| this.config['ribbon-resize-threshold']
|
||||
|| 1) * w
|
||||
|
||||
var timeout = this.config['ribbon-update-timeout']
|
||||
|
||||
// next/prev loaded...
|
||||
var img = this.ribbons.getImage(target)
|
||||
var nl = img.nextAll('.image:not(.clone)').length
|
||||
var pl = img.prevAll('.image:not(.clone)').length
|
||||
|
||||
// next/prev available...
|
||||
// NOTE: we subtract 1 to remove the current and make these
|
||||
// compatible with: nl, pl
|
||||
var na = this.data.getImages(target, size, 'after').length - 1
|
||||
var pa = this.data.getImages(target, size, 'before').length - 1
|
||||
|
||||
// do the update...
|
||||
// no threshold means force load...
|
||||
if(threshold == 0
|
||||
// the target is not loaded...
|
||||
|| img.length == 0
|
||||
// passed hard threshold on the right...
|
||||
|| (nl < w && na > nl)
|
||||
// passed hard threshold on the left...
|
||||
|| (pl < w && pa > pl)){
|
||||
|
||||
this.resizeRibbon(target, size)
|
||||
|
||||
// do a late resize...
|
||||
// loaded more than we need (crop?)...
|
||||
} else if(na + pa < nl + pl
|
||||
// passed threshold on the right...
|
||||
|| (nl < threshold && na > nl)
|
||||
// passed threshold on the left...
|
||||
|| (pl < threshold && pa > pl)
|
||||
// loaded more than we need by threshold...
|
||||
|| nl + pl + 1 > size + threshold){
|
||||
|
||||
return function(){
|
||||
// sync update...
|
||||
if(timeout == null){
|
||||
this.resizeRibbon(target, size)
|
||||
|
||||
// async update...
|
||||
} else {
|
||||
// XXX need to check if we are too close to the edge...
|
||||
var that = this
|
||||
//setTimeout(function(){ that.resizeRibbon(target, size) }, 0)
|
||||
if(this.__update_timeout){
|
||||
clearTimeout(this.__update_timeout)
|
||||
}
|
||||
this.__update_timeout = setTimeout(function(){
|
||||
delete that.__update_timeout
|
||||
that.resizeRibbon(target, size)
|
||||
}, timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
// 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
|
||||
// setup BEFORE RibbonAlignToFirst, otherwise the later will break
|
||||
// on shifting an image to a new ribbon...
|
||||
// To reproduce:
|
||||
// - setupe RibbonAlignToFirst first
|
||||
// - go to top ribbon
|
||||
// - shift image up
|
||||
// XXX The two should be completely independent.... (???)
|
||||
var PartialRibbons =
|
||||
module.PartialRibbons = core.ImageGridFeatures.Feature({
|
||||
title: 'Partial Ribbons',
|
||||
doc: 'Maintains partially loaded ribbons, this enables very lage '
|
||||
+'image sets to be hadled eficiently.',
|
||||
|
||||
// NOTE: partial ribbons needs to be setup first...
|
||||
// ...the reasons why things break otherwise is not too clear.
|
||||
priority: 'high',
|
||||
|
||||
tag: 'ui-partial-ribbons',
|
||||
depends: ['ui'],
|
||||
|
||||
|
||||
actions: PartialRibbonsActions,
|
||||
|
||||
handlers: [
|
||||
['focusImage.pre centerImage.pre',
|
||||
function(target, list){
|
||||
// NOTE: we have to do this as we are called BEFORE the
|
||||
// actual focus change happens...
|
||||
// XXX is there a better way to do this???
|
||||
target = list != null ? target = this.data.getImage(target, list) : target
|
||||
|
||||
this.updateRibbon(target)
|
||||
}],
|
||||
['focusImage.post',
|
||||
function(_, target){
|
||||
this.preCacheJumpTargets(target)
|
||||
}],
|
||||
|
||||
['resizing.pre',
|
||||
function(unit, size){
|
||||
if(unit == 'scale'){
|
||||
this.updateRibbon('current', this.screenwidth / size || 1)
|
||||
|
||||
} else if(unit == 'screenwidth'){
|
||||
this.updateRibbon('current', size || 1)
|
||||
|
||||
} else if(unit == 'screenheight'){
|
||||
size = size || 1
|
||||
|
||||
// convert target height in ribbons to width in images...
|
||||
// NOTE: this does not account for compensation that
|
||||
// .updateRibbon(..) makes for fitting whole image
|
||||
// counts, this is a small enough error so as not
|
||||
// to waste time on...
|
||||
var s = this.ribbons.scale()
|
||||
var h = this.ribbons.getScreenHeightRibbons()
|
||||
var w = this.ribbons.getScreenWidthImages()
|
||||
var nw = w / (h/size)
|
||||
|
||||
this.updateRibbon('current', nw)
|
||||
}
|
||||
|
||||
//this.preCacheJumpTargets()
|
||||
}],
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// These features glue traverse and ribbon alignment...
|
||||
|
||||
|
||||
// XXX manual align needs more work...
|
||||
var AutoAlignRibbons =
|
||||
module.AutoAlignRibbons = core.ImageGridFeatures.Feature({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user