mirror of
https://github.com/flynx/pWiki.git
synced 2025-10-29 18:10:09 +00:00
npm update + tweaks...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
30140f0918
commit
2b58903675
2
Makefile
2
Makefile
@ -7,6 +7,8 @@ BOOTSTRAP_FILES := \
|
|||||||
README.md
|
README.md
|
||||||
|
|
||||||
LOCAL_MODULES := \
|
LOCAL_MODULES := \
|
||||||
|
node_modules/ig-doc/doc.js \
|
||||||
|
node_modules/ig-stoppable/stoppable.js \
|
||||||
node_modules/ig-object/object.js \
|
node_modules/ig-object/object.js \
|
||||||
node_modules/ig-actions/actions.js \
|
node_modules/ig-actions/actions.js \
|
||||||
node_modules/ig-features/features.js
|
node_modules/ig-features/features.js
|
||||||
|
|||||||
137
lib/doc.js
Executable file
137
lib/doc.js
Executable file
@ -0,0 +1,137 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
* doc.js
|
||||||
|
*
|
||||||
|
* Basic JavaScript self-documentation utils
|
||||||
|
*
|
||||||
|
***********************************************/ /* c8 ignore next 2 */
|
||||||
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||||
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
module.TAB_SIZE = 4
|
||||||
|
|
||||||
|
module.LEADING_TABS = 1
|
||||||
|
|
||||||
|
|
||||||
|
// Normalize code indent...
|
||||||
|
//
|
||||||
|
// normalizeIndent(text)
|
||||||
|
// -> text
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This will remove common indent from each line of text, this is useful
|
||||||
|
// for printing function code of functions that were defined at deep
|
||||||
|
// levels of indent.
|
||||||
|
//
|
||||||
|
// This will ignore the indent of the first line.
|
||||||
|
//
|
||||||
|
// If the last line is indented higher or equal to the rest of the text
|
||||||
|
// we will use leading_tabs (defaults to LEADING_TABS) to indent the
|
||||||
|
// rest of the text.
|
||||||
|
// This will indent the following styles correctnly:
|
||||||
|
//
|
||||||
|
// |function(a, b){ |function(a, b){
|
||||||
|
// | return a + b } | return a + b
|
||||||
|
// | |}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this will trim out both leading and trailing white-space.
|
||||||
|
// NOTE: this is generally code-agnostic with one sigificant
|
||||||
|
// exception -- normalizeIndent(..) will break code written
|
||||||
|
// in Whitespace.
|
||||||
|
//
|
||||||
|
// XXX BUG?
|
||||||
|
// `a `a `a
|
||||||
|
// | b -> |b expected? | b
|
||||||
|
// | c` | c` | c`
|
||||||
|
// while:
|
||||||
|
// `a `a
|
||||||
|
// | b -> | b as expected.
|
||||||
|
// | c` | c`
|
||||||
|
// this leads to functions like the following to get messed up:
|
||||||
|
// |function(a){
|
||||||
|
// | return a
|
||||||
|
// | || 'moo' }
|
||||||
|
//
|
||||||
|
// XXX is this the right place for this???
|
||||||
|
// ...when moving take care that ImageGrid's core.doc uses this...
|
||||||
|
var normalizeIndent =
|
||||||
|
module.normalizeIndent =
|
||||||
|
function(text, {tab_size=module.TAB_SIZE, leading_tabs=module.LEADING_TABS, pad_tabs=0}={}){
|
||||||
|
leading_tabs *= tab_size
|
||||||
|
var padding = ' '.repeat(pad_tabs*tab_size)
|
||||||
|
// prepare text...
|
||||||
|
var tab = ' '.repeat(tab_size || 0)
|
||||||
|
text = tab != '' ?
|
||||||
|
text.replace(/\t/g, tab)
|
||||||
|
: text
|
||||||
|
// trim the tail and remove leading blank lines...
|
||||||
|
var lines = text.trimEnd().split(/\n/)
|
||||||
|
while(lines.length > 0
|
||||||
|
&& lines[0].trim() == ''){
|
||||||
|
// XXX we have two options here:
|
||||||
|
// - indent everyline including the first non-blank
|
||||||
|
// - do not indent anything (current)
|
||||||
|
// ...not sure which is best...
|
||||||
|
leading_tabs = 0
|
||||||
|
lines.shift() }
|
||||||
|
// count common indent...
|
||||||
|
var l = lines
|
||||||
|
.reduce(function(l, e, i){
|
||||||
|
var indent = e.length - e.trimLeft().length
|
||||||
|
return e.trim().length == 0
|
||||||
|
// ignore 0 indent of first line...
|
||||||
|
|| (i == 0 && indent == 0) ?
|
||||||
|
l
|
||||||
|
// last line...
|
||||||
|
: i == lines.length-1
|
||||||
|
&& indent >= l ?
|
||||||
|
// XXX feels a bit overcomplicated...
|
||||||
|
(l < 0 ?
|
||||||
|
// last of two with 0 indent on first -> indent...
|
||||||
|
Math.max(indent - leading_tabs, 0)
|
||||||
|
// ignore leading_tabs if lower indent...
|
||||||
|
: Math.min(l, Math.max(indent - leading_tabs, 0)))
|
||||||
|
// initial state...
|
||||||
|
: l < 0 ?
|
||||||
|
indent
|
||||||
|
// min...
|
||||||
|
: Math.min(l, indent) }, -1) || 0
|
||||||
|
// normalize...
|
||||||
|
return padding
|
||||||
|
+lines
|
||||||
|
.map(function(line, i){
|
||||||
|
return i == 0 ?
|
||||||
|
line
|
||||||
|
: line.slice(l) })
|
||||||
|
.join('\n'+ padding)
|
||||||
|
.trim() }
|
||||||
|
|
||||||
|
|
||||||
|
// shorthand more suted for text...
|
||||||
|
var normalizeTextIndent =
|
||||||
|
module.normalizeTextIndent =
|
||||||
|
function(text, opts={leading_tabs: 0}){
|
||||||
|
return module.normalizeIndent(text, opts) }
|
||||||
|
|
||||||
|
|
||||||
|
// template string tag versions of the above...
|
||||||
|
var doc =
|
||||||
|
module.doc =
|
||||||
|
function(strings, ...values){
|
||||||
|
return normalizeIndent(strings
|
||||||
|
.map(function(s, i){ return s + (values[i] || '') })
|
||||||
|
.join('')) }
|
||||||
|
|
||||||
|
var text =
|
||||||
|
module.text =
|
||||||
|
function(strings, ...values){
|
||||||
|
return normalizeTextIndent(strings
|
||||||
|
.map(function(s, i){ return s + (values[i] || '') })
|
||||||
|
.join('')) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* vim:set ts=4 sw=4 nowrap : */ return module })
|
||||||
466
lib/object.js
466
lib/object.js
@ -21,6 +21,19 @@
|
|||||||
(function(require){ var module={} // make module AMD/node compatible...
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var doc = require('ig-doc')
|
||||||
|
|
||||||
|
module.doc = doc.doc
|
||||||
|
module.text = doc.text
|
||||||
|
module.normalizeIndent = doc.normalizeIndent
|
||||||
|
module.normalizeTextIndent = doc.normalizeTextIndent
|
||||||
|
|
||||||
|
var STOP =
|
||||||
|
module.STOP =
|
||||||
|
require('ig-stoppable').STOP
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
// Function methods to link into a constructor producing a callable
|
// Function methods to link into a constructor producing a callable
|
||||||
// defined via .__call__(..)
|
// defined via .__call__(..)
|
||||||
@ -49,7 +62,9 @@ module.LINK_FUNCTION_METHODS = [
|
|||||||
//
|
//
|
||||||
var BOOTSTRAP =
|
var BOOTSTRAP =
|
||||||
function(func){
|
function(func){
|
||||||
var b = BOOTSTRAP.__delayed = BOOTSTRAP.__delayed || []
|
var b = BOOTSTRAP.__delayed =
|
||||||
|
BOOTSTRAP.__delayed
|
||||||
|
|| []
|
||||||
func ?
|
func ?
|
||||||
b.push(func)
|
b.push(func)
|
||||||
: b.map(function(f){ f() }) }
|
: b.map(function(f){ f() }) }
|
||||||
@ -58,144 +73,36 @@ function(func){
|
|||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
module.TAB_SIZE = 4
|
|
||||||
|
|
||||||
module.LEADING_TABS = 1
|
|
||||||
|
|
||||||
|
|
||||||
// Normalize code indent...
|
|
||||||
//
|
|
||||||
// normalizeIndent(text)
|
|
||||||
// -> text
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// This will remove common indent from each line of text, this is useful
|
|
||||||
// for printing function code of functions that were defined at deep
|
|
||||||
// levels of indent.
|
|
||||||
//
|
|
||||||
// This will ignore the indent of the first line.
|
|
||||||
//
|
|
||||||
// If the last line is indented higher or equal to the rest of the text
|
|
||||||
// we will use leading_tabs (defaults to LEADING_TABS) to indent the
|
|
||||||
// rest of the text.
|
|
||||||
// This will indent the following styles correctnly:
|
|
||||||
//
|
|
||||||
// |function(a, b){ |function(a, b){
|
|
||||||
// | return a + b } | return a + b
|
|
||||||
// | |}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: this will trim out both leading and trailing white-space.
|
|
||||||
// NOTE: this is generally code-agnostic with one sigificant
|
|
||||||
// exception -- normalizeIndent(..) will break code written
|
|
||||||
// in Whitespace.
|
|
||||||
//
|
|
||||||
// XXX BUG?
|
|
||||||
// `a `a `a
|
|
||||||
// | b -> |b expected? | b
|
|
||||||
// | c` | c` | c`
|
|
||||||
// while:
|
|
||||||
// `a `a
|
|
||||||
// | b -> | b as expected.
|
|
||||||
// | c` | c`
|
|
||||||
// this leads to functions like the following to get messed up:
|
|
||||||
// |function(a){
|
|
||||||
// | return a
|
|
||||||
// | || 'moo' }
|
|
||||||
//
|
|
||||||
// XXX is this the right place for this???
|
|
||||||
// ...when moving take care that ImageGrid's core.doc uses this...
|
|
||||||
var normalizeIndent =
|
|
||||||
module.normalizeIndent =
|
|
||||||
function(text, {tab_size=module.TAB_SIZE, leading_tabs=module.LEADING_TABS, pad_tabs=0}={}){
|
|
||||||
leading_tabs *= tab_size
|
|
||||||
var padding = ' '.repeat(pad_tabs*tab_size)
|
|
||||||
// prepare text...
|
|
||||||
var tab = ' '.repeat(tab_size || 0)
|
|
||||||
text = tab != '' ?
|
|
||||||
text.replace(/\t/g, tab)
|
|
||||||
: text
|
|
||||||
// trim the tail and remove leading blank lines...
|
|
||||||
var lines = text.trimEnd().split(/\n/)
|
|
||||||
while(lines.length > 0
|
|
||||||
&& lines[0].trim() == ''){
|
|
||||||
// XXX we have two options here:
|
|
||||||
// - indent everyline including the first non-blank
|
|
||||||
// - do not indent anything (current)
|
|
||||||
// ...not sure which is best...
|
|
||||||
leading_tabs = 0
|
|
||||||
lines.shift() }
|
|
||||||
// count common indent...
|
|
||||||
var l = lines
|
|
||||||
.reduce(function(l, e, i){
|
|
||||||
var indent = e.length - e.trimLeft().length
|
|
||||||
return e.trim().length == 0
|
|
||||||
// ignore 0 indent of first line...
|
|
||||||
|| (i == 0 && indent == 0) ?
|
|
||||||
l
|
|
||||||
// last line...
|
|
||||||
: i == lines.length-1
|
|
||||||
&& indent >= l ?
|
|
||||||
// XXX feels a bit overcomplicated...
|
|
||||||
(l < 0 ?
|
|
||||||
// last of two with 0 indent on first -> indent...
|
|
||||||
Math.max(indent - leading_tabs, 0)
|
|
||||||
// ignore leading_tabs if lower indent...
|
|
||||||
: Math.min(l, Math.max(indent - leading_tabs, 0)))
|
|
||||||
// initial state...
|
|
||||||
: l < 0 ?
|
|
||||||
indent
|
|
||||||
// min...
|
|
||||||
: Math.min(l, indent) }, -1) || 0
|
|
||||||
// normalize...
|
|
||||||
return padding
|
|
||||||
+lines
|
|
||||||
.map(function(line, i){
|
|
||||||
return i == 0 ?
|
|
||||||
line
|
|
||||||
: line.slice(l) })
|
|
||||||
.join('\n'+ padding)
|
|
||||||
.trim() }
|
|
||||||
|
|
||||||
|
|
||||||
// shorthand more suted for text...
|
|
||||||
var normalizeTextIndent =
|
|
||||||
module.normalizeTextIndent =
|
|
||||||
function(text, opts={leading_tabs: 0}){
|
|
||||||
return module.normalizeIndent(text, opts) }
|
|
||||||
|
|
||||||
|
|
||||||
// template string tag versions of the above...
|
|
||||||
var doc =
|
|
||||||
module.doc =
|
|
||||||
function(strings, ...values){
|
|
||||||
return normalizeIndent(strings
|
|
||||||
.map(function(s, i){ return s + (values[i] || '') })
|
|
||||||
.join('')) }
|
|
||||||
|
|
||||||
var text =
|
|
||||||
module.text =
|
|
||||||
function(strings, ...values){
|
|
||||||
return normalizeTextIndent(strings
|
|
||||||
.map(function(s, i){ return s + (values[i] || '') })
|
|
||||||
.join('')) }
|
|
||||||
|
|
||||||
|
|
||||||
// Get keys from prototype chain...
|
// Get keys from prototype chain...
|
||||||
//
|
//
|
||||||
// deepKeys(obj)
|
// deepKeys(obj[, all])
|
||||||
// deepKeys(obj, stop)
|
// deepKeys(obj, stop[, all])
|
||||||
|
// -> keys
|
||||||
|
//
|
||||||
|
// List all keys incuding non-enumerable...
|
||||||
|
// deepKeys(obj, true)
|
||||||
|
// deepKeys(obj, stop, true)
|
||||||
// -> keys
|
// -> keys
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// NOTE: this is like Object.keys(..) but will get keys for all levels
|
// NOTE: this is like Object.keys(..) but will get keys for all levels
|
||||||
// till stop if given...
|
// till stop if given...
|
||||||
|
// NOTE: by default this lists only enumerable keys...
|
||||||
var deepKeys =
|
var deepKeys =
|
||||||
module.deepKeys =
|
module.deepKeys =
|
||||||
function(obj, stop){
|
function(obj, stop, all){
|
||||||
|
all = arguments[arguments.length-1]
|
||||||
|
all = (all === true || all === false) ?
|
||||||
|
all
|
||||||
|
: false
|
||||||
|
stop = (stop === true || stop === false) ?
|
||||||
|
undefined
|
||||||
|
: stop
|
||||||
var res = []
|
var res = []
|
||||||
while(obj != null){
|
while(obj != null){
|
||||||
res.push(Object.keys(obj))
|
res.push(Object.keys(all ?
|
||||||
|
Object.getOwnPropertyDescriptors(obj)
|
||||||
|
: obj))
|
||||||
if(obj === stop){
|
if(obj === stop){
|
||||||
break }
|
break }
|
||||||
obj = obj.__proto__ }
|
obj = obj.__proto__ }
|
||||||
@ -235,7 +142,8 @@ function(base, obj, non_strict){
|
|||||||
return false }
|
return false }
|
||||||
// attr count...
|
// attr count...
|
||||||
var o = Object.keys(Object.getOwnPropertyDescriptors(obj))
|
var o = Object.keys(Object.getOwnPropertyDescriptors(obj))
|
||||||
if(Object.keys(Object.getOwnPropertyDescriptors(base)).length != o.length){
|
if(Object.keys(Object.getOwnPropertyDescriptors(base)).length
|
||||||
|
!= o.length){
|
||||||
return false }
|
return false }
|
||||||
// names and values...
|
// names and values...
|
||||||
o = o.map(function(k){
|
o = o.map(function(k){
|
||||||
@ -285,13 +193,16 @@ module.create =
|
|||||||
function(obj){
|
function(obj){
|
||||||
// name given...
|
// name given...
|
||||||
var name = ''
|
var name = ''
|
||||||
if(typeof(obj) == 'string' && arguments.length > 1){
|
if(typeof(obj) == 'string'
|
||||||
|
&& arguments.length > 1){
|
||||||
;[name, obj] = arguments
|
;[name, obj] = arguments
|
||||||
// sanity check...
|
// sanity check...
|
||||||
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
||||||
throw new Error(`create(..): invalid name: "${name}"`) } }
|
throw new Error(`create(..): invalid name: "${name}"`) } }
|
||||||
// calable...
|
// calable...
|
||||||
if(typeof(obj) == 'function'){
|
if(typeof(obj) == 'function'){
|
||||||
|
// NOTE: this is ignored by c8 as we will never run this directly
|
||||||
|
// as it will immediately get eval(..)'ed...
|
||||||
/* c8 ignore next 9 */
|
/* c8 ignore next 9 */
|
||||||
var func = function(){
|
var func = function(){
|
||||||
return '__call__' in func ?
|
return '__call__' in func ?
|
||||||
@ -306,12 +217,15 @@ function(obj){
|
|||||||
// NOTE: we just created func(..) so no need to sanitize it, the
|
// NOTE: we just created func(..) so no need to sanitize it, the
|
||||||
// only potential vector of atack (AFAIK) here is name and
|
// only potential vector of atack (AFAIK) here is name and
|
||||||
// that is checked above...
|
// that is checked above...
|
||||||
func.name = name
|
Object.defineProperty(func, 'name', {value: name})
|
||||||
|
/* XXX NAME...
|
||||||
|
//func.name = name
|
||||||
func.name != name
|
func.name != name
|
||||||
&& (func = eval('('+
|
&& (func = eval('('+
|
||||||
func
|
func
|
||||||
.toString()
|
.toString()
|
||||||
.replace(/function\(/, `function ${name}(`) +')'))
|
.replace(/function\(/, `function ${name}(`) +')'))
|
||||||
|
//*/
|
||||||
func.__proto__ = obj
|
func.__proto__ = obj
|
||||||
__toStringProxy(func)
|
__toStringProxy(func)
|
||||||
return func }
|
return func }
|
||||||
@ -333,78 +247,39 @@ BOOTSTRAP(function(){
|
|||||||
get name(){
|
get name(){
|
||||||
return this.constructor.name },
|
return this.constructor.name },
|
||||||
|
|
||||||
// XXX BUG? is this an error that with this everything seems to work
|
// XXX BUG? is this an error that with this everything seems
|
||||||
// while without this instances of this work fine while instances
|
// to work while without this instances of this work
|
||||||
// of "sub-classes" do not set the .stack correctly???
|
// fine while instances of "sub-classes" do not set
|
||||||
|
// the .stack correctly???
|
||||||
// ...is this a JS quirk or am I missing something???
|
// ...is this a JS quirk or am I missing something???
|
||||||
__new__: function(context, ...args){
|
__new__: function(context, ...args){
|
||||||
return Reflect.construct(module.Error.__proto__, args, this.constructor) },
|
return Reflect.construct(
|
||||||
|
module.Error.__proto__, args, this.constructor) },
|
||||||
//return Reflect.construct(Error, args, this.constructor) },
|
//return Reflect.construct(Error, args, this.constructor) },
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Prototype chain content access...
|
// Prototype chain content access...
|
||||||
|
|
||||||
BOOTSTRAP(function(){
|
// Get source objects for a prop/attr name...
|
||||||
|
|
||||||
// Value trigger iteration stop and to carry results...
|
|
||||||
//
|
|
||||||
module.STOP =
|
|
||||||
Constructor('STOP', {
|
|
||||||
doc: 'stop iteration.',
|
|
||||||
__init__: function(value){
|
|
||||||
this.value = value },
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Get a list of source objects for a prop/attr name...
|
|
||||||
//
|
//
|
||||||
// sources(obj, name)
|
// sources(obj, name)
|
||||||
// sources(obj, name, callback)
|
// -> iterator
|
||||||
// -> list
|
|
||||||
// -> []
|
|
||||||
//
|
|
||||||
// Get callables or objects defining .__call__ (special-case)
|
|
||||||
// sources(obj, '__call__')
|
|
||||||
// sources(obj, '__call__', callback)
|
|
||||||
// -> list
|
|
||||||
// -> []
|
|
||||||
//
|
//
|
||||||
// Get full chain...
|
// Get full chain...
|
||||||
// sources(obj)
|
// sources(obj)
|
||||||
// sources(obj, callback)
|
// -> iterator
|
||||||
// -> list
|
//
|
||||||
|
// Get callables or objects defining .__call__ (special-case)
|
||||||
|
// sources(obj, '__call__')
|
||||||
|
// -> iterator
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// callback(obj, i)
|
// NOTE: this goes up the prototype chain, not caring about any role (
|
||||||
// -> STOP
|
|
||||||
// -> STOP(value)
|
|
||||||
// -> ..
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// The callback(..) is called with each matching object.
|
|
||||||
//
|
|
||||||
// callback(..) return values:
|
|
||||||
// - STOP - stop the search and return the match list terminated
|
|
||||||
// with the object triggering the stop.
|
|
||||||
// - STOP(value) - stop the search and return the match list terminated
|
|
||||||
// with the value passed to STOP(..)
|
|
||||||
// - undefined - return the triggering object as-is
|
|
||||||
// NOTE: this is the same as returning [obj]
|
|
||||||
// - array - merge array content into the result insteaad of
|
|
||||||
// the triggering value.
|
|
||||||
// NOTE: an ampty array will effectively omit the
|
|
||||||
// triggering object from the results.
|
|
||||||
// - other - return a value instead of the triggering object.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: this gos up the prototype chain, not caring about any role (
|
|
||||||
// instance/class or instance/prototype) bounderies and depends
|
// instance/class or instance/prototype) bounderies and depends
|
||||||
// only on the object given as the starting point.
|
// only on the object given as the starting point.
|
||||||
// It is possible to start the search from this, thus checking
|
// It is possible to start the search from this, thus checking
|
||||||
@ -413,56 +288,31 @@ BOOTSTRAP(function(){
|
|||||||
// NOTE: this will not trigger any props...
|
// NOTE: this will not trigger any props...
|
||||||
var sources =
|
var sources =
|
||||||
module.sources =
|
module.sources =
|
||||||
function(obj, name, callback){
|
function*(obj, name=undefined){
|
||||||
// get full chain...
|
|
||||||
if(typeof(name) == 'function'){
|
|
||||||
callback = name
|
|
||||||
name = undefined
|
|
||||||
}
|
|
||||||
var i = 0
|
|
||||||
var o
|
|
||||||
var res = []
|
|
||||||
while(obj != null){
|
while(obj != null){
|
||||||
//if(obj.hasOwnProperty(name)){
|
|
||||||
if(name === undefined
|
if(name === undefined
|
||||||
|| obj.hasOwnProperty(name)
|
|| obj.hasOwnProperty(name)
|
||||||
|| (name == '__call__' && typeof(obj) == 'function')){
|
|| (name == '__call__'
|
||||||
// handle callback...
|
&& typeof(obj) == 'function')){
|
||||||
o = callback
|
yield obj }
|
||||||
&& callback(obj, i++)
|
obj = obj.__proto__ } }
|
||||||
// manage results...
|
|
||||||
res.push(
|
|
||||||
(o === undefined || o === module.STOP) ?
|
|
||||||
[obj]
|
|
||||||
: o instanceof module.STOP ?
|
|
||||||
o.value
|
|
||||||
: o )
|
|
||||||
// stop...
|
|
||||||
if(o === module.STOP
|
|
||||||
|| o instanceof module.STOP){
|
|
||||||
return res.flat() } }
|
|
||||||
obj = obj.__proto__ }
|
|
||||||
return res.flat() }
|
|
||||||
|
|
||||||
|
|
||||||
// Get a list of values/props set in source objects for a prop/attr name...
|
// Get object-value/prop pairs set in source objects for a prop/attr name...
|
||||||
//
|
//
|
||||||
// Get values...
|
// entries(obj, name)
|
||||||
// values(obj, name)
|
// -> iterator
|
||||||
// values(obj, name, callback)
|
|
||||||
// -> list
|
|
||||||
// -> []
|
|
||||||
//
|
//
|
||||||
// Get propery descriptors...
|
// Get propery descriptors...
|
||||||
// values(obj, name, true)
|
// entries(obj, name, true)
|
||||||
// values(obj, name, callback, true)
|
// -> iterator
|
||||||
// -> list
|
|
||||||
// -> []
|
|
||||||
//
|
//
|
||||||
// callback(value/prop, obj)
|
//
|
||||||
// -> STOP
|
// Item format:
|
||||||
// -> STOP(value)
|
// [
|
||||||
// -> ..
|
// source,
|
||||||
|
// value,
|
||||||
|
// ]
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Special case: name is given as '__call__'
|
// Special case: name is given as '__call__'
|
||||||
@ -470,39 +320,32 @@ function(obj, name, callback){
|
|||||||
// or the value of .__call__ attribute...
|
// or the value of .__call__ attribute...
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// NOTE: for more docs on the callback(..) see sources(..)
|
// NOTE: for more docs see sources(..)
|
||||||
var values =
|
var entries =
|
||||||
module.values =
|
module.entries =
|
||||||
function(obj, name, callback, props){
|
function*(obj, name, props=false){
|
||||||
props = callback === true ?
|
for(var obj of sources(obj, name)){
|
||||||
callback
|
yield [
|
||||||
: props
|
obj,
|
||||||
var _get = function(obj, name){
|
props ?
|
||||||
return props ?
|
|
||||||
Object.getOwnPropertyDescriptor(obj, name)
|
Object.getOwnPropertyDescriptor(obj, name)
|
||||||
// handle callable instance...
|
// handle callable instance...
|
||||||
: !(name in obj)
|
: !(name in obj)
|
||||||
&& name == '__call__'
|
&& name == '__call__'
|
||||||
&& typeof(obj) == 'function' ?
|
&& typeof(obj) == 'function' ?
|
||||||
obj
|
obj
|
||||||
// normal attr...
|
: obj[name],
|
||||||
: obj[name] }
|
] }}
|
||||||
// wrap the callback if given...
|
|
||||||
var c = typeof(callback) == 'function'
|
|
||||||
&& function(obj, i){
|
// Get values/props set in source objects for a prop/attr name...
|
||||||
var val = _get(obj, name)
|
//
|
||||||
var res = callback(val, obj, i)
|
// NOTE: this is specialization of entries(..), see that for more info.
|
||||||
return res === module.STOP ?
|
var values =
|
||||||
// wrap the expected stop result if the user did not do it...
|
module.values =
|
||||||
module.STOP(val)
|
function*(obj, name, props=false){
|
||||||
: res }
|
for(var [_, value] of entries(...arguments)){
|
||||||
return c ?
|
yield value }}
|
||||||
// NOTE: we do not need to handle the callback return values as
|
|
||||||
// this is fully done by c(..) in sources(..)
|
|
||||||
sources(obj, name, c)
|
|
||||||
: sources(obj, name)
|
|
||||||
.map(function(obj){
|
|
||||||
return _get(obj, name) }) }
|
|
||||||
|
|
||||||
|
|
||||||
// Find the next parent attribute in the prototype chain.
|
// Find the next parent attribute in the prototype chain.
|
||||||
@ -588,24 +431,22 @@ function(proto, name){
|
|||||||
if(name == ''){
|
if(name == ''){
|
||||||
throw new Error('parent(..): need a method with non-empty .name') }
|
throw new Error('parent(..): need a method with non-empty .name') }
|
||||||
// get first matching source...
|
// get first matching source...
|
||||||
proto = sources(that, name,
|
var i = 0
|
||||||
function(obj, i){
|
for(var obj of sources(that, name)){
|
||||||
// NOTE: the .hasOwnProperty(..) test is here so as
|
// NOTE: the .hasOwnProperty(..) test is here so as
|
||||||
// to skip the base callable when searching for
|
// to skip the base callable when searching for
|
||||||
// .__call__ that is returned as a special case
|
// .__call__ that is returned as a special case
|
||||||
// by sourcei(..) and this should have no effect
|
// by source(..) and this should have no effect
|
||||||
// or other cases...
|
// or other cases...
|
||||||
// NOTE: this will only skip the root callable...
|
// NOTE: this will only skip the root callable...
|
||||||
return (i > 0 || obj.hasOwnProperty(name))
|
if((i++ > 0 || obj.hasOwnProperty(name))
|
||||||
&& obj[name] === proto
|
&& obj[name] === proto){
|
||||||
&& module.STOP })
|
proto = obj
|
||||||
.pop() }
|
break }}}
|
||||||
// get first source...
|
// get first source...
|
||||||
var res = sources(proto, name,
|
var res = sources(proto, name)
|
||||||
function(obj, i){
|
res.next()
|
||||||
return i == 1
|
res = res.next().value
|
||||||
&& module.STOP })
|
|
||||||
.pop()
|
|
||||||
return !res ?
|
return !res ?
|
||||||
undefined
|
undefined
|
||||||
:(!(name in res) && typeof(res) == 'function') ?
|
:(!(name in res) && typeof(res) == 'function') ?
|
||||||
@ -624,11 +465,9 @@ var parentProperty =
|
|||||||
module.parentProperty =
|
module.parentProperty =
|
||||||
function(proto, name){
|
function(proto, name){
|
||||||
// get second source...
|
// get second source...
|
||||||
var res = sources(proto, name,
|
var res = sources(proto, name)
|
||||||
function(obj, i){
|
res.next()
|
||||||
return i == 1
|
res = res.next().value
|
||||||
&& module.STOP })
|
|
||||||
.pop()
|
|
||||||
return res ?
|
return res ?
|
||||||
// get next value...
|
// get next value...
|
||||||
Object.getOwnPropertyDescriptor(res, name)
|
Object.getOwnPropertyDescriptor(res, name)
|
||||||
@ -674,7 +513,7 @@ function(proto, name, that, ...args){
|
|||||||
var parentOf =
|
var parentOf =
|
||||||
module.parentOf =
|
module.parentOf =
|
||||||
function(parent, child){
|
function(parent, child){
|
||||||
return new Set(sources(child)).has(parent) }
|
return new Set([...sources(child)]).has(parent) }
|
||||||
|
|
||||||
// Reverse of parentOf(..)
|
// Reverse of parentOf(..)
|
||||||
var childOf =
|
var childOf =
|
||||||
@ -711,7 +550,7 @@ function(func){
|
|||||||
: '__call__' in this ?
|
: '__call__' in this ?
|
||||||
this.__call__
|
this.__call__
|
||||||
: this.__proto__)
|
: this.__proto__)
|
||||||
return module.normalizeIndent(f.toString(...args)) },
|
return doc.normalizeIndent(f.toString(...args)) },
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
})
|
})
|
||||||
return func }
|
return func }
|
||||||
@ -955,28 +794,78 @@ function(context, constructor, ...args){
|
|||||||
// to be rooted in Function.
|
// to be rooted in Function.
|
||||||
// though the typeof(..) == 'function' will always work.
|
// though the typeof(..) == 'function' will always work.
|
||||||
// NOTE: this will fail with non-identifier names...
|
// NOTE: this will fail with non-identifier names...
|
||||||
// XXX is this a bug or a feature??? =)
|
// NOTE: a bit more obvious syntax could be something like:
|
||||||
|
// var A = Constructor('A', {
|
||||||
|
// __proto__: B,
|
||||||
|
//
|
||||||
|
// // constructor stuff...
|
||||||
|
//
|
||||||
|
// prototype: {
|
||||||
|
// // instance stuff...
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// vs:
|
||||||
|
// var A = Constructor('A', B, {
|
||||||
|
// // constructor stuff...
|
||||||
|
//
|
||||||
|
// }, {
|
||||||
|
// // instance stuff...
|
||||||
|
// })
|
||||||
|
// this though a bit more obvious is in the general case can be
|
||||||
|
// more verbose, i.e. on the top level we define the constructor
|
||||||
|
// stuff which in the general case secondary while .prototype is a
|
||||||
|
// level below that while in use it's the main functionality... so,
|
||||||
|
// this is a question of ease of use vs. mirroring the JS structure,
|
||||||
|
// the answer chosen here is to prioritize simplicity and conciseness
|
||||||
|
// over verbose mirroring...
|
||||||
|
// XXX needs revision from time to time...
|
||||||
|
// XXX might be a good idea to implement the above syntax and test
|
||||||
|
// it out...
|
||||||
|
// Constructor(<name>[, <parent>], <mirror-block>)
|
||||||
|
// Constructor(<name>[, <parent>], <prototype-block>)
|
||||||
|
// Constructor(<name>[, <parent>], <prototype-block>, <prototype-block>)
|
||||||
|
//
|
||||||
|
// <mirror-block> ::=
|
||||||
|
// {
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// prototype: {
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// The only questions here is weather a set .prototype is a good
|
||||||
|
// enough indicator and should we use .__proto__?
|
||||||
var Constructor =
|
var Constructor =
|
||||||
module.Constructor =
|
module.Constructor =
|
||||||
// shorthand...
|
// shorthand...
|
||||||
module.C =
|
module.C =
|
||||||
function Constructor(name, a, b, c){
|
function Constructor(name, a, b, c){
|
||||||
var args = [...arguments].slice(1, 4)
|
|
||||||
|
|
||||||
// sanity check...
|
// sanity check...
|
||||||
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
||||||
throw new Error(`Constructor(..): invalid name: "${name}"`) }
|
throw new Error(`Constructor(..): invalid name: "${name}"`) }
|
||||||
|
|
||||||
// parse args...
|
// parse args...
|
||||||
|
var args = [...arguments].slice(1, 4)
|
||||||
|
|
||||||
// Constructor(name[[, constructor[, mixin]], proto])
|
// Constructor(name[[, constructor[, mixin]], proto])
|
||||||
var proto = args.pop() || {}
|
var proto = args.pop() || {}
|
||||||
var constructor_proto = typeof(args[0]) == 'function' ?
|
var constructor_proto =
|
||||||
|
typeof(args[0]) == 'function' ?
|
||||||
args.shift()
|
args.shift()
|
||||||
: undefined
|
: undefined
|
||||||
var constructor_mixin = args.pop()
|
var constructor_mixin = args.pop()
|
||||||
|
|
||||||
// handle:
|
/* XXX EXPERIMENTAL...
|
||||||
// Constructor(name, constructor, ..)
|
// handle: Constructor(name[, constructor], { prototype: { .. }, .. })
|
||||||
|
//
|
||||||
|
if(proto.hasOwnProperty('prototype')
|
||||||
|
&& proto.prototype){
|
||||||
|
console.log('>>>>', name)
|
||||||
|
constructor_mixin = proto
|
||||||
|
proto = proto.prototype }
|
||||||
|
//*/
|
||||||
|
|
||||||
|
// handle: Constructor(name, constructor, ..)
|
||||||
//
|
//
|
||||||
// NOTE: this is a bit too functional in style by an if-tree would
|
// NOTE: this is a bit too functional in style by an if-tree would
|
||||||
// be more bulky and less readable...
|
// be more bulky and less readable...
|
||||||
@ -1015,6 +904,8 @@ function Constructor(name, a, b, c){
|
|||||||
&& (proto.__proto__ = constructor_proto.prototype) }
|
&& (proto.__proto__ = constructor_proto.prototype) }
|
||||||
|
|
||||||
// the constructor base...
|
// the constructor base...
|
||||||
|
// NOTE: this is ignored by c8 as we will never run this directly as
|
||||||
|
// it will immediately get eval(..)'ed...
|
||||||
/* c8 ignore next 9 */
|
/* c8 ignore next 9 */
|
||||||
var _constructor = function Constructor(){
|
var _constructor = function Constructor(){
|
||||||
// create raw instance...
|
// create raw instance...
|
||||||
@ -1031,7 +922,9 @@ function Constructor(name, a, b, c){
|
|||||||
// Object.defineProperty(_constructor, 'name', { value: name })
|
// Object.defineProperty(_constructor, 'name', { value: name })
|
||||||
// because this does not affect the name displayed by the Chrome
|
// because this does not affect the name displayed by the Chrome
|
||||||
// DevTools. FF does not seem to care about either version of code...
|
// DevTools. FF does not seem to care about either version of code...
|
||||||
_constructor.name = name
|
Object.defineProperty(_constructor, 'name', {value: name})
|
||||||
|
/* XXX NAME...
|
||||||
|
//_constructor.name = name
|
||||||
// just in case the browser/node refuses to change the name, we'll make
|
// just in case the browser/node refuses to change the name, we'll make
|
||||||
// them a different offer ;)
|
// them a different offer ;)
|
||||||
// NOTE: it is not possible to abstract this eval(..) into something
|
// NOTE: it is not possible to abstract this eval(..) into something
|
||||||
@ -1044,6 +937,7 @@ function Constructor(name, a, b, c){
|
|||||||
_constructor
|
_constructor
|
||||||
.toString()
|
.toString()
|
||||||
.replace(/Constructor/g, name) +')'))
|
.replace(/Constructor/g, name) +')'))
|
||||||
|
//*/
|
||||||
// set .toString(..)...
|
// set .toString(..)...
|
||||||
// NOTE: this test is here to enable mixinFlat(..) to overwrite
|
// NOTE: this test is here to enable mixinFlat(..) to overwrite
|
||||||
// .toString(..) below...
|
// .toString(..) below...
|
||||||
@ -1065,7 +959,7 @@ function Constructor(name, a, b, c){
|
|||||||
.toString()
|
.toString()
|
||||||
.replace(/[^{]*{/, '{')
|
.replace(/[^{]*{/, '{')
|
||||||
: '{ .. }'
|
: '{ .. }'
|
||||||
return `${this.name}(${args})${module.normalizeIndent(code)}` },
|
return `${this.name}(${args})${doc.normalizeIndent(code)}` },
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
})
|
})
|
||||||
// set generic raw instance constructor...
|
// set generic raw instance constructor...
|
||||||
@ -1228,6 +1122,8 @@ function(base, ...objects){
|
|||||||
// NOTE: this will also match base...
|
// NOTE: this will also match base...
|
||||||
// NOTE: if base matches directly callback(..) will get undefined as parent
|
// NOTE: if base matches directly callback(..) will get undefined as parent
|
||||||
// NOTE: for more docs on the callback(..) see sources(..)
|
// NOTE: for more docs on the callback(..) see sources(..)
|
||||||
|
//
|
||||||
|
// XXX should this be a generator???
|
||||||
var mixins =
|
var mixins =
|
||||||
module.mixins =
|
module.mixins =
|
||||||
function(base, object, callback){
|
function(base, object, callback){
|
||||||
@ -1275,11 +1171,9 @@ function(base, object){
|
|||||||
mixins(base, object, function(){ return module.STOP })
|
mixins(base, object, function(){ return module.STOP })
|
||||||
.length > 0
|
.length > 0
|
||||||
// flat mixin search...
|
// flat mixin search...
|
||||||
|| sources(base, function(p){
|
|| [...sources(base)]
|
||||||
return matchPartial(p, object) ?
|
.some(function(p){
|
||||||
module.STOP
|
return matchPartial(p, object) }) )}
|
||||||
: [] })
|
|
||||||
.length > 0 )}
|
|
||||||
|
|
||||||
|
|
||||||
// Mix-out sets of methods/props/attrs out of an object prototype chain...
|
// Mix-out sets of methods/props/attrs out of an object prototype chain...
|
||||||
@ -1435,4 +1329,4 @@ BOOTSTRAP()
|
|||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* vim:set ts=4 sw=4 : */ return module })
|
* vim:set ts=4 sw=4 nowrap : */ return module })
|
||||||
|
|||||||
157
lib/stoppable.js
Executable file
157
lib/stoppable.js
Executable file
@ -0,0 +1,157 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
* stoppable.js
|
||||||
|
*
|
||||||
|
* Utility library implementing tooling to make stoppable functions...
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Repo and docs:
|
||||||
|
* https://github.com/flynx/stoppable.js
|
||||||
|
*
|
||||||
|
*
|
||||||
|
***********************************************/ /* c8 ignore next 2 */
|
||||||
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||||
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// helpers...
|
||||||
|
|
||||||
|
var AsyncFunction =
|
||||||
|
(async function(){}).constructor
|
||||||
|
|
||||||
|
var Generator =
|
||||||
|
(function*(){}).constructor
|
||||||
|
|
||||||
|
var AsyncGenerator =
|
||||||
|
(async function*(){}).constructor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Wrap a callable in a STOP handler
|
||||||
|
//
|
||||||
|
// stoppable(func)
|
||||||
|
// -> func
|
||||||
|
//
|
||||||
|
// stoppable(gen)
|
||||||
|
// -> gen
|
||||||
|
//
|
||||||
|
// stoppable(asyncgen)
|
||||||
|
// -> asyncgen
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The client callable can be one of:
|
||||||
|
// - function
|
||||||
|
// - generator
|
||||||
|
// - async generator
|
||||||
|
//
|
||||||
|
// The returned callable will be of the same type as the input callable.
|
||||||
|
//
|
||||||
|
// The wrapper handles STOP slightly differently if the client is a
|
||||||
|
// function or if it is a generator / async generator:
|
||||||
|
// - function
|
||||||
|
// STOP returned / thrown
|
||||||
|
// -> return undefined
|
||||||
|
// STOP(value) returned / thrown
|
||||||
|
// -> return value
|
||||||
|
// - generator / async generator
|
||||||
|
// STOP yielded / thrown
|
||||||
|
// -> iteration stops
|
||||||
|
// STOP(value) yielded / thrown
|
||||||
|
// -> value yielded and iteration stops
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this repeats the same code at lest twice, not sure yet how to avoid
|
||||||
|
// this...
|
||||||
|
module =
|
||||||
|
function(func){
|
||||||
|
return Object.assign(
|
||||||
|
// NOTE: the below implementations are almost the same, the main
|
||||||
|
// differences being the respective generator/async mechanics...
|
||||||
|
func instanceof Generator ?
|
||||||
|
function*(){
|
||||||
|
try{
|
||||||
|
for(var res of func.call(this, ...arguments)){
|
||||||
|
if(res === module.STOP){
|
||||||
|
return }
|
||||||
|
if(res instanceof module.STOP){
|
||||||
|
yield res.value
|
||||||
|
return }
|
||||||
|
yield res }
|
||||||
|
} catch(err){
|
||||||
|
if(err === module.STOP){
|
||||||
|
return
|
||||||
|
} else if(err instanceof module.STOP){
|
||||||
|
yield err.value
|
||||||
|
return }
|
||||||
|
throw err } }
|
||||||
|
: func instanceof AsyncGenerator ?
|
||||||
|
async function*(){
|
||||||
|
try{
|
||||||
|
for await(var res of func.call(this, ...arguments)){
|
||||||
|
if(res === module.STOP){
|
||||||
|
return }
|
||||||
|
if(res instanceof module.STOP){
|
||||||
|
yield res.value
|
||||||
|
return }
|
||||||
|
yield res }
|
||||||
|
} catch(err){
|
||||||
|
if(err === module.STOP){
|
||||||
|
return
|
||||||
|
} else if(err instanceof module.STOP){
|
||||||
|
yield err.value
|
||||||
|
return }
|
||||||
|
throw err } }
|
||||||
|
: func instanceof AsyncFunction ?
|
||||||
|
async function(){
|
||||||
|
try{
|
||||||
|
var res = await func.call(this, ...arguments)
|
||||||
|
// NOTE: this is here for uniformity...
|
||||||
|
if(res === module.STOP){
|
||||||
|
return }
|
||||||
|
if(res instanceof module.STOP){
|
||||||
|
return res.value }
|
||||||
|
return res
|
||||||
|
} catch(err){
|
||||||
|
if(err === module.STOP){
|
||||||
|
return
|
||||||
|
} else if(err instanceof module.STOP){
|
||||||
|
return err.value }
|
||||||
|
throw err } }
|
||||||
|
: function(){
|
||||||
|
try{
|
||||||
|
var res = func.call(this, ...arguments)
|
||||||
|
// NOTE: this is here for uniformity...
|
||||||
|
if(res === module.STOP){
|
||||||
|
return }
|
||||||
|
if(res instanceof module.STOP){
|
||||||
|
return res.value }
|
||||||
|
return res
|
||||||
|
} catch(err){
|
||||||
|
if(err === module.STOP){
|
||||||
|
return
|
||||||
|
} else if(err instanceof module.STOP){
|
||||||
|
return err.value }
|
||||||
|
throw err } },
|
||||||
|
{ toString: function(){
|
||||||
|
return func.toString() }, }) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
module.STOP =
|
||||||
|
function(value){
|
||||||
|
return {
|
||||||
|
__proto__: module.STOP.prototype,
|
||||||
|
doc: 'stop iteration.',
|
||||||
|
value,
|
||||||
|
} }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* vim:set ts=4 sw=4 nowrap : */ return module })
|
||||||
Loading…
x
Reference in New Issue
Block a user