reworked system caches...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-11-27 23:11:10 +03:00
parent 026ff53ab2
commit d77c01e63e
7 changed files with 304 additions and 203 deletions

View File

@ -1806,9 +1806,19 @@ module.Crop = core.ImageGridFeatures.Feature({
tag: 'crop',
depends: [
'base',
//'cache',
],
actions: CropActions,
handlers: [
[[
'crop',
'uncrop',
'removeFromCrop',
],
'clearCache: "view(-.*)?" "*" -- Clear view cache'],
],
})

View File

@ -1779,6 +1779,13 @@ module.Collection = core.ImageGridFeatures.Feature({
// XXX
})
}],
// invalidate caches...
[[
'loadCollection',
'uncollect',
],
'clearCache: "view(-.*)?" "*" -- Clear view cache'],
],
})

View File

@ -1027,9 +1027,21 @@ module.Serialization = ImageGridFeatures.Feature({
// Cache...
// XXX should this be in actions.js???
// XXX should we invalidate the cache automatically???
// XXX the cache can also be saved to localStorage and loaded until either
// the version changes or the feature list...
// XXX revise: cache group naming...
// currently the used groups are:
// Session groups -- cleared on .clear() ('cache')
// session-*
// view-*
// View groups -- cleared by crop/collection ('crop', 'collections')
// view-*
// Changes groups -- cleared when specific changes are made ('changes')
// *-data
// *-images
// ...
// This approach seems not flexible enough...
// Ideas:
// - use keywords in group names??
// XXX should we consider persistent caches -- localStorage???
var CacheActions = actions.Actions({
config: {
// Enable/disable caching...
@ -1062,11 +1074,30 @@ var CacheActions = actions.Actions({
// action is available...
'pre-cache-progress': 3000,
// Groups to be cleared at the longest on session change...
//
// These include by default:
// 'session' - will live through the whole session.
// 'view' - cleared when view changes
//
'cache-session-groups': [
'session',
'view',
],
// XXX handler cache..
},
// Cache utility method...
//
// .cache(title, handler)
// -> value
//
// .cache(group, title, handler)
// -> value
//
//
// Example use:
// someAction: [
// function(){
@ -1086,18 +1117,106 @@ var CacheActions = actions.Actions({
// return data
// }) }],
//
cache: function(title, lister){
// XXX what should the default group be???
// XXX should this be an action???
__cache: null,
cache: function(title, handler){
var group = 'global'
// caching disabled...
if(!(this.config || {}).cache){
return lister.call(this)
}
return handler.call(this) }
arguments.length > 2
&& ([group, title, handler] = arguments)
var cache = this.__cache = this.__cache || {}
cache = cache[group] = cache[group] || {}
return (cache[title] =
title in cache ?
// pass the cached data for cloning/update to the lister...
lister.call(this, cache[title])
: lister.call(this))
},
// pass the cached data for cloning/update to the handler...
handler.call(this, cache[title])
: handler.call(this)) },
clearCache: ['System/Clear cache',
doc`
Clear cache fully...
.clearCache()
Clear title (global group)...
.clearCache(title)
Clear title from group...
.clearCache(group, title)
Clear out the full group...
.clearCache(group, '*')
NOTE: a group can be a string, list or a regexp object.
`,
function(title){
var that = this
// full clear...
if(arguments.length == 0
|| (arguments[0] == '*'
&& arguments[1] == '*')){
delete this.__cache
// partial clear...
} else {
var group = 'global'
// both group and title given...
arguments.length > 1
&& ([group, title] = arguments)
// regexp...
// NOTE: these are only supported in groups...
if(group != '*' && group.includes('*')){
group = new RegExp('^'+ group +'$', 'i')
group = Object.keys(this.__cache || {})
.filter(function(g){
return group.test(g) }) }
// clear title from each group...
if(group == '*' || group instanceof Array || group instanceof RegExp){
;(group instanceof Array ?
group
: group instanceof RegExp ?
Object.keys(this.__cache || {})
.filter(function(g){
return group.test(g) })
: Object.keys(this.__cache || {}))
.forEach(function(group){
that.clearCache(group, title) })
// clear multiple titles...
} else if(title instanceof Array){
title.forEach(function(title){
delete ((that.__cache || {})[group] || {})[title] })
// clear group...
} else if(title == '*'){
delete (this.__cache || {})[group]
// clear title from group...
} else {
delete ((this.__cache || {})[group] || {})[title] } } }],
// special caches...
//
sessionCache: ['- System/',
doc`Add to session cache...
.sessionCache(title, handler)
-> value
This is a shorthand to:
.cache('session', title, handler)
-> value
NOTE: also see .cache(..)
`,
'cache: "session" ...'],
// XXX doc: what are we precaching???
preCache: ['System/Run pre-cache',
doc`Run pre-cache...
@ -1120,8 +1239,7 @@ var CacheActions = actions.Actions({
var done = 0
var attrs = []
for(var k in this){
attrs.push(k)
}
attrs.push(k) }
var l = attrs.length
var started = Date.now()
@ -1131,45 +1249,25 @@ var CacheActions = actions.Actions({
var a = Date.now()
var b = a
if(attrs.length == 0){
return
}
return }
while(b - a < c){
this[attrs.pop()]
b = Date.now()
done += 1
this.showProgress
&& (show === true || (show && b - started > show))
&& this.showProgress('Caching', done, l)
}
&& this.showProgress('Caching', done, l) }
t === true ?
tick()
: setTimeout(tick, t)
}.bind(this)
: setTimeout(tick, t) }.bind(this)
tick()
}
}],
clearCache: ['System/Clear cache',
function(title){
if(title){
delete (this.__cache|| {})[title]
} else {
delete this.__cache
}
}],
// XXX do we need this...
tick() } }],
reCache: ['System/Re-cache',
function(t){
this
.clearCache()
.preCache(t) }],
toggleHandlerCache: ['System/Action handler cache',
makeConfigToggler('action-handler-cache',
['off', 'on']/*,
@ -1192,15 +1290,16 @@ module.Cache = ImageGridFeatures.Feature({
actions: CacheActions,
handlers: [
// System...
['start.pre',
function(){
this.clearCache()
var t = this.config['pre-cache']
t === true ?
this.preCache('now')
: t >= 0 ?
this.preCache()
: false
}],
: false }],
['start',
function(){
// XXX this breaks loading...
@ -1208,7 +1307,6 @@ module.Cache = ImageGridFeatures.Feature({
// there seems to be no problems...
//this.toggleHandlerCache(this.config['action-handler-cache'] || 'on')
}],
/*/ XXX clear cache when feature/action topology changes...
[[
'inlineMixin',
@ -1222,6 +1320,16 @@ module.Cache = ImageGridFeatures.Feature({
this.clearCache()
}],
//*/
// clear session cache...
['clear',
//'clearCache: "(session|view)(-.*)?" "*" -- Clear session cache'],
function(){
this.clearCache(`(${
(this.config['cache-session-groups']
|| ['session', 'view'])
.join('|') })(-.*)?`) }],
],
})
@ -2148,6 +2256,16 @@ module.Changes = ImageGridFeatures.Feature({
this.changes = JSON.parse(JSON.stringify(data.changes))
}
}],
// clear caches relating to stuff we just changed...
['markChanged',
function(_, section){
section = (section instanceof Array ?
section
: [section])
.map(function(section){
return '.*-'+section })
this.clearCache(section, '*') }],
],
})

View File

@ -25,8 +25,7 @@ var base = require('features/base')
var makeStateIndicator = function(type){
return $('<div>')
.addClass('state-indicator-container ' + type || '')
}
.addClass('state-indicator-container ' + type || '') }
// XXX do we need this???
var makeStateIndicatorItem = function(container, type, text){
@ -35,8 +34,7 @@ var makeStateIndicatorItem = function(container, type, text){
.attr('text', text)
this.dom.find('.state-indicator-container.'+container)
.append(item)
return item
}
return item }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -102,6 +100,13 @@ var StatusBarActions = actions.Actions({
},
},
get __statusbar_cache(){
return this.cache('view-data', 'statusbar',
function(data){
return data || {}}) },
__statusbar_elements__: {
/* item template...
item: function(item){
@ -133,8 +138,7 @@ var StatusBarActions = actions.Actions({
index: function(item, gid, img){
// cleanup...
if(item == null){
return
}
return }
var that = this
gid = gid || this.current
@ -162,8 +166,7 @@ var StatusBarActions = actions.Actions({
// toggle index state...
.click(function(){
that.toggleStatusBarIndexMode()
that.updateStatusBar()
})
that.updateStatusBar() })
// editable...
: $('<span>')
.addClass('position editable')
@ -182,40 +185,35 @@ var StatusBarActions = actions.Actions({
//event.stopPropagation()
(that.config['status-bar-index'] || {})['live-update-on-edit']
&& go(parseInt($(this).text()))
})
&& go(parseInt($(this).text())) })
.focus(function(){
$(this).selectText()
})
$(this).selectText() })
.blur(function(){
that.updateStatusBar()
}))
that.updateStatusBar() }))
.append($('<span>')
.addClass('length')
.attr('info', 'Image count (click to toggle ribbon/global)')
// toggle index state...
.click(function(){
that.toggleStatusBarIndexMode()
that.updateStatusBar()
}))
that.updateStatusBar() }))
} else {
var type = item.attr('type')
}
var type = item.attr('type') }
// NOTE: using .toggleStatusBarIndexMode(..) here will fall
// into an infinite recursion...
var cls = (that.config['status-bar-index'] || {})['mode'] || 'normal'
// XXX get the cached length...
// XXX make this part of the status bar cache...
var cache = this.__statusbar_index_length_cache || []
cache = cache[0] == cls ?
cache[1]
// get the cached length...
var cache = this.__statusbar_cache.index_total
cache = cache ?
(cache[0] == cls
&& cache[1])
: null
// empty view...
if(this.data){
if(!this.data){
var i = -1
var l = 0
@ -234,11 +232,16 @@ var StatusBarActions = actions.Actions({
// ribbon index...
} else {
var i = this.data.getImageOrder('ribbon', gid)
var l = cache =
cache || this.data.getImages(gid).len }
var r = this.current_ribbon
var l = cache
&& cache instanceof Array
&& cache[0] == r
&& cache[1]
l = l || this.data.getImages(gid).len
cache = [r, l] }
// XXX save cache...
this.__statusbar_index_length_cache = [cls, cache]
// save cache...
this.__statusbar_cache.index_total = [cls, cache]
// update...
item
@ -250,13 +253,11 @@ var StatusBarActions = actions.Actions({
.find('.length')
.text(l > 0 ? ('/' + l) : '')
return item
},
return item },
ribbon: function(item, gid, img){
// cleanup...
if(item == null){
return
}
return }
var that = this
@ -294,8 +295,7 @@ var StatusBarActions = actions.Actions({
}))
.append($('<span>')
.addClass('ribbon-count')
.attr('info', 'Ribbon count'))
}
.attr('info', 'Ribbon count')) }
item
.find('.ribbon-number')
@ -312,22 +312,18 @@ var StatusBarActions = actions.Actions({
item[0].setAttribute('base', '')
} else {
item[0].removeAttribute('base')
}
item[0].removeAttribute('base') }
return item
},
return item },
changes: function(item, gid, img){
// cleanup...
if(item == null){
return
}
return }
if(typeof(item) == typeof('str')){
item = $('<span>')
.addClass('changes')
.attr('info', 'Unsaved changes')
}
.attr('info', 'Unsaved changes') }
//item.html(this.changes !== false ?
// this.config['status-bar-changes-text'] || '*'
@ -337,14 +333,12 @@ var StatusBarActions = actions.Actions({
this.config['status-bar-changes-text'] || '*'
: '')
return item
},
return item },
// XXX handle path correctly...
gid: function(item, gid, img){
// cleanup...
if(item == null){
return
}
return }
var that = this
gid = gid || this.current
@ -363,12 +357,10 @@ var StatusBarActions = actions.Actions({
// select the text...
// XXX should this also copy???
.click(function(){
$(this).selectText()
}))
$(this).selectText() }))
} else {
var type = item.attr('type')
}
var type = item.attr('type') }
// update...
var txt = ''
@ -386,14 +378,14 @@ var StatusBarActions = actions.Actions({
text = (img && img.path && ((img.base_path || '') +'/'+ img.path) || '---')
// remove /./
.replace(/[\\\/]\.[\\\/]/, '/')
txt = img && ((img.name || '') + (img.ext || '')) || text.split(/[\\\/]/).pop()
}
txt = img
&& ((img.name || '') + (img.ext || ''))
|| text.split(/[\\\/]/).pop() }
item.find('.shown').text(txt)
item.find('.hidden').text(text)
return item
},
return item },
path: 'gid',
'edit-mode': function(item){
// cleanup...
@ -401,21 +393,20 @@ var StatusBarActions = actions.Actions({
this.__edit_mode_indicator_update
&& this.off('keyPress', this.__edit_mode_indicator_update)
delete this.__edit_mode_indicator_update
return
}
return }
var update = this.__edit_mode_indicator_update = this.__edit_mode_indicator_update
|| (function(){
var caps = this.keyboard.capslock
caps = typeof(event) != 'undefined' && event.getModifierState ?
event.getModifierState('CapsLock')
: caps
item
.attr('info', 'Edit mode '
+ (caps ? 'on' : 'off')
+ ' (Click to update / Press CapsLock to toggle)')
[caps ? 'addClass' : 'removeClass']('on')
}).bind(this)
var update = this.__edit_mode_indicator_update =
this.__edit_mode_indicator_update
|| (function(){
var caps = this.keyboard.capslock
caps = typeof(event) != 'undefined' && event.getModifierState ?
event.getModifierState('CapsLock')
: caps
item
.attr('info', 'Edit mode '
+ (caps ? 'on' : 'off')
+ ' (Click to update / Press CapsLock to toggle)')
[caps ? 'addClass' : 'removeClass']('on') }).bind(this)
// cleanup interval handling...
this.__edit_mode_indicator_update_interval
@ -426,8 +417,7 @@ var StatusBarActions = actions.Actions({
if(item == null){
this.off('keyPress', update)
this.dom.off('focus', update)
return
}
return }
// setup...
if(typeof(item) == typeof('str')){
@ -441,8 +431,7 @@ var StatusBarActions = actions.Actions({
.click(update)
this.on('keyPress', update)
this.dom.focus(update)
}
this.dom.focus(update) }
// update timer...
// NOTE: this is needed so as to reflect changes to settings...
@ -454,15 +443,13 @@ var StatusBarActions = actions.Actions({
// update state...
update()
return item
},
return item },
// XXX show menu in the appropriate corner...
// XXX remove the type+ed class...
mark: function(item, gid, img){
// cleanup...
if(item == null){
return
}
return }
gid = gid || this.current
var that = this
@ -484,12 +471,10 @@ var StatusBarActions = actions.Actions({
evt.preventDefault()
evt.stopPropagation()
that.browseActions('/'+ type.capitalize() +'/')
})
that.browseActions('/'+ type.capitalize() +'/') })
} else {
var type = item.attr('type')
}
var type = item.attr('type') }
// NOTE: we are not using .toggleMark('?') and friends
// here to avoid recursion as we might be handling
@ -503,8 +488,7 @@ var StatusBarActions = actions.Actions({
'removeClass'
: 'addClass']('on')
return item
},
return item },
bookmark: 'mark',
},
@ -520,8 +504,7 @@ var StatusBarActions = actions.Actions({
function(){
// no viewer yet...
if(!this.ribbons || !this.dom){
return $()
}
return $() }
var bar = this.dom.find('.state-indicator-container.global-info')
if(bar.length == 0){
@ -543,17 +526,14 @@ var StatusBarActions = actions.Actions({
.on('mouseout', function(){
bar.find('.info').empty()
})
.appendTo(this.dom)
}
return bar
},
.appendTo(this.dom) }
return bar },
function(){ return Object.keys(this.config['status-bars']).concat(['none']) },
// XXX check if we will be getting gid reliably...
function(state, bar, gid){
// do not do anything unless the status bar exists...
if(bar.length == 0){
return
}
return }
var that = this
this.config['status-bar'] = state
@ -564,8 +544,7 @@ var StatusBarActions = actions.Actions({
var handler = elems[key] || base_elems[key]
if(handler == null){
return
}
return }
// handle aliases...
var seen = []
@ -576,15 +555,13 @@ var StatusBarActions = actions.Actions({
if(seen.indexOf(handler) >= 0){
console.error('state indicator alias loop detected at:', key)
handler = null
break
}
}
break } }
return handler
}
return handler }
// clear...
if(state == 'none' || !bar.hasClass(state)){
this.clearStatusBarCache()
// notify items that they are removed...
bar.children()
.each(function(i, item){
@ -592,25 +569,21 @@ var StatusBarActions = actions.Actions({
var type = item.attr('type')
if(type == null){
return
}
return }
var handler = _getHandler(type)
if(handler != null){
handler.call(that, null)
}
handler.call(that, null) }
})
bar.empty()
}
bar.empty() }
if(state == 'none'){
!('none' in this.config['status-bars'])
// XXX this feels like a hack...
&& setTimeout(function(){ this.toggleStatusBar(0) }.bind(this), 0)
//return Object.keys(this.config['status-bars'])[0]
return
}
return }
// build/update...
gid = gid || this.current
@ -642,11 +615,9 @@ var StatusBarActions = actions.Actions({
item = (handler ?
handler.call(that, item, gid, img)
: $('<span>'))
.attr('type', item)
}
.attr('type', item) }
bar.append(item)
})
bar.append(item) })
// update...
} else {
@ -656,17 +627,12 @@ var StatusBarActions = actions.Actions({
var type = item.attr('type')
if(type == null){
return
}
return }
var handler = _getHandler(type)
if(handler != null){
handler.call(that, item, gid, img)
}
})
}
},
handler.call(that, item, gid, img) } }) } },
null)],
updateStatusBar: ['- Interface/Update satus bar',
'toggleStatusBar: "!"'],
@ -675,8 +641,10 @@ var StatusBarActions = actions.Actions({
function(){
var mode = this.toggleStatusBar('?')
this.toggleStatusBar('none')
this.toggleStatusBar(mode)
}],
this.toggleStatusBar(mode) }],
clearStatusBarCache: ['- Interface/Clear status bar cache',
'clearCache: "*" "statusbar"'],
// XXX should this blink the on state only???
statusItemBlink: ['- Interface/',
@ -685,8 +653,7 @@ var StatusBarActions = actions.Actions({
NOTE: type is the same as in .__statusbar_elements__`,
function(type){
if(type == null){
return
}
return }
var gid = this.current
var item = this.dom.find(`.state-indicator-container.global-info [type=${type}]`)
@ -695,42 +662,36 @@ var StatusBarActions = actions.Actions({
item
.removeClass('blink')
.addClass('blink')
.on('animationend', function(){ item.removeClass('blink') })
}],
.on('animationend', function(){ item.removeClass('blink') }) }],
// XXX should these be here???
// XXX should this show a dialog???
editStatusBarIndex: ['- Interface/Edit image focus position in statusbar',
function(){
if((this.config['status-bar-index'] || {} )['editable']){
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
this.toggleStatusBar('?') == 'none'
&& this.toggleStatusBar()
// XXX do this better...
this.dom.find('.global-info .index .position').focus().click()
}
}],
this.dom.find('.global-info .index .position').focus().click() } }],
editStatusBarRibbon: ['- Interface/Edit ribbon focus position in statusbar',
function(){
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
this.toggleStatusBar('?') == 'none'
&& this.toggleStatusBar()
// XXX do this better...
this.dom.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.dom.find('.global-info .index') },
['normal', 'loaded', 'global'],
function(state){
this.toggleStatusBar('?') == 'none' && this.toggleStatusBar()
this.toggleStatusBar('?') == 'none'
&& this.toggleStatusBar()
// prepare for saving the config...
this.config['status-bar-index'] =
JSON.parse(JSON.stringify(this.config['status-bar-index']))
this.config['status-bar-index']['mode'] = state
this.updateStatusBar()
})],
this.updateStatusBar() })],
// XXX revise...
showStatusBarInfo: ['- Interface/',
@ -807,12 +768,10 @@ module.StatusBar = core.ImageGridFeatures.Feature({
handlers: [
['start',
function(){
this.toggleStatusBar(this.config['status-bar'])
}],
this.toggleStatusBar(this.config['status-bar']) }],
['focusImage clear markChanged refresh',
function(){
this.updateStatusBar()
}],
this.updateStatusBar() }],
[[
'tag',
'untag',
@ -823,14 +782,12 @@ module.StatusBar = core.ImageGridFeatures.Feature({
&& (gids.indexOf('current') >= 0
|| gids.indexOf(this.current) >= 0)
|| this.data.getImage(gids) == this.current){
this.updateStatusBar()
}
}],
this.updateStatusBar() } }],
['ribbonPanning.post',
function(_, gid){
gid == this.data.getRibbon() && this.updateStatusBar()
}],
gid == this.data.getRibbon()
&& this.updateStatusBar() }],
// blink status mark indicators on toggle...
['toggleMark',
@ -853,13 +810,11 @@ module.StatusBar = core.ImageGridFeatures.Feature({
],
function(workspace){
if(!workspace || workspace in this.workspaces){
return
}
return }
this.config['status-bar'] =
(this.config['status-bar-workspace-defaults'][workspace]
|| this.config['status-bar'])
}],
|| this.config['status-bar']) }],
['loadWorkspace',
core.makeWorkspaceConfigLoader(
function(){
@ -871,9 +826,7 @@ module.StatusBar = core.ImageGridFeatures.Feature({
} else {
'status-bar' in workspace ?
this.toggleStatusBar(workspace['status-bar'])
: this.toggleStatusBar(this.config['status-bar'])
}
})],
: this.toggleStatusBar(this.config['status-bar']) } })],
],
})

