mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-28 18:00:09 +00:00
refactoring, hooks and some fixes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
9f11fa834b
commit
7f4fd26509
@ -79,14 +79,15 @@ var normalizeSplit = function(args){
|
||||
/*********************************************************************/
|
||||
|
||||
// Helpers...
|
||||
var makeSplitter = function(separator){
|
||||
var makeSplitter = function(separator, unique){
|
||||
return function(...tags){
|
||||
var SP = this[separator]
|
||||
return normalizeSplit(tags)
|
||||
.map(function(tag){
|
||||
return tag.split(SP) })
|
||||
.flat()
|
||||
.unique() } }
|
||||
.run(function(){
|
||||
return unique ? this.unique() : this }) } }
|
||||
var makeJoiner = function(separator){
|
||||
return function(...items){
|
||||
return normalizeSplit(items).join(this[separator]) } }
|
||||
@ -116,7 +117,7 @@ var BaseTagsClassPrototype = {
|
||||
// -> parts
|
||||
//
|
||||
// NOTE: these will combine the parts of all the tags...
|
||||
splitSet: makeSplitter('SET_SEPARATOR_PATTERN'),
|
||||
splitSet: makeSplitter('SET_SEPARATOR_PATTERN', true),
|
||||
splitPath: makeSplitter('PATH_SEPARATOR_PATTERN'),
|
||||
splitTag: makeSplitter('COMBINED_SEPARATOR_PATTERN'),
|
||||
joinSet: makeJoiner('SET_SEPARATOR'),
|
||||
@ -442,7 +443,6 @@ var BaseTagsPrototype = {
|
||||
//
|
||||
// NOTE: this is not symmetric e.g. a will match a:b but not vice-versa.
|
||||
//
|
||||
// XXX BUG: .directMatch('a/*', 'a/a/a') -> false (should be true)
|
||||
directMatch: function(a, b, cmp, no_definitions){
|
||||
var that = this
|
||||
// parse args...
|
||||
@ -548,7 +548,6 @@ var BaseTagsPrototype = {
|
||||
.length == 0)
|
||||
}
|
||||
},
|
||||
|
||||
// Match tags...
|
||||
//
|
||||
// This is the same as .directMatch(..) but also uses persistent
|
||||
@ -627,7 +626,6 @@ var BaseTagsPrototype = {
|
||||
false
|
||||
: search(a, b)
|
||||
},
|
||||
|
||||
// Search tags...
|
||||
//
|
||||
// Search the tags...
|
||||
@ -1005,64 +1003,6 @@ var BaseTagsPrototype = {
|
||||
(that.tag(tag, v), true)
|
||||
: null) }) },
|
||||
|
||||
// Toggle a tag to persistent/non-persistent...
|
||||
//
|
||||
// A persistent tag is not removed when untagging a value.
|
||||
//
|
||||
// .togglePersistent(tag)
|
||||
// .togglePersistent(tag, tag, ...)
|
||||
// .togglePersistent([tag, tag, ...])
|
||||
// -> states
|
||||
//
|
||||
// .togglePersistent(tag, action)
|
||||
// .togglePersistent(tag, tag, ..., action)
|
||||
// .togglePersistent([tag, tag, ...], action)
|
||||
// -> states
|
||||
//
|
||||
//
|
||||
// action can be:
|
||||
// 'on' - toggle all tags on
|
||||
// 'off' - toggle all off
|
||||
// 'toggle' - toggle all depending on initial state
|
||||
// '?' - return list of states
|
||||
//
|
||||
//
|
||||
// XXX one way to play with this is to add a special tag to set/path
|
||||
// to make it persistent...
|
||||
// Example:
|
||||
// .tag('abc', ...) -> 'abc' is a normal tag...
|
||||
//
|
||||
// .tag('persistent:abc', ...) -> 'abc' is persistent...
|
||||
// .tag('persistent/abc', ...) -> 'abc' is persistent...
|
||||
//
|
||||
// We would need "virtual" tags for this, i.e. tags that are
|
||||
// not actually added to the index but are used for system
|
||||
// stuff...
|
||||
togglePersistent: function(...tags){
|
||||
action = ['on', 'off', 'toggle', '?'].includes(tags[tags.length-1]) ?
|
||||
tags.pop()
|
||||
: 'toggle'
|
||||
tags = normalizeSplit(tags)
|
||||
|
||||
var persistent =
|
||||
this.persistent =
|
||||
this.persistent || new Set()
|
||||
|
||||
return this.normalize(tags)
|
||||
.map(function(tag){
|
||||
return action == 'on' ?
|
||||
(persistent.add(tag), 'on')
|
||||
: action == 'off' ?
|
||||
(persistent.delete(tag), 'off')
|
||||
: action == 'toggle' ?
|
||||
(persistent.has(tag) ?
|
||||
(persistent.delete(tag), 'off')
|
||||
: (persistent.add(tag), 'on'))
|
||||
: (persistent.has(tag) ?
|
||||
'on'
|
||||
: 'off') })
|
||||
},
|
||||
|
||||
// Rename a tag...
|
||||
//
|
||||
// Rename tag...
|
||||
@ -1152,55 +1092,15 @@ var BaseTagsPrototype = {
|
||||
|
||||
return this
|
||||
},
|
||||
|
||||
// NOTE: this is a short hand to .rename(tag, '', ..) for extra
|
||||
// docs see that...
|
||||
removeTag: function(tag, ...tags){
|
||||
return this.rename(tag, '', ...tags) },
|
||||
|
||||
// Remove the given values...
|
||||
//
|
||||
// .remove(value, ..)
|
||||
// .remove([value, ..])
|
||||
// -> this
|
||||
//
|
||||
remove: function(...values){
|
||||
values = normalizeSplit(values)
|
||||
var res = this.clone()
|
||||
|
||||
Object.entries(res.__index || {})
|
||||
.forEach(function(e){
|
||||
res.__index[e[0]] = e[1].subtract(values) })
|
||||
|
||||
return res
|
||||
},
|
||||
|
||||
// Keep only the given values...
|
||||
//
|
||||
// .keep(value, ..)
|
||||
// .keep([value, ..])
|
||||
// -> this
|
||||
//
|
||||
keep: function(...values){
|
||||
values = normalizeSplit(values)
|
||||
var res = this.clone()
|
||||
|
||||
Object.entries(res.__index || {})
|
||||
.forEach(function(e){
|
||||
res.__index[e[0]] = e[1].intersect(values) })
|
||||
|
||||
return res
|
||||
},
|
||||
|
||||
// Replace tags...
|
||||
//
|
||||
// Replace tags...
|
||||
// .replace(from, to)
|
||||
// .replace(tag, to)
|
||||
// -> this
|
||||
//
|
||||
// Replace tags in list...
|
||||
// .replace(from, to, tag, ..)
|
||||
// .replace(from, to, [tag, ..])
|
||||
// .replace(tag, to, tag, ..)
|
||||
// .replace(tag, to, [tag, ..])
|
||||
// -> tags
|
||||
//
|
||||
// Replace tags via func's return values...
|
||||
@ -1221,8 +1121,7 @@ var BaseTagsPrototype = {
|
||||
// reachability or definitions...
|
||||
// NOTE: this will match tags in .__index, .persistent and .definitions
|
||||
//
|
||||
// XXX EXPERIMENTAL...
|
||||
replace: function(from, to, ...tags){
|
||||
replace: function(tag, to, ...tags){
|
||||
var that = this
|
||||
tags = normalizeSplit(tags)
|
||||
|
||||
@ -1233,15 +1132,18 @@ var BaseTagsPrototype = {
|
||||
|
||||
var local = arguments.length > 2
|
||||
|
||||
if(from instanceof Function){
|
||||
to = from
|
||||
from = '*'
|
||||
if(tag instanceof Function){
|
||||
to = tag
|
||||
tag = '*'
|
||||
}
|
||||
to = to instanceof Function ?
|
||||
to
|
||||
: this.normalize(to)
|
||||
|
||||
// XXX this uses definitions to collect tags, this is too broad...
|
||||
var res = this.directMatch(from, local ? tags : null, true)
|
||||
var res = this.directMatch(tag, local ? tags : null, true)
|
||||
// XXX is this needed / correct / worth it???
|
||||
.concat(local ? [] : this.directMatch(from,
|
||||
.concat(local ? [] : this.directMatch(tag,
|
||||
Object.entries(definitions)
|
||||
.map(function(e){
|
||||
e[1] = e[1].join(that.SET_SEPARATOR)
|
||||
@ -1249,38 +1151,38 @@ var BaseTagsPrototype = {
|
||||
return e })
|
||||
.flat(), true))
|
||||
.unique()
|
||||
.map(function(from){
|
||||
.map(function(tag){
|
||||
var target = to instanceof Function ?
|
||||
to.call(that, from)
|
||||
that.normalize(to.call(that, tag))
|
||||
: to
|
||||
|
||||
if(from == target || target == undefined){
|
||||
if(tag == target || target == undefined){
|
||||
return []
|
||||
}
|
||||
|
||||
// persistent...
|
||||
if(persistent.has(from)){
|
||||
persistent.delete(from)
|
||||
if(persistent.has(tag)){
|
||||
persistent.delete(tag)
|
||||
target != ''
|
||||
&& persistent.add(target)
|
||||
}
|
||||
|
||||
// index...
|
||||
if(from in index){
|
||||
if(tag in index){
|
||||
target != ''
|
||||
&& (index[target] = index[from].unite(index[target] || []))
|
||||
delete index[from]
|
||||
&& (index[target] = index[tag].unite(index[target] || []))
|
||||
delete index[tag]
|
||||
}
|
||||
|
||||
// definitions (key)...
|
||||
if(from in definitions){
|
||||
if(tag in definitions){
|
||||
target != ''
|
||||
&& that.define(target, definitions[from].join(that.SET_SEPARATOR))
|
||||
delete definitions[from]
|
||||
&& that.define(target, definitions[tag].join(that.SET_SEPARATOR))
|
||||
delete definitions[tag]
|
||||
}
|
||||
// definitions (value)...
|
||||
if(def_index.has(from)){
|
||||
def_index.get(from)
|
||||
if(def_index.has(tag)){
|
||||
def_index.get(tag)
|
||||
.forEach(function(key){
|
||||
that.define(key, target == '' ? null : target) })
|
||||
}
|
||||
@ -1294,6 +1196,11 @@ var BaseTagsPrototype = {
|
||||
res.flat()
|
||||
: this
|
||||
},
|
||||
// NOTE: this is a short hand to .rename(tag, '', ..) for extra
|
||||
// docs see that...
|
||||
removeTag: function(tag, ...tags){
|
||||
return this.rename(tag, '', ...tags) },
|
||||
|
||||
// Replace values...
|
||||
//
|
||||
// .replaceValue(from, to)
|
||||
@ -1305,6 +1212,38 @@ var BaseTagsPrototype = {
|
||||
values.has(from)
|
||||
&& values.delete(from)
|
||||
&& values.add(to) }) },
|
||||
// Keep only the given values...
|
||||
//
|
||||
// .keep(value, ..)
|
||||
// .keep([value, ..])
|
||||
// -> this
|
||||
//
|
||||
keep: function(...values){
|
||||
values = normalizeSplit(values)
|
||||
var res = this.clone()
|
||||
|
||||
Object.entries(res.__index || {})
|
||||
.forEach(function(e){
|
||||
res.__index[e[0]] = e[1].intersect(values) })
|
||||
|
||||
return res
|
||||
},
|
||||
// Remove the given values...
|
||||
//
|
||||
// .remove(value, ..)
|
||||
// .remove([value, ..])
|
||||
// -> this
|
||||
//
|
||||
remove: function(...values){
|
||||
values = normalizeSplit(values)
|
||||
var res = this.clone()
|
||||
|
||||
Object.entries(res.__index || {})
|
||||
.forEach(function(e){
|
||||
res.__index[e[0]] = e[1].subtract(values) })
|
||||
|
||||
return res
|
||||
},
|
||||
|
||||
// Get/set/remove tag definitions...
|
||||
//
|
||||
@ -1406,6 +1345,58 @@ var BaseTagsPrototype = {
|
||||
return this
|
||||
},
|
||||
|
||||
// Toggle a tag to persistent/non-persistent...
|
||||
//
|
||||
// A persistent tag is not removed when untagging a value.
|
||||
//
|
||||
// .togglePersistent(tag)
|
||||
// .togglePersistent(tag, tag, ...)
|
||||
// .togglePersistent([tag, tag, ...])
|
||||
// -> states
|
||||
//
|
||||
// .togglePersistent(tag, action)
|
||||
// .togglePersistent(tag, tag, ..., action)
|
||||
// .togglePersistent([tag, tag, ...], action)
|
||||
// -> states
|
||||
//
|
||||
//
|
||||
// action can be:
|
||||
// 'on' - toggle all tags on
|
||||
// 'off' - toggle all off
|
||||
// 'toggle' - toggle all depending on initial state
|
||||
// '?' - return list of states
|
||||
//
|
||||
//
|
||||
togglePersistent: function(...tags){
|
||||
action = ['on', 'off', 'toggle', '?'].includes(tags[tags.length-1]) ?
|
||||
tags.pop()
|
||||
: 'toggle'
|
||||
tags = normalizeSplit(tags)
|
||||
|
||||
var persistent =
|
||||
this.persistent =
|
||||
this.persistent || new Set()
|
||||
|
||||
return this.normalize(tags)
|
||||
.map(function(tag){
|
||||
return action == 'on' ?
|
||||
(persistent.add(tag), 'on')
|
||||
: action == 'off' ?
|
||||
(persistent.delete(tag), 'off')
|
||||
: action == 'toggle' ?
|
||||
(persistent.has(tag) ?
|
||||
(persistent.delete(tag), 'off')
|
||||
: (persistent.add(tag), 'on'))
|
||||
: (persistent.has(tag) ?
|
||||
'on'
|
||||
: 'off') })
|
||||
},
|
||||
// Make paths persistent...
|
||||
//
|
||||
// NOTE: this will touch only longest unique paths (see: .uniquePaths(..))
|
||||
makePathsPersistent: function(){
|
||||
this.persistent = new Set(this.uniquePaths())
|
||||
return this },
|
||||
|
||||
// Optimize tags...
|
||||
//
|
||||
@ -1439,13 +1430,6 @@ var BaseTagsPrototype = {
|
||||
&& that.untag(tags, value)
|
||||
return tags.length > 0 }) },
|
||||
|
||||
// Make paths persistent...
|
||||
//
|
||||
// NOTE: this will touch only longest unique paths (see: .uniquePaths(..))
|
||||
makePathsPersistent: function(){
|
||||
this.persistent = new Set(this.uniquePaths())
|
||||
return this },
|
||||
|
||||
|
||||
// Tags-Tags API...
|
||||
//
|
||||
@ -1836,6 +1820,7 @@ object.makeConstructor('BaseTags',
|
||||
// Add an ability to trigger handlers when working with specific (special)
|
||||
// tags.
|
||||
//
|
||||
// XXX idea: */* to make a tag persistent...
|
||||
// XXX EXPERIMENTAL...
|
||||
var TagsWithHandlersPrototype = {
|
||||
__proto__: BaseTagsPrototype,
|
||||
@ -1871,6 +1856,11 @@ var TagsWithHandlersPrototype = {
|
||||
// 'stop': function(tag, action, ...other){
|
||||
// return false },
|
||||
//
|
||||
// // make all paths persistent...
|
||||
// '*/*': function(tag, action){
|
||||
// action == 'tag'
|
||||
// && this.togglePersistent(tag) },
|
||||
//
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
@ -1934,31 +1924,50 @@ var TagsWithHandlersPrototype = {
|
||||
// no handlers -> return as-is...
|
||||
|| tags) },
|
||||
|
||||
//
|
||||
// Handler actions:
|
||||
// [method] [action]
|
||||
// .tag(..) -> 'tag'
|
||||
// .untag(..) -> 'untag'
|
||||
// .rename(..) -> 'rename'
|
||||
// -> 'remove'
|
||||
//
|
||||
// NOTE: these may modify the input tags...
|
||||
// handler: action(tag, 'tag')
|
||||
tag: function(tags, value){
|
||||
var that = this
|
||||
return object.parent(TagsWithHandlersPrototype.tag, this).call(this,
|
||||
that.handleSpecialTag(tags, 'tag', value),
|
||||
...[...arguments].slice(1)) },
|
||||
// handler: action(tag, 'untag')
|
||||
untag: function(tags, value){
|
||||
var that = this
|
||||
return object.parent(TagsWithHandlersPrototype.untag, this).call(this,
|
||||
that.handleSpecialTag(tags, 'untag', value),
|
||||
...[...arguments].slice(1)) },
|
||||
// handler: action(tag, 'remove')
|
||||
// handler: action(to, 'rename', tag)
|
||||
rename: function(tag, to, ...tags){
|
||||
return object.parent(TagsWithHandlersPrototype.rename, this).call(this,
|
||||
tags.length == 0 ?
|
||||
this.handleSpecialTag(tag, to == '' ? 'remove' : 'rename', to)
|
||||
: tag,
|
||||
...[...arguments].slice(1)) },
|
||||
tag,
|
||||
arguments.length == 2 ?
|
||||
(to == '' ?
|
||||
this.handleSpecialTag(tag, 'remove')
|
||||
: this.handleSpecialTag(to, 'rename', tag))
|
||||
: to,
|
||||
...[...arguments].slice(2)) },
|
||||
// handler: action(tag, 'replace', from)
|
||||
replace: function(tag, to, ...tags){
|
||||
// XXX can we avoid doing this here???
|
||||
if(tag instanceof Function){
|
||||
to = tag
|
||||
tag = '*'
|
||||
}
|
||||
return object.parent(TagsWithHandlersPrototype.replace, this).call(this,
|
||||
tag,
|
||||
arguments.length == 2 ?
|
||||
(to instanceof Function ?
|
||||
// wrap the handler...
|
||||
function(tag){
|
||||
var res = to.call(this, ...arguments)
|
||||
typeof(res) == typeof('str')
|
||||
&& this.handleSpecialTag(res, 'replace', tag)
|
||||
return res
|
||||
}
|
||||
: this.handleSpecialTag(to, 'replace', tag))
|
||||
: to,
|
||||
...[...arguments].slice(2)) },
|
||||
}
|
||||
|
||||
|
||||
@ -1978,8 +1987,8 @@ module.TagsWithHandlers =
|
||||
// Maintain non-normalized forms of tags added when tagging and provide
|
||||
// means of translating a tag back to that form.
|
||||
//
|
||||
// XXX hook tag and value removal...
|
||||
// XXX EXPERIMENTAL...
|
||||
// XXX do we need to hook .rename(..)
|
||||
var TagsWithDictPrototype = {
|
||||
__proto__: BaseTagsPrototype,
|
||||
|
||||
@ -2059,6 +2068,7 @@ var TagsWithDictPrototype = {
|
||||
// NOTE: an orphan is a dict entry for a tag that is no longer used.
|
||||
// NOTE: this will not remove tags that are not orphaned...
|
||||
// XXX check which of the branches is faster...
|
||||
// XXX do we want to .match(..) here???
|
||||
removeOrphansFromDict: function(...tags){
|
||||
if(!this.dict){
|
||||
return this
|
||||
@ -2107,12 +2117,36 @@ var TagsWithDictPrototype = {
|
||||
return res
|
||||
},
|
||||
rename: function(from, to, ...tags){
|
||||
var res = object.parent(TagsWithDictPrototype.rename, this).call(this, ...arguments)
|
||||
arguments.length == 2
|
||||
&& this.normalizeSave(to)
|
||||
var res = object.parent(TagsWithDictPrototype.rename, this).call(this, ...arguments)
|
||||
this.removeOrphansFromDict(from)
|
||||
return res
|
||||
},
|
||||
replace: function(tag, to, ...tags){
|
||||
if(tag instanceof Function){
|
||||
to = tag
|
||||
tag = '*'
|
||||
}
|
||||
var can_remove = []
|
||||
var res = object.parent(TagsWithDictPrototype.replace, this).call(this,
|
||||
tag,
|
||||
arguments.length == 2 ?
|
||||
(to instanceof Function ?
|
||||
// wrap the handler...
|
||||
function(tag){
|
||||
can_remove.push(tag)
|
||||
var res = to.call(this, ...arguments)
|
||||
return typeof(res) == typeof('str') ?
|
||||
this.normalizeSave(res)
|
||||
: res }
|
||||
: this.normalizeSave(to))
|
||||
: to,
|
||||
...[...arguments].slice(2))
|
||||
typeof(tag) == typeof('str')
|
||||
&& this.removeOrphansFromDict(can_remove)
|
||||
return res
|
||||
},
|
||||
togglePersistent: function(...tags){
|
||||
this.normalizeSave(tags)
|
||||
var res = object.parent(TagsWithDictPrototype.togglePersistent, this).call(this, ...arguments)
|
||||
@ -2131,6 +2165,8 @@ var TagsWithDictPrototype = {
|
||||
&& this.removeOrphansFromDict(tag)
|
||||
return res
|
||||
},
|
||||
// Serialization...
|
||||
//
|
||||
// Format:
|
||||
// {
|
||||
// ...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user