refactored .walk(..) order and index manipulation + fixes and docs...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-04-25 17:40:28 +03:00
parent 0a5e6617c3
commit 1deb1e939f

View File

@ -485,32 +485,55 @@ var BaseBrowserPrototype = {
+ Date.now() }, + Date.now() },
// Walk the browser...
// //
// .walk(handleItem[, options]) // .walk(handleItem[, options])
// -> result // -> result
// //
// .walk(handleItem, handleNested[, options]) // .walk(handleItem, handleRecursion[, options])
// -> result // -> result
// //
// .walk(handleItem, handleNested, isWalkable[, options]) // .walk(handleItem, handleRecursion, isWalkable[, options])
// -> result // -> result
// //
// //
//
// handleItem(path, elem, index, doNested, sublist) // handleItem(path, elem, index, doNested, sublist)
// -> array // -> items
// //
// Trigger nested item handling...
// doNested([options])
// doNested(list[, options]) // doNested(list[, options])
// doNested(index[, options])
// doNested(list, index[, options])
// -> items
//
// Disable automatic nested item handling...
// doNested(false)
// -> undefined
//
// Force nested item handling...
// doNested(true, ..)
// -> items
//
// NOTE: doNested(..) has no effect of options.reverseIteration is
// set to 'flat'...
// NOTE: only the first call to doNested(..) as any effect, all
// consecutive calls will return cached results of the first
// call...
//
//
//
// Handle recursion down...
// handleRecursion(func, index, path, sublist, options)
// -> items // -> items
// //
// //
// XXX
// handleNested(..)
// ->
// //
// Test if item is walkable...
// isWalkable(item)
// -> bool
// //
// Request manual iteration...
// doNested(false)
// -> undefined
// //
// //
// options format: // options format:
@ -529,11 +552,19 @@ var BaseBrowserPrototype = {
// // If true skip iterating nested items... // // If true skip iterating nested items...
// skipNested: <bool>, // skipNested: <bool>,
// //
// // If true, reverse iteration order... // // Reverse iteration order...
// // NOTE: containing items will still precede the contained, // //
// // i.e. this will reverse the level order but not // // modes:
// // nesting order... // // false | null - normal order (default)
// reverseIteration: <bool>, // // true - reverse order of levels but keep
// // topology order, i.e. containers
// // will precede contained elements.
// // 'flat' - full flat reverse
// //
// // NOTE: in 'flat' mode the client loses control over the
// // order of processing via doNested(..) as it will be
// // called before handleItem(..)
// reverseIteration: <bool> | 'flat',
// //
// // If true include inlined parent id in path... // // If true include inlined parent id in path...
// // XXX not implemented yet -- can we implement this???... // // XXX not implemented yet -- can we implement this???...
@ -542,9 +573,7 @@ var BaseBrowserPrototype = {
// } // }
// //
// //
// XXX add 'flat' reverseIteration mode...
// XXX revise protocol... // XXX revise protocol...
// XXX make sublist test customizable...
walk: function(func, options){ walk: function(func, options){
var that = this var that = this
@ -557,11 +586,9 @@ var BaseBrowserPrototype = {
|| typeof(args[0]) == typeof('str')) ? || typeof(args[0]) == typeof('str')) ?
args.shift() args.shift()
: undefined : undefined
var isWalkable = args[0] instanceof Function ? var userIsWalkable = args[0] instanceof Function ?
args.shift() args.shift()
// XXX revise... : null
: function(elem){
return elem instanceof Browser }
var i = typeof(args[0]) == typeof(123) ? var i = typeof(args[0]) == typeof(123) ?
args.shift() args.shift()
: 0 : 0
@ -579,6 +606,14 @@ var BaseBrowserPrototype = {
var skipNested = !options.iterateAll && options.skipNested var skipNested = !options.iterateAll && options.skipNested
var reverse = options.reverseIteration var reverse = options.reverseIteration
var isWalkable = userIsWalkable ?
function(elem){
return elem instanceof Array
|| userIsWalkable(elem) }
: function(elem){
return elem instanceof Array
|| elem instanceof Browser }
// level walk function... // level walk function...
var walk = function(i, path, list){ var walk = function(i, path, list){
return list return list
@ -601,13 +636,33 @@ var BaseBrowserPrototype = {
var p var p
// nested browser/list handler... // nested browser/list handler...
var nested_called = false var nested = false
var doNested = function(list, opts){ var doNested = function(list, j, opts){
list = (!iterateCollapsed && elem.collapsed) ? // this can be called only once...
if(nested !== false){
return nested
}
// parse args...
var args = [...arguments]
list = (args[0] === true
|| args[0] === false
|| isWalkable(args[0])) ?
args.shift()
: undefined
j = typeof(args[0]) == typeof(123) ?
args.shift()
: undefined
opts = args.shift() || options
// normalize list...
list = list === true ?
sublist
:(!iterateCollapsed && elem.collapsed) ?
[] []
: (list || sublist) : (list || sublist)
list = list === true ? sublist : list // adjust index...
nested_called = true i = j != null ? j : i
return ( return (
// request manual iteration... // request manual iteration...
@ -617,41 +672,59 @@ var BaseBrowserPrototype = {
walk(i, p, list) walk(i, p, list)
// user-defined recursion... // user-defined recursion...
: recursion instanceof Function ? : recursion instanceof Function ?
recursion.call(that, func, i, p, list, opts || options) recursion.call(that, func, i, p, list, opts)
: list[recursion || 'walk'](func, i, p, opts || options)) : list[recursion || 'walk'](func, i, p, opts))
.run(function(){ .run(function(){
var res = this instanceof Array ? var res = this instanceof Array ?
this this
: [this] : [this]
i += this.length i += this.length
nested = res
return res return res
})} })
}
// setup some context...
var inline = false
// inline browser or array...
if(isWalkable(elem)){
inline = true
p = path
sublist = elem
// nested browser / array...
} else if(!skipNested
&& isWalkable(elem.sublist)){
p = path.concat([elem_id])
sublist = elem.sublist
// normal element...
} else {
p = path.concat([elem_id])
sublist = null
doNested = null
}
return ( return (
// inline browser or array... // prepend nested elements on flat reverse...
(elem instanceof Array (sublist && options.reverseIteration == 'flat' ?
|| isWalkable(elem)) ? doNested(sublist)
: [])
// append the actual element...
.concat(
func.call(that, func.call(that,
i, p = path, // NOTE: for inline elements we do not need to
null, doNested, // count the header...
sublist = elem) inline ? i : i++,
// nested browser / array... p,
: (!skipNested // NOTE: inlined sets have no header...
&& (elem.sublist instanceof Array inline ? null : elem,
|| isWalkable(elem.sublist))) ? doNested,
func.call(that, sublist))
i++, p = path.concat([elem_id]), // append nested elements if not done so...
elem, doNested, .concat((!sublist || nested !== false) ?
sublist = elem.sublist)
// normal element...
: func.call(that,
i++, p = path.concat([elem_id]),
elem, null,
sublist = null) )
// append nested elements...
.concat((!sublist || nested_called) ?
[] []
: doNested(sublist)) : doNested(sublist)) )
}) })
.flat() } .flat() }
@ -664,11 +737,8 @@ var BaseBrowserPrototype = {
// This is mainly here for doc/debug purposes... // This is mainly here for doc/debug purposes...
// //
// XXX rename this?? // XXX rename this??
text: function(options, renderer){ text: function(options, base){
var that = this var that = this
// XXX Q: should options and context be distinguished only via
// the .options attr as is the case now???
// ...see no reason why not, though it does not feel right...
var context = (options == null || options.options == null) ? var context = (options == null || options.options == null) ?
{ {
root: this, root: this,
@ -678,15 +748,12 @@ var BaseBrowserPrototype = {
options: Object.assign( options: Object.assign(
Object.create(this.options || {}), Object.create(this.options || {}),
// defaults... // defaults...
// XXX is this the correct way to setup defaults??? { iterateNonIterable: true },
{
iterateNonIterable: true,
},
options || {}), options || {}),
} }
: options : options
options = context.options options = context.options
renderer = renderer || this base = base || []
var getValue = function(item){ var getValue = function(item){
return item.value || item } return item.value || item }
@ -694,28 +761,16 @@ var BaseBrowserPrototype = {
var items = this var items = this
.walk( .walk(
function(i, path, item, nested, sublist){ function(i, path, item, nested, sublist){
var indent = path.map(e => ' ').join('') var indent = base
return ( .concat(path)
// inline... .slice(1)
(item == null && sublist) ? .map(e => ' ')
// NOTE: here we are forcing rendering of the .join('')
// inline browser/list, i.e. ignoring return item ?
// options.skipNested for inline stuff... indent + getValue(item)
nested(true) : [] },
.map(function(e){
return indent + getValue(e) })
// nested...
: sublist ?
[item.value]
.concat(
nested()
.map(function(e){
return indent + getValue(e) }))
// single item...
: [getValue(item)]
) },
function(func, i, path, sublist, options){ function(func, i, path, sublist, options){
return sublist.text(context) }, return sublist.text(context, base.concat(path)) },
options) options)
return context.root === this ? return context.root === this ?
@ -781,7 +836,7 @@ var BaseBrowserPrototype = {
var options = args.pop() || {} var options = args.pop() || {}
return this.walk( return this.walk(
function(i, path, elem){ function(i, path, elem, doNested){
return elem != null ? return elem != null ?
[func === undefined ? [func === undefined ?
elem elem
@ -931,8 +986,9 @@ var BaseBrowserPrototype = {
// XXX do we need to support negative indexes??? // XXX do we need to support negative indexes???
var Stop = new Error('.get(..): Result found exception.') var Stop = new Error('.get(..): Result found exception.')
var i = 0 var i = 0
// reverse indexing...
options = Object.assign( options = Object.assign(
{reverseIteration: key < 0}, {reverseIteration: key < 0 && 'flat'},
options || {}) options || {})
key = key < 0 ? key = key < 0 ?
-key - 1 -key - 1