View File

@ -13,6 +13,7 @@
var sha1 = require('ext-lib/sha1')
var types = require('lib/types')
var object = require('lib/object')
var tags = require('imagegrid/tags')
@ -223,6 +224,7 @@ var DataPrototype = {
ribbon_order: null,
ribbons: null,
//__current: null,
get current(){
return this.__current = this.__current
|| this.getImages(this.ribbon_order[0])[0]
@ -230,6 +232,7 @@ var DataPrototype = {
set current(value){
this.focusImage(value) },
//__base: null,
get base(){
return this.__base || this.ribbon_order[0] },
set base(value){
@ -237,13 +240,18 @@ var DataPrototype = {
this.getRibbon(value)
: value },
// NOTE: experiments with wrapping data in Proxy yielded a
// significant slowdown on edits...
//__order: null,
get order(){
return this.__order },
set order(value){
delete this.__order_index
this.__order = value },
//__order_index: null,
get order_index(){
return this.__order_index = this.__order_index || this.order.toKeys() },
return this.__order_index =
this.__order_index || this.order.toKeys() },
@ -1027,16 +1035,16 @@ var DataPrototype = {
// order.
getImageOrder: function(context, target, mode, list){
if(context == 'loaded' || context == 'global'){
return this.getImages('loaded').indexOf(this.getImage(target, mode, list))
return this.getImages('loaded').lastIndexOf(this.getImage(target, mode, list))
} else if(context == 'ribbon'){
var gid = this.getImage(target, mode, list)
return this.getImages(gid).indexOf(gid)
return this.getImages(gid).lastIndexOf(gid)
} else if(context == 'all'){
return this.order.indexOf(this.getImage(target, mode, list)) }
return this.order.lastIndexOf(this.getImage(target, mode, list)) }
return this.order.indexOf(this.getImage(context, target, mode)) },
return this.order.lastIndexOf(this.getImage(context, target, mode)) },
// Get a list of image gids...
//
@ -1183,7 +1191,6 @@ var DataPrototype = {
target = this.getImage(target)
|| this.getImage(target, 'after')
var i = list.indexOf(target)
// prepare to slice the list...
if(mode == 'around' || mode == 'total'){
@ -1204,6 +1211,10 @@ var DataPrototype = {
var res = [target]
// XXX can we avoid .indexOf(..) here???
//var i = list.indexOf(target)
//var i = list.index(target)
var i = list.lastIndexOf(target)
// pre...
for(var n = i-1; n >= 0 && pre > 0; n--){
// NOTE: list may be sparse so we skip the items that are not
@ -1227,7 +1238,9 @@ var DataPrototype = {
// in the post section...
if(mode == 'total' && post > 0){
var pad = count - res.length
var i = list.indexOf(res[0])
//var i = list.indexOf(res[0])
//var i = list.index(res[0])
var i = list.lastIndexOf(res[0])
res.reverse()
for(var n = i-1; n >= 0 && pad > 0; n--){

View File

@ -1,6 +1,6 @@
{
"name": "ImageGrid.Viewer.g4",
"version": "4.0.0a",
"version": "4.0.0-a",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1110,9 +1110,9 @@
"integrity": "sha512-9kZM80Js9/eTwXN9VXwLDC1wDJ7gIAdYU9GIzb5KJmNcLAMaW+zhgFrwFFMrcSfggUuadgnqSrS41E4XLe8JZw=="
},
"ig-types": {
"version": "5.0.18",
"resolved": "https://registry.npmjs.org/ig-types/-/ig-types-5.0.18.tgz",
"integrity": "sha512-8c16slG/J0qC9oYW1Mx6oSriEYKEY/HLs4SvgWDLqac/qx/nMaa6HlEUA/fi+whGxvTk03t4s/IKca+MKWV9oQ==",
"version": "5.0.20",
"resolved": "https://registry.npmjs.org/ig-types/-/ig-types-5.0.20.tgz",
"integrity": "sha512-d2IEwY3ZDSWxn2PY0e7XxMzuOPWzeK/py0sBgqfoCNbKlc4IHEogFwoQIv0C4EZdcXL0cJOsCo74NwbetoSWiA==",
"requires": {
"ig-object": "^5.4.12",
"object-run": "^1.0.1"

View File

@ -32,7 +32,7 @@
"ig-argv": "^2.15.0",
"ig-features": "^3.4.2",
"ig-object": "^5.4.12",
"ig-types": "^5.0.18",
"ig-types": "^5.0.20",
"moment": "^2.29.1",
"object-run": "^1.0.1",
"requirejs": "^2.3.6",