more work on .walk2(..) and friends...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-08-25 00:24:09 +03:00
parent 7a56564835
commit c045d67a89

View File

@ -724,6 +724,17 @@ var BrowserViewMixin = {
var viewWrap =
function(context, lst, options){
return context.view(
'as-is',
lst,
{
__proto__: context.options || {},
skipNested: 'skipNested' in (options || {}) ?
options.skipNested
: true,
}) }
// //
// options format: // options format:
// { // {
@ -745,15 +756,16 @@ function(options){
false false
: (options.wrapper : (options.wrapper
|| function(res){ || function(res){
return this.view( return viewWrap(this, res, options) }) }
'as-is',
res,
{ var makeFlatRunViewWrapper =
__proto__: this.options || {}, function(context, options){
skipNested: 'skipNested' in (options || {}) ? return function(){
options.skipNested return (options || {}).rawResults === true ?
: true, this
}) }) } : viewWrap(context, this, options) } }
@ -1920,33 +1932,82 @@ var BaseBrowserPrototype = {
// XXX EXPERIMENTAL -- an attempt to simplify walking... // XXX EXPERIMENTAL -- an attempt to simplify walking...
// //
// .walk(func, options) // .walk(func[, options])
// -> res??? // -> list
// -> res
//
// //
// func(elem, i, path, next(..), stop(..)) // func(elem, i, path, next(..), stop(..))
// -> res??? // -> [item, ..]
// -> item
// //
// //
// NOTE: when options.reverse is set to true, func(..) for a parent // Ignore current .children...
// item is still called BEFORE it is called for the children but // next()
// its return value is placed after, i.e. the func(..) call order //
// is different to the result order. // Explicitly pass the children to handle...
// next(browser)
// next([elem, ...])
//
//
// Stop walking (returning undefined)...
// stop()
//
// Stop walking and return res...
// stop(res)
//
//
// If func(..) returns an array it's content is merged (.flat()) into
// .walk(..)'s return value, this enables it to:
// - return more than one value per item by returning an array of values
// - return no values for an item by returning []
//
// NOTE: to explicitly return an array wrap it in another array.
//
// //
// //
// options format: // options format:
// { // {
// // reverse walking...
// //
// // modes:
// // true - use .defaultReverse
// // 'mixed' - results reversed,
// // handlers called topologically
// // (i.e. container handled before children
// // but its return value is placed after)
// // 'full' - results reversed
// // (i.e. container handled after children)
// // 'tree' - results reversed topologically
// // (i.e. container handled after children)
// //
// // NOTE: in 'full' mode, next(..) has no effect, as when the
// // container handler is called the children have already
// // been processed...
// reverse: <bool> | 'mixed' | 'full' | 'tree',
// //
// reverse: <bool> | 'full', // defaultReverse: 'mixed',
// //
// iterateNonIterable: <bool>, // // if true iterate collapsed children...
// iterateCollapsed: <bool>, // iterateCollapsed: <bool>,
// //
// // if true iterate non-iterable elements...
// iterateNonIterable: <bool>,
//
//
// // shorthand for: // // shorthand for:
// // iterateCollapsed: true, iterateNonIterable: true // // iterateCollapsed: true, iterateNonIterable: true
// iterateAll: <bool>, // iterateAll: <bool>,
// //
// // XXX ??? // // if true call func(..) on inline block containers...
// //includeInlinedBlocks: <bool>, // includeInlinedBlocks: <bool>,
//
// skipDisabledMode: 'node' | 'branch',
// skipDisabled: <bool> | 'node' | 'branch',
//
// // skip nested/inlined elements (children)...
// skipNested: <bool>,
// skipInlined: <bool>,
// //
// //
// // list of sections to iterate... // // list of sections to iterate...
@ -1961,6 +2022,8 @@ var BaseBrowserPrototype = {
// } // }
// //
// //
// XXX should we implement next(true) to synchronously process the
// children and return the result to the caller???
walk2: function(func, options){ walk2: function(func, options){
var that = this var that = this
var [func, options={}, path=[], context={}] = [...arguments] var [func, options={}, path=[], context={}] = [...arguments]
@ -1974,8 +2037,11 @@ var BaseBrowserPrototype = {
Object.create(this.options || {}), Object.create(this.options || {}),
options) options)
// options.reverse... // options.reverse...
var reverse = options.reverse === true ?
(options.defaultReverse || 'mixed')
: options.reverse
var handleReverse = function(lst){ var handleReverse = function(lst){
return options.reverse ? return reverse ?
lst.slice().reverse() lst.slice().reverse()
: lst } : lst }
// options.section... // options.section...
@ -1991,8 +2057,10 @@ var BaseBrowserPrototype = {
// iteration filtering... // iteration filtering...
var iterateNonIterable = !!(options.iterateAll || options.iterateNonIterable) var iterateNonIterable = !!(options.iterateAll || options.iterateNonIterable)
var iterateCollapsed = !!(options.iterateAll || options.iterateCollapsed) var iterateCollapsed = !!(options.iterateAll || options.iterateCollapsed)
// XXX var includeInlinedBlocks = !!options.includeInlinedBlocks
//var includeInlinedBlocks = !!options.includeInlinedBlocks var skipDisabled = options.skipDisabled === true ?
options.skipDisabledMode || 'node'
: options.skipDisabled
// stopping mechanics... // stopping mechanics...
var res, StopException var res, StopException
@ -2009,45 +2077,50 @@ var BaseBrowserPrototype = {
return function(elem){ return function(elem){
var p = path var p = path
var children = // item...
// skip collapsed... var skipItem =
(!iterateCollapsed && elem.collapsed) ? (skipDisabled && elem.disabled)
|| (!iterateNonIterable && elem.noniterable)
|| (!includeInlinedBlocks
&& (elem instanceof Array || elem instanceof BaseBrowser))
var p = !skipItem ?
path.concat(elem.id)
: p
var item
// NOTE: this will handle the item once and then re-return its value...
var getItem = function(){
return (item =
item !== undefined ?
item
: !skipItem ?
[ func.call(that, elem, context.index++, p, next, stop) ].flat()
: []) }
// pre-call the item if reverse is not 'full'...
reverse == 'full'
|| getItem()
// children...
var children = (
// skip...
((!iterateCollapsed && elem.collapsed)
|| (skipDisabled == 'branch')) ?
[] []
// inlined... // inlined...
: (elem instanceof BaseBrowser : !options.skipInlined
|| elem instanceof Array) ? && (elem instanceof BaseBrowser || elem instanceof Array) ?
elem elem
// nested... // nested...
: (elem.children || []) : (!options.skipNested && elem.children) )
|| []
var next = function(elems){ var next = function(elems){
children = elems == null ? children = elems == null ?
[] []
: elems } : elems }
// handle item... // build the result...
// skip non-iterable and inlined block items...
var handleItem = !((!iterateNonIterable && elem.noniterable)
|| (elem instanceof Array || elem instanceof BaseBrowser))
var p = handleItem ?
path.concat(elem.id)
: p
var item
// NOTE: this will handle the item once and then re-return the value...
var getItem = function(){
return (item =
item !== undefined ?
item
: handleItem ?
[ func.call(that, elem, context.index++, p, next, stop) ].flat()
: []) }
// pre-call the item if options.reverse is not set to 'full'...
options.reverse == 'full'
|| getItem()
return [ return [
// item... // item (normal order)...
...!options.reverse ? ...!(reverse && reverse != 'tree') ?
getItem() getItem()
: [], : [],
// children... // children...
@ -2060,12 +2133,10 @@ var BaseBrowserPrototype = {
.walk2(func, options, p, context) .walk2(func, options, p, context)
: [], : [],
// item (in reverse)... // item (in reverse)...
...options.reverse ? ...(reverse && reverse != 'tree') ?
getItem() getItem()
: [], ] } } : [], ] } }
// do the handling...
try { try {
return handleReverse( return handleReverse(
sections sections
@ -2075,12 +2146,64 @@ var BaseBrowserPrototype = {
.map(makeMap(path)) .map(makeMap(path))
.flat() .flat()
// handle StopException and errors... // handle stop(..) and propagate errors...
} catch(e){ } catch(e){
if(e === StopException){ if(e === StopException){
return res } return res }
throw e } }, throw e } },
// basic iteration...
map2: function(func, options){
var that = this
options = !(options || {}).defaultReverse ?
Object.assign({},
options || {},
{ defaultReverse: 'full' })
: options
return this.walk2(
function(e, i, p){
return [func ?
func.call(that, e, i, p)
: e] },
options)
.run(makeFlatRunViewWrapper(this, options)) },
filter2: function(func, options){
var that = this
options = !(options || {}).defaultReverse ?
Object.assign({},
options || {},
{ defaultReverse: 'full' })
: options
return this.walk2(
function(e, i, p){
return func.call(that, e, i, p) ? [e] : [] },
options)
.run(makeFlatRunViewWrapper(this, options)) },
reduce2: function(func, start, options){
var that = this
options = !(options || {}).defaultReverse ?
Object.assign({},
options || {},
{ defaultReverse: 'full' })
: options
this.walk2(
function(e, i, p){
start = func.call(that, start, e, i, p) },
options)
return start },
forEach2: function(func, options){
this.map2(...arguments)
return this },
toArray2: function(options){
return this.map2(null,
Object.assign({},
options || {},
{rawResults: true})) },
// XXX search(..), get(..), ...
// Data access and iteration... // Data access and iteration...
@ -2142,9 +2265,9 @@ var BaseBrowserPrototype = {
// // Partial walking... // // Partial walking...
// // // //
// // XXX not implemented yet... // // XXX not implemented yet...
// start: <index> | <path>, // //start: <index> | <path>,
// count: <number>, // //count: <number>,
// end: <index> | <path>, // //end: <index> | <path>,
// //
// //
// // Iterate ALL items... // // Iterate ALL items...