mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 02:10:08 +00:00
refactoring + a several util methods in tags.js
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
158da70086
commit
1667632227
@ -132,9 +132,8 @@ if(typeof(sha1) != 'undefined'){
|
|||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
// Data...
|
||||||
|
|
||||||
// Data class methods and API...
|
|
||||||
//
|
|
||||||
var DataClassPrototype = {
|
var DataClassPrototype = {
|
||||||
// NOTE: we consider the input list sorted...
|
// NOTE: we consider the input list sorted...
|
||||||
fromArray: function(list){
|
fromArray: function(list){
|
||||||
@ -157,12 +156,6 @@ var DataClassPrototype = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
|
||||||
|
|
||||||
// Data object methods and API...
|
|
||||||
//
|
|
||||||
var DataPrototype = {
|
var DataPrototype = {
|
||||||
|
|
||||||
get version(){
|
get version(){
|
||||||
@ -231,6 +224,9 @@ var DataPrototype = {
|
|||||||
//
|
//
|
||||||
/*****************************************************************/
|
/*****************************************************************/
|
||||||
|
|
||||||
|
ribbon_order: null,
|
||||||
|
ribbons: null,
|
||||||
|
|
||||||
get current(){
|
get current(){
|
||||||
return this.__current = this.__current
|
return this.__current = this.__current
|
||||||
|| this.getImages(this.ribbon_order[0])[0]
|
|| this.getImages(this.ribbon_order[0])[0]
|
||||||
@ -238,11 +234,12 @@ var DataPrototype = {
|
|||||||
set current(value){
|
set current(value){
|
||||||
this.focusImage(value) },
|
this.focusImage(value) },
|
||||||
|
|
||||||
// XXX should this default to top or bottom ribbon???
|
|
||||||
get base(){
|
get base(){
|
||||||
return this.__base || this.ribbon_order[0] },
|
return this.__base || this.ribbon_order[0] },
|
||||||
set base(value){
|
set base(value){
|
||||||
this.__base = value },
|
this.__base = value in this.ribbons ?
|
||||||
|
this.getRibbon(value)
|
||||||
|
: value },
|
||||||
|
|
||||||
get order(){
|
get order(){
|
||||||
return this.__order },
|
return this.__order },
|
||||||
@ -427,7 +424,6 @@ var DataPrototype = {
|
|||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Remove duplicate items from list in-place...
|
// Remove duplicate items from list in-place...
|
||||||
//
|
//
|
||||||
// NOTE: only the first occurrence is kept...
|
// NOTE: only the first occurrence is kept...
|
||||||
@ -1554,10 +1550,7 @@ var DataPrototype = {
|
|||||||
// This is signature compatible with .getRibbon(..), see it for more
|
// This is signature compatible with .getRibbon(..), see it for more
|
||||||
// info...
|
// info...
|
||||||
setBase: function(target, offset){
|
setBase: function(target, offset){
|
||||||
var base = this.getRibbon(target, offset)
|
this.base = this.getRibbon(target, offset)
|
||||||
if(base in this.ribbons){
|
|
||||||
this.base = base
|
|
||||||
}
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2858,7 +2851,6 @@ var DataPrototype = {
|
|||||||
var that = this
|
var that = this
|
||||||
data = typeof(data) == typeof('str') ? JSON.parse(data) : data
|
data = typeof(data) == typeof('str') ? JSON.parse(data) : data
|
||||||
data = formats.updateData(data, DATA_VERSION)
|
data = formats.updateData(data, DATA_VERSION)
|
||||||
this.base = data.base
|
|
||||||
this.order = data.order.slice()
|
this.order = data.order.slice()
|
||||||
this.ribbon_order = data.ribbon_order.slice()
|
this.ribbon_order = data.ribbon_order.slice()
|
||||||
|
|
||||||
@ -2876,6 +2868,7 @@ var DataPrototype = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.current = data.current
|
this.current = data.current
|
||||||
|
this.base = data.base
|
||||||
|
|
||||||
// extra data...
|
// extra data...
|
||||||
!clean
|
!clean
|
||||||
@ -2926,8 +2919,17 @@ var DataPrototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
/*********************************************************************/
|
var BaseData =
|
||||||
|
module.BaseData =
|
||||||
|
object.makeConstructor('BaseData',
|
||||||
|
DataClassPrototype,
|
||||||
|
DataPrototype)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
// XXX make a API compatible replacement to the above -- to access
|
// XXX make a API compatible replacement to the above -- to access
|
||||||
// compatibility and performance...
|
// compatibility and performance...
|
||||||
@ -3220,7 +3222,17 @@ var DataWithTagsPrototype = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var DataWithTags =
|
||||||
|
module.DataWithTags =
|
||||||
|
object.makeConstructor('DataWithTags',
|
||||||
|
DataClassPrototype,
|
||||||
|
DataWithTagsPrototype)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
// Proxy Data API to one of the target data objects...
|
// Proxy Data API to one of the target data objects...
|
||||||
var DataProxyPrototype = {
|
var DataProxyPrototype = {
|
||||||
@ -3236,28 +3248,13 @@ var DataProxyPrototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
|
||||||
|
|
||||||
// Main Data object...
|
|
||||||
var BaseData =
|
|
||||||
module.BaseData =
|
|
||||||
object.makeConstructor('BaseData',
|
|
||||||
DataClassPrototype,
|
|
||||||
DataPrototype)
|
|
||||||
|
|
||||||
|
|
||||||
var DataWithTags =
|
|
||||||
module.DataWithTags =
|
|
||||||
object.makeConstructor('DataWithTags',
|
|
||||||
DataClassPrototype,
|
|
||||||
DataWithTagsPrototype)
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
var Data =
|
var Data =
|
||||||
module.Data = DataWithTags
|
module.Data = DataWithTags
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* vim:set ts=4 sw=4 : */ return module })
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
|
|||||||
@ -49,7 +49,7 @@ var normalizeSplit = function(args){
|
|||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
var TagsClassPrototype = {
|
var BaseTagsClassPrototype = {
|
||||||
// Utils...
|
// Utils...
|
||||||
//
|
//
|
||||||
// .normalize(tag)
|
// .normalize(tag)
|
||||||
@ -207,74 +207,42 @@ var TagsClassPrototype = {
|
|||||||
// ...there are two ways to think of this:
|
// ...there are two ways to think of this:
|
||||||
// 1) both (a-la flickr) -- keep both, use normalized internally
|
// 1) both (a-la flickr) -- keep both, use normalized internally
|
||||||
// 2) only normalized -- simpler but may surprise the user and not be as pretty...
|
// 2) only normalized -- simpler but may surprise the user and not be as pretty...
|
||||||
var TagsPrototype = {
|
// XXX should we split out the non-basic stuff???
|
||||||
|
// like:
|
||||||
|
// .makePathsPersistent()
|
||||||
|
// .optimizeTags()
|
||||||
|
// ...
|
||||||
|
var BaseTagsPrototype = {
|
||||||
config: {
|
config: {
|
||||||
tagRemovedChars: '[\\s-_]',
|
tagRemovedChars: '[\\s-_]',
|
||||||
},
|
},
|
||||||
|
|
||||||
// data...
|
// Tag index...
|
||||||
//
|
//
|
||||||
// Format:
|
|
||||||
// Set([ <tag>, ... ])
|
|
||||||
//
|
|
||||||
// XXX Q: should these be normalized???
|
|
||||||
__persistent_tags: null,
|
|
||||||
|
|
||||||
// Format:
|
|
||||||
// {
|
|
||||||
// <alias>: <normalized-tag>,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// XXX need introspection for this...
|
|
||||||
// ...should this be .aliases ???
|
|
||||||
__aliases: null,
|
|
||||||
|
|
||||||
// Format:
|
// Format:
|
||||||
// {
|
// {
|
||||||
// <tag>: [ <item>, ... ],
|
// <tag>: [ <item>, ... ],
|
||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
__index: null,
|
__index: null,
|
||||||
|
|
||||||
|
|
||||||
|
// Persistent tags...
|
||||||
|
//
|
||||||
|
// Format:
|
||||||
|
// Set([ <tag>, ... ])
|
||||||
|
//
|
||||||
|
persistent: null,
|
||||||
|
|
||||||
// XXX EXPERIMENTAL...
|
// Tag aliases...
|
||||||
// XXX need a way to edit the compound tag...
|
//
|
||||||
__special_tag_handlers__: {
|
// Format:
|
||||||
'*persistent*': function(action, tag, value){
|
// {
|
||||||
// XXX remove the tag...
|
// <alias>: <normalized-tag>,
|
||||||
// XXX add the tag to .__persistent_tags
|
// }
|
||||||
// XXX return the new tag for normal handling...
|
//
|
||||||
},
|
aliases: null,
|
||||||
},
|
|
||||||
handleSpecialTag: function(action, tag, value){
|
|
||||||
var that = this
|
|
||||||
var handlers = this.__special_tag_handlers__ || {}
|
|
||||||
|
|
||||||
// get the matching handler key...
|
|
||||||
var key = Object.keys(handlers)
|
|
||||||
.filter(function(k){
|
|
||||||
return that.match(k, tag) })
|
|
||||||
// XXX should we handle multiple matches???
|
|
||||||
.shift()
|
|
||||||
|
|
||||||
// resolve handler aliases...
|
|
||||||
var match = key
|
|
||||||
do {
|
|
||||||
match = handlers[match]
|
|
||||||
} while(!(match instanceof Function) && match in handlers)
|
|
||||||
|
|
||||||
// no handler...
|
|
||||||
if(!(match instanceof Function)){
|
|
||||||
// XXX
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX remove key from tag...
|
|
||||||
|
|
||||||
return match.call(this, action, tag, value)
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Utils...
|
// Utils...
|
||||||
@ -569,6 +537,41 @@ var TagsPrototype = {
|
|||||||
//return that.directMatch(query, t, cmp) }) },
|
//return that.directMatch(query, t, cmp) }) },
|
||||||
return that.match(query, t, cmp) }) },
|
return that.match(query, t, cmp) }) },
|
||||||
|
|
||||||
|
// Keep only the longest matching paths...
|
||||||
|
//
|
||||||
|
// .uniquePaths(path, ..)
|
||||||
|
// .uniquePaths([path, ..])
|
||||||
|
// -> paths
|
||||||
|
//
|
||||||
|
// Algorithm:
|
||||||
|
// - sort by path length descending
|
||||||
|
// - take top path (head)
|
||||||
|
// - remove all paths that match it after it (tail)
|
||||||
|
// - next path
|
||||||
|
//
|
||||||
|
uniquePaths: function(...list){
|
||||||
|
var that = this
|
||||||
|
return that.normalize(normalizeSplit(list))
|
||||||
|
// sort by number of path elements (longest first)...
|
||||||
|
.map(function(p){ return p.split(/[\\\/]/g) })
|
||||||
|
.sort(function(a, b){ return b.length - a.length })
|
||||||
|
.map(function(p){ return p.join('/') })
|
||||||
|
// remove all paths in tail that match the current...
|
||||||
|
.map(function(p, i, list){
|
||||||
|
// skip []...
|
||||||
|
!(p instanceof Array)
|
||||||
|
&& list
|
||||||
|
// only handle the tail...
|
||||||
|
.slice(i+1)
|
||||||
|
.forEach(function(o, j){
|
||||||
|
// skip []...
|
||||||
|
!(p instanceof Array)
|
||||||
|
&& that.directMatch(o, p)
|
||||||
|
// match -> replace the matching elem with []
|
||||||
|
&& (list[i+j+1] = []) })
|
||||||
|
return p })
|
||||||
|
.flat() },
|
||||||
|
|
||||||
|
|
||||||
// Introspection and Access API...
|
// Introspection and Access API...
|
||||||
//
|
//
|
||||||
@ -611,7 +614,7 @@ var TagsPrototype = {
|
|||||||
// -> bool
|
// -> bool
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// NOTE: this includes all the .persistent tags as well as all the
|
// NOTE: this includes all the persistent tags as well as all the
|
||||||
// tags actually used.
|
// tags actually used.
|
||||||
//
|
//
|
||||||
// XXX should this return split values???
|
// XXX should this return split values???
|
||||||
@ -641,7 +644,7 @@ var TagsPrototype = {
|
|||||||
// get all tags...
|
// get all tags...
|
||||||
} else {
|
} else {
|
||||||
return Object.keys(this.__index || {})
|
return Object.keys(this.__index || {})
|
||||||
.concat([...(this.__persistent_tags || [])]
|
.concat([...(this.persistent || [])]
|
||||||
.map(function(t){
|
.map(function(t){
|
||||||
return that.normalize(t) }))
|
return that.normalize(t) }))
|
||||||
.unique()
|
.unique()
|
||||||
@ -700,7 +703,7 @@ var TagsPrototype = {
|
|||||||
// -> this
|
// -> this
|
||||||
//
|
//
|
||||||
alias: function(tag, value){
|
alias: function(tag, value){
|
||||||
var aliases = this.__aliases = this.__aliases || {}
|
var aliases = this.aliases = this.aliases || {}
|
||||||
// XXX this seems a bit ugly...
|
// XXX this seems a bit ugly...
|
||||||
var resolve = function(tag, seen){
|
var resolve = function(tag, seen){
|
||||||
seen = seen || []
|
seen = seen || []
|
||||||
@ -903,8 +906,8 @@ var TagsPrototype = {
|
|||||||
tags = normalizeSplit(tags)
|
tags = normalizeSplit(tags)
|
||||||
|
|
||||||
var persistent =
|
var persistent =
|
||||||
this.__persistent_tags =
|
this.persistent =
|
||||||
this.__persistent_tags || new Set()
|
this.persistent || new Set()
|
||||||
|
|
||||||
return this.normalize(tags)
|
return this.normalize(tags)
|
||||||
.map(function(tag){
|
.map(function(tag){
|
||||||
@ -1004,9 +1007,9 @@ var TagsPrototype = {
|
|||||||
|
|
||||||
// rename actual data...
|
// rename actual data...
|
||||||
} else {
|
} else {
|
||||||
patchSet(this.__persistent_tags || [])
|
patchSet(this.persistent || [])
|
||||||
patchObj(this.__index || {})
|
patchObj(this.__index || {})
|
||||||
patchObj(this.__aliases || {}, true)
|
patchObj(this.aliases || {}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
@ -1051,8 +1054,45 @@ var TagsPrototype = {
|
|||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Keep only the longest tag paths per value...
|
||||||
|
//
|
||||||
|
// Optimize tags for all values...
|
||||||
|
// .optimizeTags()
|
||||||
|
// -> values
|
||||||
|
//
|
||||||
|
// Optimize tags for specific values...
|
||||||
|
// .optimizeTags(value, ..)
|
||||||
|
// .optimizeTags([value, ..])
|
||||||
|
// -> values
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// var ts = new Tags()
|
||||||
|
// ts.tag(['a/b/c', 'a/c'], x)
|
||||||
|
//
|
||||||
|
// ts.optimizeTags() // will remove 'a/c' form x as it is fully
|
||||||
|
// // contained within 'a/b/c'...
|
||||||
|
//
|
||||||
|
// XXX should this be done on .tag(..) and friends???
|
||||||
|
optimizeTags: function(...values){
|
||||||
|
var that = this
|
||||||
|
return (normalizeSplit(values) || this.values())
|
||||||
|
.filter(function(value){
|
||||||
|
var tags = new Set(that.paths(value))
|
||||||
|
tags = [...tags.subtract(that.uniquePaths(...tags))]
|
||||||
|
tags.length > 0
|
||||||
|
&& that.untag(tags, value)
|
||||||
|
return tags.length > 0 }) },
|
||||||
|
|
||||||
// Tags - Tags API...
|
// Make all paths persistent...
|
||||||
|
//
|
||||||
|
// NOTE: this will add only longest unique paths (see: .uniquePaths(..))
|
||||||
|
makePathsPersistent: function(){
|
||||||
|
this.persistent = new Set(this.uniquePaths(this.paths()))
|
||||||
|
return this },
|
||||||
|
|
||||||
|
|
||||||
|
// Tags-Tags API...
|
||||||
//
|
//
|
||||||
// Join 1 or more Tags objects...
|
// Join 1 or more Tags objects...
|
||||||
//
|
//
|
||||||
@ -1379,12 +1419,12 @@ var TagsPrototype = {
|
|||||||
var res = {}
|
var res = {}
|
||||||
|
|
||||||
// aliases...
|
// aliases...
|
||||||
this.__aliases && Object.keys(this.__aliases).length > 0
|
this.aliases && Object.keys(this.aliases).length > 0
|
||||||
&& (res.aliases = Object.assign({}, this.__aliases))
|
&& (res.aliases = Object.assign({}, this.aliases))
|
||||||
|
|
||||||
// persistent tags...
|
// persistent tags...
|
||||||
this.__persistent_tags && this.__persistent_tags.size > 0
|
this.persistent && this.persistent.size > 0
|
||||||
&& (res.persistent = [...this.__persistent_tags])
|
&& (res.persistent = [...this.persistent])
|
||||||
|
|
||||||
// tags...
|
// tags...
|
||||||
res.tags = {}
|
res.tags = {}
|
||||||
@ -1400,11 +1440,11 @@ var TagsPrototype = {
|
|||||||
|
|
||||||
// aliases...
|
// aliases...
|
||||||
json.aliases
|
json.aliases
|
||||||
&& (this.__aliases = Object.assign({}, json.aliases))
|
&& (this.aliases = Object.assign({}, json.aliases))
|
||||||
|
|
||||||
// persistent tags...
|
// persistent tags...
|
||||||
json.persistent
|
json.persistent
|
||||||
&& (this.__persistent_tags = new Set(json.persistent))
|
&& (this.persistent = new Set(json.persistent))
|
||||||
|
|
||||||
// tags...
|
// tags...
|
||||||
json.tags
|
json.tags
|
||||||
@ -1424,11 +1464,90 @@ var TagsPrototype = {
|
|||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
var Tags =
|
var BaseTags =
|
||||||
module.Tags =
|
module.BaseTags =
|
||||||
object.makeConstructor('Tags',
|
object.makeConstructor('BaseTags',
|
||||||
TagsClassPrototype,
|
BaseTagsClassPrototype,
|
||||||
TagsPrototype)
|
BaseTagsPrototype)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// XXX EXPERIMENTAL...
|
||||||
|
var TagsWithHandlersPrototype = {
|
||||||
|
__proto__: BaseTagsPrototype,
|
||||||
|
|
||||||
|
// XXX docs...
|
||||||
|
__special_tag_handlers__: {
|
||||||
|
// print and remove tag...
|
||||||
|
'test': function(tag, action, value){
|
||||||
|
console.log('TEST TAG:', tag, action, value)
|
||||||
|
return this.removeTag('test', tag)[0]
|
||||||
|
},
|
||||||
|
// terminate handling...
|
||||||
|
'stop': function(tag, action, value){
|
||||||
|
console.log('STOP:', tag, action, value)
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
// print the tag...
|
||||||
|
'*': function(tag, action, value){
|
||||||
|
console.log('TAG:', tag, action, value)
|
||||||
|
return tag
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// NOTE: handlers are called in order of handler occurrence and not
|
||||||
|
// in the order the tags are in the given chain/path...
|
||||||
|
handleSpecialTag: function(tag, ...args){
|
||||||
|
var that = this
|
||||||
|
var handlers = this.__special_tag_handlers__
|
||||||
|
tag = this.normalize(tag)
|
||||||
|
return Object.keys(handlers)
|
||||||
|
.filter(function(p){
|
||||||
|
// keep only valid tag patterns...
|
||||||
|
// NOTE: this enables us to create special handlers
|
||||||
|
// that will not be used for matching but are more
|
||||||
|
// mnemonic...
|
||||||
|
return p == that.normalize(p)
|
||||||
|
// get the matching handler keys...
|
||||||
|
&& that.directMatch(p, tag)
|
||||||
|
})
|
||||||
|
// resolve handler aliases...
|
||||||
|
.map(function(match){
|
||||||
|
do {
|
||||||
|
match = handlers[match]
|
||||||
|
} while(!(match instanceof Function) && match in handlers)
|
||||||
|
|
||||||
|
return match instanceof Function ?
|
||||||
|
match
|
||||||
|
// no handler...
|
||||||
|
: [] })
|
||||||
|
.flat()
|
||||||
|
// call the handlers...
|
||||||
|
// NOTE: we are threading tag through the handlers...
|
||||||
|
.reduce(function(tag, handler){
|
||||||
|
return tag && handler.call(that, tag, ...args) }, tag)
|
||||||
|
// no handlers -> return as-is...
|
||||||
|
|| tag },
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var TagsWithHandlers =
|
||||||
|
module.TagsWithHandlers =
|
||||||
|
object.makeConstructor('TagsWithHandlers',
|
||||||
|
BaseTagsClassPrototype,
|
||||||
|
TagsWithHandlersPrototype)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
var Tags =
|
||||||
|
module.Tags = TagsWithHandlers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user