.walk2(..) done -- needs testing...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-05-04 04:46:29 +03:00
parent bed990372b
commit dffd66a8ae

View File

@ -151,7 +151,7 @@ Items.group = function(...items){
Items.nest = function(item, list, options){ Items.nest = function(item, list, options){
options = options || {} options = options || {}
//options = Object.assign(Object.create(this.options || {}), options || {}) //options = Object.assign(Object.create(this.options || {}), options || {})
options.sublist = list instanceof Array ? options.children = list instanceof Array ?
collectItems(this, list) collectItems(this, list)
: list : list
return this(item, options) return this(item, options)
@ -312,7 +312,7 @@ var BaseBrowserPrototype = {
// XXX can we make the format here simpler with less level // XXX can we make the format here simpler with less level
// of indirection?? // of indirection??
// ...currently to go down a path we need to: // ...currently to go down a path we need to:
// this.item_key_index.A.sublist.item_key_index.B.sublist... // this.item_key_index.A.children.item_key_index.B.children...
// would be nice to be closer to: // would be nice to be closer to:
// this.A.B... // this.A.B...
__item_index: null, __item_index: null,
@ -358,21 +358,21 @@ var BaseBrowserPrototype = {
.reduce(function(res, e){ .reduce(function(res, e){
return e.collapsed ? return e.collapsed ?
res + 1 res + 1
: res + e.sublist.length }, 0) }, : res + e.children.length }, 0) },
// tree -- ignores .collapsed... // tree -- ignores .collapsed...
get lengthTree(){ get lengthTree(){
return this.map({skipNested: true}).length return this.map({skipNested: true}).length
// XXX this is wrong as it will not account for nested nested elements... // XXX this is wrong as it will not account for nested nested elements...
+ this.nested() + this.nested()
.reduce(function(res, e){ .reduce(function(res, e){
return res + e.sublist.length }, 0) }, return res + e.children.length }, 0) },
// full -- ignores .collapsed and .noniterable... // full -- ignores .collapsed and .noniterable...
get lengthAll(){ get lengthAll(){
return this.map({skipNested: true, iterateNonIterable: true}).length return this.map({skipNested: true, iterateNonIterable: true}).length
// XXX this is wrong as it will not account for nested nested elements... // XXX this is wrong as it will not account for nested nested elements...
+ this.nested() + this.nested()
.reduce(function(res, e){ .reduce(function(res, e){
return res + (e.sublist.lengthAll || e.sublist.length) }, 0) }, return res + (e.children.lengthAll || e.children.length) }, 0) },
// Item list constructor... // Item list constructor...
@ -418,7 +418,7 @@ var BaseBrowserPrototype = {
// id: <string>, // id: <string>,
// value: <string> | <array>, // value: <string> | <array>,
// //
// sublist: <browser> | <array>, // children: <browser> | <array>,
// //
// focused: <bool>, // focused: <bool>,
// selected: <bool>, // selected: <bool>,
@ -502,7 +502,7 @@ var BaseBrowserPrototype = {
// //
// //
// //
// handleItem(path, elem, index, doNested, sublist) // handleItem(path, elem, index, doNested, children)
// -> items // -> items
// //
// Trigger nested item handling... // Trigger nested item handling...
@ -529,7 +529,7 @@ var BaseBrowserPrototype = {
// //
// //
// Handle recursion down... // Handle recursion down...
// handleRecursion(func, index, path, sublist, options) // handleRecursion(func, index, path, children, options)
// -> items // -> items
// //
// //
@ -558,7 +558,7 @@ var BaseBrowserPrototype = {
// //
// // If true do not skip items with .noniterable set to true... // // If true do not skip items with .noniterable set to true...
// iterateNonIterable: <bool>, // iterateNonIterable: <bool>,
// // If true do not skip item.sublist of items with .collapsed // // If true do not skip item.children of items with .collapsed
// // set to true... // // set to true...
// iterateCollapsed: <bool>, // iterateCollapsed: <bool>,
// // If true skip iterating nested items... // // If true skip iterating nested items...
@ -609,7 +609,7 @@ var BaseBrowserPrototype = {
|| typeof(args[0]) == typeof('str')) ? || typeof(args[0]) == typeof('str')) ?
args.shift() args.shift()
: undefined : undefined
var userIsWalkable = args[0] instanceof Function ? var walkable = args[0] instanceof Function ?
args.shift() args.shift()
: null : null
var i = typeof(args[0]) == typeof(123) ? var i = typeof(args[0]) == typeof(123) ?
@ -620,19 +620,16 @@ var BaseBrowserPrototype = {
args.shift() args.shift()
: [] : []
path = path instanceof Array ? path : [path] path = path instanceof Array ? path : [path]
options = args.pop() || {} options = args.pop() || {}
// set call context... // set call context...
// XXX revise this....
options = !options.root ? options = !options.root ?
Object.assign({}, Object.assign({},
this.options,
options, options,
{root: this}) {root: this})
: options : options
// options...
options = Object.assign(Object.create(this.options || {}), options)
/*var start = options.start || 0 /*var start = options.start || 0
var end = options.end var end = options.end
|| (options.count >= 0 && start + (options.count)) || (options.count >= 0 && start + (options.count))
@ -645,9 +642,9 @@ var BaseBrowserPrototype = {
(options.defaultReverse || 'tree') (options.defaultReverse || 'tree')
: options.reverse : options.reverse
var isWalkable = userIsWalkable ? var isWalkable = walkable ?
function(elem){ function(elem){
return elem instanceof Array || userIsWalkable(elem) } return elem instanceof Array || walkable(elem) }
: function(elem){ : function(elem){
return elem instanceof Array || elem instanceof Browser } return elem instanceof Array || elem instanceof Browser }
@ -669,7 +666,7 @@ var BaseBrowserPrototype = {
var elem_id = elem.id || elem.value var elem_id = elem.id || elem.value
// these will be set in the return expression below... // these will be set in the return expression below...
var sublist var children
var p var p
// nested browser/list handler... // nested browser/list handler...
@ -694,10 +691,12 @@ var BaseBrowserPrototype = {
// normalize... // normalize...
list = list === true ? list = list === true ?
sublist children
: list === false ?
list
: (!iterateCollapsed && elem.collapsed) ? : (!iterateCollapsed && elem.collapsed) ?
[] []
: (list || sublist) : (list || children)
i = j != null ? j : i i = j != null ? j : i
return ( return (
@ -731,25 +730,25 @@ var BaseBrowserPrototype = {
if(isWalkable(elem)){ if(isWalkable(elem)){
inline = true inline = true
p = path p = path
sublist = elem children = elem
// nested browser / array... // nested browser / array...
} else if(!skipNested } else if(!skipNested
&& isWalkable(elem.sublist)){ && isWalkable(elem.children)){
p = path.concat([elem_id]) p = path.concat([elem_id])
sublist = elem.sublist children = elem.children
// normal element... // normal element...
} else { } else {
p = path.concat([elem_id]) p = path.concat([elem_id])
sublist = null children = null
doNested = null doNested = null
} }
return ( return (
// prepend nested elements on flat reverse... // prepend nested elements on flat reverse...
(sublist && reverse == 'flat' ? (children && reverse == 'flat' ?
doNested(sublist) doNested(children)
: []) : [])
// append the actual element... // append the actual element...
.concat( .concat(
@ -762,11 +761,11 @@ var BaseBrowserPrototype = {
// NOTE: inlined sets have no header... // NOTE: inlined sets have no header...
inline ? null : elem, inline ? null : elem,
doNested, doNested,
sublist)) children))
// append nested elements if not done so... // append nested elements if not done so...
.concat((!sublist || nested !== false) ? .concat((!children || nested !== false) ?
[] []
: doNested(sublist)) ) : doNested(children)) )
}) })
// XXX this here loses the length information we need in doNested(..) // XXX this here loses the length information we need in doNested(..)
// to calc indexes... // to calc indexes...
@ -787,39 +786,139 @@ var BaseBrowserPrototype = {
// - get a feeling of this running // - get a feeling of this running
// - see if we need to change the API // - see if we need to change the API
// - either embed into .walk(..) or reimplement... // - either embed into .walk(..) or reimplement...
walk2: function(func, i, path, options){ // XXX can this be simpler???
walk2: function(func, recursion, walkable, options){
var that = this
i = i || 0 // parse args...
path = path || [] var args = [...arguments]
func = args[0] instanceof Function ?
args.shift()
: undefined
var recursion = (args[0] instanceof Function
|| typeof(args[0]) == typeof('str')
|| args[0] == null) ?
args.shift()
: undefined
var walkable = (args[0] instanceof Function
|| args[0] == null) ?
args.shift()
: null
options = args.shift() || {}
// threaded args...
var i = args.shift() || 0
var path = args.shift() || []
// options specifics...
options = !options.root ?
Object.assign({},
this.options,
options,
// set call context...
{root: this})
: options
var iterateNonIterable = options.iterateAll || options.iterateNonIterable
var iterateCollapsed = options.iterateAll || options.iterateCollapsed
var skipNested = !options.iterateAll && options.skipNested
var reverse = options.reverse === true ?
(options.defaultReverse || 'tree')
: options.reverse
var isWalkable = walkable ?
function(node){
return node instanceof Array || walkable(node) }
: function(node){
return node && (node instanceof Array || node.walk2) }
return walk( return walk(
function(state, node, next, stop){ function(state, node, next, stop){
node.value // skip non-iterable items...
&& console.log('---', i++, path, node.value) if(!iterateNonIterable && node.noniterable){
return state
}
return ( var nested = false
var doNested = function(list){
// this can be called only once...
if(nested !== false){
return nested
}
// normalize...
list = (list === true || list == null) ?
children
: (!iterateCollapsed && node.collapsed) ?
[]
: list
return (list === false ?
[]
: list instanceof Array ?
next('do', state, ...(reverse ? list.slice().reverse() : list))
// user-defined recursion...
: recursion instanceof Function ?
recursion.call(that, func, i, p, list, options)
: list[recursion || 'walk2'](func, recursion, walkable, options, i, p))
.run(function(){
// normalize...
nested = this instanceof Array ?
this
: [this]
// merge recursion results into states...
if(!(list === false || list instanceof Array)){
state.splice(state.length, 0, ...nested)
i += nested.length
}
return nested
})
}
// prepare context...
var id = node.id || node.value
path = this.path = this.path || path
var [inline, p, children] =
// inline... // inline...
node instanceof Array ? isWalkable(node) ?
next('do', state, ...node) [true, path, node]
: node.walk2 instanceof Function ?
// XXX pass stop(..) to this...
state.concat(node.walk2(func, i, path, options))
// nested... // nested...
// XXX add node to state... : (!skipNested && isWalkable(node.children)) ?
: node.sublist instanceof Array ? [false,
// XXX need to update path... // prepare context for nested items...
next('do', state, ...node.sublist) this.path = path.concat([id]),
: node.sublist && node.sublist.walk2 instanceof Function ? node.children]
// XXX pass stop(..) to this... // leaf...
state.concat(node.sublist.walk2(func, i, path.concat([node.value]), options)) : [false, path.concat([id]), undefined]
: state) // reverse -> do children...
reverse == 'flat'
&& children
&& doNested()
// element...
state.splice(state.length, 0,
...[func.call(that,
...(inline ? [null, i] : [node, i++]),
p,
doNested,
stop,
children) || []])
// normal order -> do children...
children
&& (doNested(),
// restore path context...
this.path.pop())
return state
}, },
// XXX done(..) // normalize the root result...
//function(state){ console.log('done:', state) }, function(state){
return options.root === that ?
state.flat()
: state },
[], [],
...this.items) ...(reverse ?
this.items.slice().reverse()
: this.items))
}, },
@ -851,7 +950,7 @@ var BaseBrowserPrototype = {
var items = this var items = this
.walk( .walk(
function(i, path, item, nested, sublist){ function(i, path, item, nested, children){
var indent = base var indent = base
.concat(path) .concat(path)
.slice(1) .slice(1)
@ -860,8 +959,8 @@ var BaseBrowserPrototype = {
return item ? return item ?
indent + getValue(item) indent + getValue(item)
: [] }, : [] },
function(func, i, path, sublist, options){ function(func, i, path, children, options){
return sublist.text(context, base.concat(path)) }, return children.text(context, base.concat(path)) },
options) options)
return context.root === this ? return context.root === this ?
@ -950,11 +1049,11 @@ var BaseBrowserPrototype = {
// container to func??? // container to func???
: func.call(that, elem, i, path, that)] : func.call(that, elem, i, path, that)]
: [] }, : [] },
function(_, i, path, sublist, options){ function(_, i, path, children, options){
// NOTE: this needs to call the actual func that the user // NOTE: this needs to call the actual func that the user
// gave us and not the constructed function that we // gave us and not the constructed function that we
// pass to .walk(..) above... // pass to .walk(..) above...
return sublist.map(func, i, path, options) }, return children.map(func, i, path, options) },
...args, ...args,
options) options)
}, },
@ -1235,7 +1334,7 @@ var BaseBrowserPrototype = {
// //
// XXX NOTE: these will return a sparse array... ??? // XXX NOTE: these will return a sparse array... ???
sublists: function(func, options){ sublists: function(func, options){
return this.search({sublist: true}, func, options) }, return this.search({children: true}, func, options) },
// XXX broken, needs support for options.skipInlined ... // XXX broken, needs support for options.skipInlined ...
nested: function(func){ nested: function(func){
return this.sublists(func, {skipInlined: true}) }, return this.sublists(func, {skipInlined: true}) },
@ -1319,7 +1418,7 @@ var BaseBrowserPrototype = {
delete (cur.parent.item_key_index[cur.id] delete (cur.parent.item_key_index[cur.id]
|| cur.parent.items || cur.parent.items
.filter(function(e){ .filter(function(e){
return e.sublist === cur }) return e.children === cur })
.shift()).collapsed .shift()).collapsed
cur = cur.parent } cur = cur.parent }
@ -1533,8 +1632,8 @@ var BaseBrowserPrototype = {
}) })
// XXX do we need both this and the above ref??? // XXX do we need both this and the above ref???
item.sublist instanceof Browser item.children instanceof Browser
&& (item.sublist.parent = this) && (item.children.parent = this)
} }
// store the item... // store the item...
@ -1569,7 +1668,7 @@ var BaseBrowserPrototype = {
// //
// .renderFinalize(items, context) // .renderFinalize(items, context)
// .renderList(items, context) // .renderList(items, context)
// .renderNested(header, sublist, item, context) // .renderNested(header, children, item, context)
// .renderNestedHeader(item, i, context) // .renderNestedHeader(item, i, context)
// .renderItem(item, i, context) // .renderItem(item, i, context)
// .renderGroup(items, context) // .renderGroup(items, context)
@ -1580,16 +1679,16 @@ var BaseBrowserPrototype = {
renderList: function(items, context){ renderList: function(items, context){
return items }, return items },
// NOTE: to skip rendering an item/list return null... // NOTE: to skip rendering an item/list return null...
// XXX should this take an empty sublist??? // XXX should this take an empty children???
// ...this would make it simpler to expand/collapse without // ...this would make it simpler to expand/collapse without
// re-rendering the whole list... // re-rendering the whole list...
renderNested: function(header, sublist, item, context){ renderNested: function(header, children, item, context){
return header ? return header ?
this.renderGroup([ this.renderGroup([
header, header,
sublist, children,
]) ])
: sublist }, : children },
renderNestedHeader: function(item, i, context){ renderNestedHeader: function(item, i, context){
return this.renderItem(item, i, context) }, return this.renderItem(item, i, context) },
// NOTE: to skip rendering an item/list return null... // NOTE: to skip rendering an item/list return null...
@ -1632,6 +1731,7 @@ var BaseBrowserPrototype = {
// //
// XXX would be nice to add ability to do a full render but not // XXX would be nice to add ability to do a full render but not
// finalize the result... // finalize the result...
// XXX BUG: calling .render() removes all non-iterable items for some reason...
render: function(options, renderer){ render: function(options, renderer){
var that = this var that = this
// XXX Q: should options and context be distinguished only via // XXX Q: should options and context be distinguished only via
@ -1648,9 +1748,7 @@ var BaseBrowserPrototype = {
Object.create(this.options || {}), Object.create(this.options || {}),
// defaults... // defaults...
// XXX is this the correct way to setup defaults??? // XXX is this the correct way to setup defaults???
{ { iterateNonIterable: true },
iterateNonIterable: true,
},
options || {}), options || {}),
} }
: options : options
@ -1664,10 +1762,10 @@ var BaseBrowserPrototype = {
// - arg threading // - arg threading
var items = this var items = this
.walk( .walk(
function(i, path, item, nested, sublist){ function(i, path, item, nested, children){
return ( return (
// inline... // inline...
(item == null && sublist) ? (item == null && children) ?
// NOTE: here we are forcing rendering of the // NOTE: here we are forcing rendering of the
// inline browser/list, i.e. ignoring // inline browser/list, i.e. ignoring
// options.skipNested for inline stuff... // options.skipNested for inline stuff...
@ -1675,7 +1773,7 @@ var BaseBrowserPrototype = {
// inlined lists and browsers... (XXX ???) // inlined lists and browsers... (XXX ???)
[ renderer.renderGroup(nested(true), context) ] [ renderer.renderGroup(nested(true), context) ]
// nested... // nested...
: sublist ? : children ?
[ renderer.renderNested( [ renderer.renderNested(
renderer.renderNestedHeader(item, i, context), renderer.renderNestedHeader(item, i, context),
nested(), nested(),
@ -1683,8 +1781,8 @@ var BaseBrowserPrototype = {
context) ] context) ]
// normal item... // normal item...
: [ renderer.renderItem(item, i, context) ] ) }, : [ renderer.renderItem(item, i, context) ] ) },
function(func, i, path, sublist, options){ function(func, i, path, children, options){
return sublist.render(context, renderer, i, path) }, return children.render(context, renderer, i, path) },
// make the element render less strict... // make the element render less strict...
function(elem){ function(elem){
return elem return elem
@ -2071,12 +2169,12 @@ var BrowserPrototype = {
// <!-- header (optional) --> // <!-- header (optional) -->
// ... // ...
// //
// <!-- sublist (optional) --> // <!-- children (optional) -->
// ... // ...
// </div> // </div>
// //
// XXX register event handlers... // XXX register event handlers...
renderNested: function(header, sublist, item, context){ renderNested: function(header, children, item, context){
var that = this var that = this
var options = context.options || this.options var options = context.options || this.options
@ -2095,11 +2193,11 @@ var BrowserPrototype = {
&& e.appendChild(header) && e.appendChild(header)
// items... // items...
sublist instanceof Node ? children instanceof Node ?
e.appendChild(sublist) e.appendChild(children)
// XXX should this add the items to a container??? // XXX should this add the items to a container???
: sublist instanceof Array ? : children instanceof Array ?
sublist children
.forEach(function(item){ .forEach(function(item){
e.appendChild(item) }) e.appendChild(item) })
: null : null
@ -2341,10 +2439,10 @@ var TextBrowserPrototype = {
return item.current ? return item.current ?
`[ ${value} ]` `[ ${value} ]`
: value }, : value },
renderNested: function(header, sublist, context, item, options){ renderNested: function(header, children, context, item, options){
var that = this var that = this
var nested = sublist var nested = children
&& sublist && children
.flat() .flat()
.map(function(e){ .map(function(e){
return e instanceof Array ? return e instanceof Array ?