.walk(..) experiment almost done...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-04-21 16:54:19 +03:00
parent b5cf08f12e
commit ed0c942676

View File

@ -501,6 +501,7 @@ var BaseBrowserPrototype = {
// calls for such items... // calls for such items...
// //
// XXX revise options handling for .__list__(..) // XXX revise options handling for .__list__(..)
// XXX make(browser) should add a browser as-is without any options... (???)
make: function(options){ make: function(options){
options = Object.assign(Object.create(this.options || {}), options || {}) options = Object.assign(Object.create(this.options || {}), options || {})
@ -510,83 +511,112 @@ var BaseBrowserPrototype = {
// item constructor... // item constructor...
// //
// Make an item...
// make(value[, options]) // make(value[, options])
// make(value, func[, options]) // make(value, func[, options])
// -> make // -> make
// //
// Inline a browser instance...
// make(browser)
// -> make
//
//
// NOTE: when inlining a browser, options are ignored.
// NOTE: when inlining a browser it's .parent will be set this
// reusing the inlined object browser may mess up this
// property...
//
// XXX problem: make(Browser(..), ..) and make.group(...) produce
// different formats -- the first stores {value: browser, ...}
// while the latter stores a list of items.
// ...would be more logical to store the object (i.e. browser/list)
// directly as the element...
var make_called = false var make_called = false
var make = function(value, opts){ var make = function(value, opts){
make_called = true make_called = true
var args = [...arguments]
opts = opts || {} // special-case: inlined browser...
// handle: make(.., func, ..) //
opts = opts instanceof Function ? // NOTE: we ignore opts here...
{open: opts} // XXX not sure if this is the right way to go...
: opts // ...for removal just remove the if statement and its
// handle trailing options... // first branch...
opts = args.length > 2 ? if(value instanceof Browser){
Object.assign({}, var item = value
args.pop(), item.parent = this
opts)
: opts
opts = Object.assign(
{},
opts,
{value: value})
// item id... // normal item...
var key = this.__key__(opts) } else {
var id_changed = (old_index[key] || {}).id_changed var args = [...arguments]
opts = opts || {}
// handle: make(.., func, ..)
opts = opts instanceof Function ?
{open: opts}
: opts
// handle trailing options...
opts = args.length > 2 ?
Object.assign({},
args.pop(),
opts)
: opts
opts = Object.assign(
{},
opts,
{value: value})
// handle duplicate ids -> err if found... // item id...
if(opts.id && opts.id in new_index){ var key = this.__key__(opts)
throw new Error(`make(..): duplicate id "${key}": ` var id_changed = (old_index[key] || {}).id_changed
+`can't create multiple items with the same key.`) }
// handle duplicate keys... // handle duplicate ids -> err if found...
// NOTE: we can't reuse an old copy when re-making the list if(opts.id && opts.id in new_index){
// because there is now way to correctly identify an throw new Error(`make(..): duplicate id "${key}": `
// object when it's id is tweaked (and we can not rely
// on item order)...
// ...for this reason all "persistent" state for such
// an element will be lost when calling .make(..) again
// and re-making the list...
// a solution to this would be to manually assign an .id
// to such elements in .__list__(..)...
// XXX can we go around this without requiring the user
// to manage ids???
var k = key
while(k in new_index){
// duplicate keys disabled...
if(options.noDuplicateValues){
throw new Error(`make(..): duplicate key "${key}": `
+`can't create multiple items with the same key.`) } +`can't create multiple items with the same key.`) }
// handle duplicate keys...
// NOTE: we can't reuse an old copy when re-making the list
// because there is now way to correctly identify an
// object when it's id is tweaked (and we can not rely
// on item order)...
// ...for this reason all "persistent" state for such
// an element will be lost when calling .make(..) again
// and re-making the list...
// a solution to this would be to manually assign an .id
// to such elements in .__list__(..)...
// XXX can we go around this without requiring the user
// to manage ids???
var k = key
while(k in new_index){
// duplicate keys disabled...
if(options.noDuplicateValues){
throw new Error(`make(..): duplicate key "${key}": `
+`can't create multiple items with the same key.`) }
// mark both the current and the first items as id-mutated... // mark both the current and the first items as id-mutated...
opts.id_changed = true opts.id_changed = true
new_index[key].id_changed = true new_index[key].id_changed = true
// create a new key... // create a new key...
k = this.__id__(key) k = this.__id__(key)
}
key = opts.id = k
// build the item...
var item = Object.assign(
Object.create(options || {}),
// get the old item values (only for non duplicate items)...
id_changed ?
{}
: old_index[key] || {},
// XXX inherit from this...
opts,
{
parent: this,
})
// XXX do we need both this and the above ref???
item.sublist instanceof Browser
&& (item.sublist.parent = this)
} }
key = opts.id = k
// build the item...
var item = Object.assign(
Object.create(options || {}),
// get the old item values (only for non duplicate items)...
id_changed ?
{}
: old_index[key] || {},
// XXX inherit from this...
opts,
{
parent: this,
})
// XXX do we need both this and the above ref???
item.sublist instanceof Browser
&& (item.sublist.parent = this)
// store the item... // store the item...
items.push(item) items.push(item)
@ -1191,19 +1221,28 @@ var BaseBrowserPrototype = {
: this }) : this })
.map(function(elem){ .map(function(elem){
return ( return (
// item not iterable -> skip...
(!iterateNonIterable && elem.noniterable) ?
[]
// group... // group...
: elem instanceof Array ? (elem instanceof Array ?
walk(path, elem) walk(path, elem)
// item not iterable -> skip...
: !iterateNonIterable && elem.noniterable) ?
[]
// elem is Browser (inline)...
: elem instanceof Browser ?
elem.map(func,
options.inlinedPaths ?
path.concat(elem.id)
: path.slice(),
options)
// value is Browser (inline)... // value is Browser (inline)...
/*/ XXX legacy...
: elem.value instanceof Browser ? : elem.value instanceof Browser ?
elem.value.map(func, elem.value.map(func,
options.inlinedPaths ? options.inlinedPaths ?
path.concat(elem.id) path.concat(elem.id)
: path.slice(), : path.slice(),
options) options)
//*/
// .sublist is Browser (nested)... // .sublist is Browser (nested)...
: (!skipNested : (!skipNested
&& elem.sublist instanceof Browser) ? && elem.sublist instanceof Browser) ?
@ -1228,7 +1267,106 @@ var BaseBrowserPrototype = {
// XXX EXPERIMENTAL...
//
// .walk(func[, options])
// -> result
//
// func(elem, nested, sublist)
// -> array
//
// nested(list[, options])
// -> items
//
//
// XXX this should be just like .map(..) but add ability to handle
// node types in func...
// this can be done in several ways:
// - pass type info + control callback (do/skip)
// - pass a set of handlers
// (a-la how .render(..) uses node render methods)
walk: function(func, options){ walk: function(func, options){
var that = this
// parse args...
var args = [...arguments]
func = args[0] instanceof Function ?
args.shift()
: undefined
var path = (args[0] instanceof Array
|| typeof(args[0]) == typeof('str')) ?
args.shift()
: []
path = path instanceof Array ? path : [path]
var options = args.pop() || {}
var walk = function(path, list){
return list
// XXX add reversing support...
.map(function(elem){
var elem_id = elem.id || elem.value
// these will be set in the return expression below...
var sublist
var p
var nested_called = false
var nested = function(list, opts){
nested_called = true
return list instanceof Array ?
walk(p, list)
: list.walk(func, p, opts || options) }
return (
// inline browser or array...
(elem instanceof Array
|| elem instanceof Browser) ?
func.call(that,
p = path,
null,
nested,
sublist = elem)
// nested browser / array...
: (elem.sublist instanceof Browser
|| elem.sublist instanceof Array) ?
func.call(that,
p = path.concat([elem_id]),
elem,
nested,
// XXX handle elem.collapsed and friends...
sublist = elem.sublist)
// normal element...
: func.call(that,
p = path.concat([elem_id]),
elem,
null,
sublist = null) )
// append nested elements...
.concat((!sublist || nested_called) ?
[]
: nested(sublist))
})
.flat()
}
return walk(path, this.items)
},
map2: function(func, options){
var that = this
var args = [...arguments]
func = args[0] instanceof Function ?
args.shift()
: undefined
var options = args.pop() || {}
return this.walk(function(path, elem){
return elem != null ?
[func === undefined ?
elem
: func.call(that, elem, path)]
: [] }, options)
}, },