From 98412ab28cea7e0ecaf0fbc08386b48752222a22 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Wed, 24 Apr 2019 16:10:26 +0300 Subject: [PATCH] moved things to their logical locations... Signed-off-by: Alex A. Naanou --- ui (gen4)/lib/widget/browse2.js | 846 +++++++++++++++++--------------- 1 file changed, 437 insertions(+), 409 deletions(-) diff --git a/ui (gen4)/lib/widget/browse2.js b/ui (gen4)/lib/widget/browse2.js index de296ba8..d62bad7c 100755 --- a/ui (gen4)/lib/widget/browse2.js +++ b/ui (gen4)/lib/widget/browse2.js @@ -482,6 +482,359 @@ var BaseBrowserPrototype = { + Date.now() }, + // + // .walk(item_handler[, options]) + // -> result + // + // .walk(item_handler, nested_handler[, options]) + // -> result + // + // + // item_handler(path, elem, index, nested, sublist) + // -> array + // + // nested(list[, options]) + // -> items + // + // + // XXX + // nested_handler(..) + // -> + // + // + // Request manual iteration... + // nested(false) + // -> undefined + // + // + // options format: + // { + // // Iterate ALL items... + // // + // // NOTE: this if true overrides all other iteration coverage + // // options... + // iterateAll: , + // + // // If true do not skip items with .noniterable set to true... + // iterateNonIterable: , + // // If true do not skip item.sublist of items with .collapsed + // // set to true... + // iterateCollapsed: , + // // If true skip iterating nested items... + // skipNested: , + // + // // If true, reverse iteration order... + // // NOTE: containing items will still precede the contained, + // // i.e. this will reverse the level order but not + // // nesting order... + // reverseIteration: , + // + // // If true include inlined parent id in path... + // // XXX not implemented yet -- can we implement this???... + // inlinedPaths: , + // } + // + // + // XXX make sublist test customizable... + walk: function(func, options){ + var that = this + + // parse args... + var args = [...arguments] + func = args[0] instanceof Function ? + args.shift() + : undefined + var recursion = (args[0] instanceof Function + || typeof(args[0]) == typeof('str')) ? + args.shift() + : undefined + var i = typeof(args[0]) == typeof(123) ? + args.shift() + : 0 + var path = (args[0] instanceof Array + || typeof(args[0]) == typeof('str')) ? + args.shift() + : [] + path = path instanceof Array ? path : [path] + var options = args.pop() || {} + + // options... + options = Object.assign(Object.create(this.options || {}), options || {}) + var iterateNonIterable = options.iterateAll || options.iterateNonIterable + var iterateCollapsed = options.iterateAll || options.iterateCollapsed + var skipNested = !options.iterateAll && options.skipNested + var reverse = !!options.reverseIteration + + // level walk function... + var walk = function(i, path, list){ + return list + // reverse the items... + .run(function(){ + return reverse ? + // NOTE: we .slice() as we do not want to affect + // the actual list... + this.slice().reverse() + : this }) + .map(function(elem){ + // skip non-iterable items... + if(!iterateNonIterable && elem.noniterable){ + return [] + } + + var elem_id = elem.id || elem.value + // these will be set in the return expression below... + var sublist + var p + + // nested browser/list handler... + var nested_called = false + var nested = function(list, opts){ + var skip = skipNested && !list + list = (!iterateCollapsed && elem.collapsed) ? + [] + : (list || sublist) + list = list === true ? sublist : list + nested_called = true + + return ( + // request manual iteration... + (skip || list === false) ? + [] + :list instanceof Array ? + walk(i, p, list) + // user-defined recursion... + : recursion instanceof Function ? + recursion.call(that, func, i, p, list, opts || options) + : list[recursion || 'walk'](func, i, p, opts || options)) + .run(function(){ + var res = this instanceof Array ? + this + : [this] + i += this.length + return res + })} + + return ( + // inline browser or array... + (elem instanceof Array + || elem instanceof Browser) ? + func.call(that, + i, p = path, + null, nested, + sublist = elem) + // nested browser / array... + : (elem.sublist instanceof Browser + || elem.sublist instanceof Array) ? + func.call(that, + i++, p = path.concat([elem_id]), + elem, nested, + 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) ? + [] + : nested(sublist)) + }) + .flat() } + + return walk(i, path, this.items) + }, + + + // Text render... + // + // This is mainly here for doc/debug purposes... + // + // XXX rename this?? + text: function(options, renderer){ + 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) ? + { + root: this, + // NOTE: we are not combining this with .options as nested + // lists can have their own unique sets of options + // independently of the root list... + options: Object.assign( + Object.create(this.options || {}), + // defaults... + // XXX is this the correct way to setup defaults??? + { + iterateNonIterable: true, + }, + options || {}), + } + : options + options = context.options + renderer = renderer || this + + var getValue = function(item){ + return item.value || item } + + var items = this + .walk( + function(i, path, item, nested, sublist){ + var indent = path.map(e => ' ').join('') + return ( + // inline... + (item == null && sublist) ? + // NOTE: here we are forcing rendering of the + // inline browser/list, i.e. ignoring + // options.skipNested for inline stuff... + 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){ + return sublist.text(context) }, + options) + + return context.root === this ? + items.join('\n') + : items + }, + + + // Extended map... + // + // Get all items... + // .map([options]) + // -> items + // + // Map func to items... + // .map(func[, options]) + // -> items + // + // + // + // func(item, index, path, browser) + // -> result + // + // + // + // For supported options see docs for .walk(..) + // + // By default this will not iterate items that are: + // - non-iterable (item.noniterable is true) + // - collapsed sub-items (item.collapsed is true) + // + // This extends the Array .map(..) by adding: + // - ability to run without arguments + // - support for options + // + // + // XXX make item access by index lazy... + // - index nested stuff and lengths... (.sublist_length) + // - stop when target reached... (control callback???) + // XXX Q: should we have an option to treat groups as elements??? + map: function(func, options){ + var that = this + + // parse args... + // + // NOTE: in addition to the user signatures documented above this + // also supports two variants used internally: + // .map(func, path[, options]) + // .map(func, i, path[, options]) + // these set the "base" path and index passed to func... + // XXX can we avoid argument parsing here??? + var args = [...arguments] + func = args[0] instanceof Function ? + args.shift() + : undefined + var i = typeof(args[0]) == typeof(123) ? + args.shift() + : 0 + var path = (args[0] instanceof Array + || typeof(args[0]) == typeof('str')) ? + args.shift() + : [] + var options = args.pop() || {} + + return this.walk( + function(i, path, elem){ + return elem != null ? + [func === undefined ? + elem + // XXX should this pass the current or the root + // container to func??? + : func.call(that, elem, i, path, that)] + : [] }, + function(_, i, path, sublist, options){ + // NOTE: this needs to call the actual func that the user + // gave us and not the constructed function that we + // pass to .walk(..) above... + return sublist.map(func, i, path, options) }, + i, + path, + options) + }, + + + // Sublist map functions... + // NOTE: there are different from .map(..) in that instead of paths + // func(..) will get indexes in the current browser... + // NOTE: these will return a sparse array... + sublists: function(func, options){ + var that = this + //options = options || {} + options = Object.assign(Object.create(this.options || {}), options || {}) + var skipNested = options.skipNested + var skipInlined = options.skipInlined + + var res = [] + this.items + .forEach(function(elem, i){ + if((!skipInlined && elem.value instanceof Browser) + || (!skipNested && elem.sublist)){ + res[i] = func ? + func.call(that, elem, i, that) + : elem + } }) + return res + }, + nested: function(func){ + return this.sublists(func, {skipInlined: true}) }, + inlined: function(func){ + return this.sublists(func, {skipNested: true}) }, + + next: function(){}, + prev: function(){}, + + // XXX should there return an array or a .constructor(..) instance?? + // XXX should these call respective methods (.forEach(..), .filter(..), + // .reduce(..)) on the nested browsers??? + forEach: function(func, options){ + this.map(...arguments) + return this }, + filter: function(func, options){ + return this.map(function(e, i, p, b){ + return func.call(this, e, i, p, b) ? [e] : [] }) + .flat() }, + reduce: function(){}, + + + // XXX do we need edit ability here? + // i.e. .set(..), .remove(..), .sort(..), ... + // ...if we are going to implement editing then we'll need to + // callback the user code or update the user state... + + // Make .items and .item_key_index... // @@ -766,6 +1119,90 @@ var BaseBrowserPrototype = { }, //*/ + + // Render state... + // + // .render() + // .render(options) + // .render(context) + // -> state + // + // + // context format: + // { + // root: , + // options: , + // } + // + // + // NOTE: currently options and context are distinguished only via + // the .options attribute... + // + // XXX test sublist via .render rather than instanceof... + render: function(options, renderer){ + 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) ? + { + root: this, + // NOTE: we are not combining this with .options as nested + // lists can have their own unique sets of options + // independently of the root list... + //options: options || this.options || {}, + options: Object.assign( + Object.create(this.options || {}), + // defaults... + // XXX is this the correct way to setup defaults??? + { + iterateNonIterable: true, + }, + options || {}), + } + : options + options = context.options + renderer = renderer || this + + // XXX should we control render parameters (range, start, end, ...) + // from outside render and pass this info down to nested lists??? + // ...if yes how?? + // - options + // - arg threading + var items = this + .walk( + function(i, path, item, nested, sublist){ + return ( + // inline... + (item == null && sublist) ? + // NOTE: here we are forcing rendering of the + // inline browser/list, i.e. ignoring + // options.skipNested for inline stuff... + // NOTE: we here do not distinguish between + // inlined lists and browsers... (XXX ???) + [ renderer.renderGroup(nested(true), context) ] + // nested... + : sublist ? + [ renderer.renderNested( + renderer.renderNestedHeader(item, i, context), + nested(), + item, + context) ] + // normal item... + : [ renderer.renderItem(item, i, context) ] ) }, + function(func, i, path, sublist, options){ + return sublist.render(context, renderer) }, + options) + + // determine the render mode... + return context.root === this ? + // root context -> render list and return this... + renderer.renderList(items, context) + // nested context -> return item list... + : items + }, + + // Update state (make then render)... // // .update() @@ -1270,415 +1707,6 @@ var BaseBrowserPrototype = { - // - // .walk(item_handler[, options]) - // -> result - // - // .walk(item_handler, nested_handler[, options]) - // -> result - // - // - // item_handler(path, elem, index, nested, sublist) - // -> array - // - // nested(list[, options]) - // -> items - // - // - // XXX - // nested_handler(..) - // -> - // - // - // Request manual iteration... - // nested(false) - // -> undefined - // - // - // options format: - // { - // // Iterate ALL items... - // // - // // NOTE: this if true overrides all other iteration coverage - // // options... - // iterateAll: , - // - // // If true do not skip items with .noniterable set to true... - // iterateNonIterable: , - // // If true do not skip item.sublist of items with .collapsed - // // set to true... - // iterateCollapsed: , - // // If true skip iterating nested items... - // skipNested: , - // - // // If true, reverse iteration order... - // // NOTE: containing items will still precede the contained, - // // i.e. this will reverse the level order but not - // // nesting order... - // reverseIteration: , - // - // // If true include inlined parent id in path... - // // XXX not implemented yet -- can we implement this???... - // inlinedPaths: , - // } - // - // - // XXX make sublist test customizable... - walk: function(func, options){ - var that = this - - // parse args... - var args = [...arguments] - func = args[0] instanceof Function ? - args.shift() - : undefined - var recursion = (args[0] instanceof Function - || typeof(args[0]) == typeof('str')) ? - args.shift() - : undefined - var i = typeof(args[0]) == typeof(123) ? - args.shift() - : 0 - var path = (args[0] instanceof Array - || typeof(args[0]) == typeof('str')) ? - args.shift() - : [] - path = path instanceof Array ? path : [path] - var options = args.pop() || {} - - // options... - options = Object.assign(Object.create(this.options || {}), options || {}) - var iterateNonIterable = options.iterateAll || options.iterateNonIterable - var iterateCollapsed = options.iterateAll || options.iterateCollapsed - var skipNested = !options.iterateAll && options.skipNested - var reverse = !!options.reverseIteration - - // level walk function... - var walk = function(i, path, list){ - return list - // reverse the items... - .run(function(){ - return reverse ? - // NOTE: we .slice() as we do not want to affect - // the actual list... - this.slice().reverse() - : this }) - .map(function(elem){ - // skip non-iterable items... - if(!iterateNonIterable && elem.noniterable){ - return [] - } - - var elem_id = elem.id || elem.value - // these will be set in the return expression below... - var sublist - var p - - // nested browser/list handler... - var nested_called = false - var nested = function(list, opts){ - var skip = skipNested && !list - list = (!iterateCollapsed && elem.collapsed) ? - [] - : (list || sublist) - list = list === true ? sublist : list - nested_called = true - - return ( - // request manual iteration... - (skip || list === false) ? - [] - :list instanceof Array ? - walk(i, p, list) - // user-defined recursion... - : recursion instanceof Function ? - recursion.call(that, func, i, p, list, opts || options) - : list[recursion || 'walk'](func, i, p, opts || options)) - .run(function(){ - var res = this instanceof Array ? - this - : [this] - i += this.length - return res - })} - - return ( - // inline browser or array... - (elem instanceof Array - || elem instanceof Browser) ? - func.call(that, - i, p = path, - null, nested, - sublist = elem) - // nested browser / array... - : (elem.sublist instanceof Browser - || elem.sublist instanceof Array) ? - func.call(that, - i++, p = path.concat([elem_id]), - elem, nested, - 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) ? - [] - : nested(sublist)) - }) - .flat() } - - return walk(i, path, this.items) - }, - - // XXX rename this?? - text: function(options, renderer){ - 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) ? - { - root: this, - // NOTE: we are not combining this with .options as nested - // lists can have their own unique sets of options - // independently of the root list... - options: Object.assign( - Object.create(this.options || {}), - // defaults... - // XXX is this the correct way to setup defaults??? - { - iterateNonIterable: true, - }, - options || {}), - } - : options - options = context.options - renderer = renderer || this - - var getValue = function(item){ - return item.value || item } - - var items = this - .walk( - function(i, path, item, nested, sublist){ - var indent = path.map(e => ' ').join('') - return ( - // inline... - (item == null && sublist) ? - // NOTE: here we are forcing rendering of the - // inline browser/list, i.e. ignoring - // options.skipNested for inline stuff... - 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){ - return sublist.text(context) }, - options) - - return context.root === this ? - items.join('\n') - : items - }, - - // XXX test sublist via .render rather than instanceof... - render: function(options, renderer){ - 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) ? - { - root: this, - // NOTE: we are not combining this with .options as nested - // lists can have their own unique sets of options - // independently of the root list... - //options: options || this.options || {}, - options: Object.assign( - Object.create(this.options || {}), - // defaults... - // XXX is this the correct way to setup defaults??? - { - iterateNonIterable: true, - }, - options || {}), - } - : options - options = context.options - renderer = renderer || this - - // XXX should we control render parameters (range, start, end, ...) - // from outside render and pass this info down to nested lists??? - // ...if yes how?? - // - options - // - arg threading - var items = this - .walk( - function(i, path, item, nested, sublist){ - return ( - // inline... - (item == null && sublist) ? - // NOTE: here we are forcing rendering of the - // inline browser/list, i.e. ignoring - // options.skipNested for inline stuff... - // NOTE: we here do not distinguish between - // inlined lists and browsers... (XXX ???) - [ renderer.renderGroup(nested(true), context) ] - // nested... - : sublist ? - [ renderer.renderNested( - renderer.renderNestedHeader(item, i, context), - nested(), - item, - context) ] - // normal item... - : [ renderer.renderItem(item, i, context) ] ) }, - function(func, i, path, sublist, options){ - return sublist.render(context, renderer) }, - options) - - // determine the render mode... - return context.root === this ? - // root context -> render list and return this... - renderer.renderList(items, context) - // nested context -> return item list... - : items - }, - - // Extended map... - // - // Get all items... - // .map([options]) - // -> items - // - // Map func to items... - // .map(func[, options]) - // -> items - // - // - // - // func(item, index, path, browser) - // -> result - // - // - // - // For supported options see docs for .walk(..) - // - // By default this will not iterate items that are: - // - non-iterable (item.noniterable is true) - // - collapsed sub-items (item.collapsed is true) - // - // This extends the Array .map(..) by adding: - // - ability to run without arguments - // - support for options - // - // - // XXX make item access by index lazy... - // - index nested stuff and lengths... (.sublist_length) - // - stop when target reached... (control callback???) - // XXX Q: should we have an option to treat groups as elements??? - map: function(func, options){ - var that = this - - // parse args... - // - // NOTE: in addition to the user signatures documented above this - // also supports two variants used internally: - // .map(func, path[, options]) - // .map(func, i, path[, options]) - // these set the "base" path and index passed to func... - // XXX can we avoid argument parsing here??? - var args = [...arguments] - func = args[0] instanceof Function ? - args.shift() - : undefined - var i = typeof(args[0]) == typeof(123) ? - args.shift() - : 0 - var path = (args[0] instanceof Array - || typeof(args[0]) == typeof('str')) ? - args.shift() - : [] - var options = args.pop() || {} - - return this.walk( - function(i, path, elem){ - return elem != null ? - [func === undefined ? - elem - // XXX should this pass the current or the root - // container to func??? - : func.call(that, elem, i, path, that)] - : [] }, - function(_, i, path, sublist, options){ - // NOTE: this needs to call the actual func that the user - // gave us and not the constructed function that we - // pass to .walk(..) above... - return sublist.map(func, i, path, options) }, - i, - path, - options) - }, - - - // Sublist map functions... - // NOTE: there are different from .map(..) in that instead of paths - // func(..) will get indexes in the current browser... - // NOTE: these will return a sparse array... - sublists: function(func, options){ - var that = this - //options = options || {} - options = Object.assign(Object.create(this.options || {}), options || {}) - var skipNested = options.skipNested - var skipInlined = options.skipInlined - - var res = [] - this.items - .forEach(function(elem, i){ - if((!skipInlined && elem.value instanceof Browser) - || (!skipNested && elem.sublist)){ - res[i] = func ? - func.call(that, elem, i, that) - : elem - } }) - return res - }, - nested: function(func){ - return this.sublists(func, {skipInlined: true}) }, - inlined: function(func){ - return this.sublists(func, {skipNested: true}) }, - - next: function(){}, - prev: function(){}, - - // XXX should there return an array or a .constructor(..) instance?? - // XXX should these call respective methods (.forEach(..), .filter(..), - // .reduce(..)) on the nested browsers??? - forEach: function(func, options){ - this.map(...arguments) - return this }, - filter: function(func, options){ - return this.map(function(e, i, p, b){ - return func.call(this, e, i, p, b) ? [e] : [] }) - .flat() }, - reduce: function(){}, - - - // XXX do we need edit ability here? - // i.e. .set(..), .remove(..), .sort(..), ... - // ...if we are going to implement editing then we'll need to - // callback the user code or update the user state... // Events...