mirror of
https://github.com/flynx/pWiki.git
synced 2025-10-28 09:30:07 +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
|
||||
|
||||
LOCAL_MODULES := \
|
||||
node_modules/ig-doc/doc.js \
|
||||
node_modules/ig-stoppable/stoppable.js \
|
||||
node_modules/ig-object/object.js \
|
||||
node_modules/ig-actions/actions.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 })
|
||||
504
lib/object.js
504
lib/object.js
@ -21,6 +21,19 @@
|
||||
(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
|
||||
// defined via .__call__(..)
|
||||
@ -49,7 +62,9 @@ module.LINK_FUNCTION_METHODS = [
|
||||
//
|
||||
var BOOTSTRAP =
|
||||
function(func){
|
||||
var b = BOOTSTRAP.__delayed = BOOTSTRAP.__delayed || []
|
||||
var b = BOOTSTRAP.__delayed =
|
||||
BOOTSTRAP.__delayed
|
||||
|| []
|
||||
func ?
|
||||
b.push(func)
|
||||
: 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...
|
||||
//
|
||||
// deepKeys(obj)
|
||||
// deepKeys(obj, stop)
|
||||
// deepKeys(obj[, all])
|
||||
// deepKeys(obj, stop[, all])
|
||||
// -> keys
|
||||
//
|
||||
// List all keys incuding non-enumerable...
|
||||
// deepKeys(obj, true)
|
||||
// deepKeys(obj, stop, true)
|
||||
// -> keys
|
||||
//
|
||||
//
|
||||
// NOTE: this is like Object.keys(..) but will get keys for all levels
|
||||
// till stop if given...
|
||||
// NOTE: by default this lists only enumerable keys...
|
||||
var 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 = []
|
||||
while(obj != null){
|
||||
res.push(Object.keys(obj))
|
||||
res.push(Object.keys(all ?
|
||||
Object.getOwnPropertyDescriptors(obj)
|
||||
: obj))
|
||||
if(obj === stop){
|
||||
break }
|
||||
obj = obj.__proto__ }
|
||||
@ -218,8 +125,8 @@ function(obj, stop){
|
||||
// Non-strict match...
|
||||
// match(a, b, true)
|
||||
//
|
||||
// This is similar to the default case but uses equality rather than
|
||||
// identity to match values.
|
||||
// This is similar to the default case but uses equality rather than
|
||||
// identity to match values.
|
||||
//
|
||||
//
|
||||
// NOTE: this will do a shallow test using Object.keys(..) thus .__proto__
|
||||
@ -235,7 +142,8 @@ function(base, obj, non_strict){
|
||||
return false }
|
||||
// attr count...
|
||||
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 }
|
||||
// names and values...
|
||||
o = o.map(function(k){
|
||||
@ -285,13 +193,16 @@ module.create =
|
||||
function(obj){
|
||||
// name given...
|
||||
var name = ''
|
||||
if(typeof(obj) == 'string' && arguments.length > 1){
|
||||
if(typeof(obj) == 'string'
|
||||
&& arguments.length > 1){
|
||||
;[name, obj] = arguments
|
||||
// sanity check...
|
||||
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
||||
throw new Error(`create(..): invalid name: "${name}"`) } }
|
||||
// calable...
|
||||
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 */
|
||||
var func = function(){
|
||||
return '__call__' in func ?
|
||||
@ -306,12 +217,15 @@ function(obj){
|
||||
// NOTE: we just created func(..) so no need to sanitize it, the
|
||||
// only potential vector of atack (AFAIK) here is name and
|
||||
// that is checked above...
|
||||
func.name = name
|
||||
Object.defineProperty(func, 'name', {value: name})
|
||||
/* XXX NAME...
|
||||
//func.name = name
|
||||
func.name != name
|
||||
&& (func = eval('('+
|
||||
func
|
||||
.toString()
|
||||
.replace(/function\(/, `function ${name}(`) +')'))
|
||||
//*/
|
||||
func.__proto__ = obj
|
||||
__toStringProxy(func)
|
||||
return func }
|
||||
@ -329,82 +243,43 @@ BOOTSTRAP(function(){
|
||||
//
|
||||
// XXX EXPERIMENTAL
|
||||
module.Error =
|
||||
Constructor('Error', Error, {
|
||||
get name(){
|
||||
return this.constructor.name },
|
||||
Constructor('Error', Error, {
|
||||
get name(){
|
||||
return this.constructor.name },
|
||||
|
||||
// XXX BUG? is this an error that with this everything seems to work
|
||||
// while without this instances of this work fine while instances
|
||||
// of "sub-classes" do not set the .stack correctly???
|
||||
// ...is this a JS quirk or am I missing something???
|
||||
__new__: function(context, ...args){
|
||||
return Reflect.construct(module.Error.__proto__, args, this.constructor) },
|
||||
//return Reflect.construct(Error, args, this.constructor) },
|
||||
})
|
||||
// XXX BUG? is this an error that with this everything seems
|
||||
// to work while without this instances of this work
|
||||
// fine while instances of "sub-classes" do not set
|
||||
// the .stack correctly???
|
||||
// ...is this a JS quirk or am I missing something???
|
||||
__new__: function(context, ...args){
|
||||
return Reflect.construct(
|
||||
module.Error.__proto__, args, this.constructor) },
|
||||
//return Reflect.construct(Error, args, this.constructor) },
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Prototype chain content access...
|
||||
|
||||
BOOTSTRAP(function(){
|
||||
|
||||
// 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...
|
||||
// Get source objects for a prop/attr name...
|
||||
//
|
||||
// sources(obj, name)
|
||||
// sources(obj, name, callback)
|
||||
// -> list
|
||||
// -> []
|
||||
//
|
||||
// Get callables or objects defining .__call__ (special-case)
|
||||
// sources(obj, '__call__')
|
||||
// sources(obj, '__call__', callback)
|
||||
// -> list
|
||||
// -> []
|
||||
// -> iterator
|
||||
//
|
||||
// Get full chain...
|
||||
// sources(obj)
|
||||
// sources(obj, callback)
|
||||
// -> list
|
||||
// -> iterator
|
||||
//
|
||||
//
|
||||
// callback(obj, i)
|
||||
// -> 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.
|
||||
// Get callables or objects defining .__call__ (special-case)
|
||||
// sources(obj, '__call__')
|
||||
// -> iterator
|
||||
//
|
||||
//
|
||||
// NOTE: this gos up the prototype chain, not caring about any role (
|
||||
// NOTE: this goes up the prototype chain, not caring about any role (
|
||||
// instance/class or instance/prototype) bounderies and depends
|
||||
// only on the object given as the starting point.
|
||||
// It is possible to start the search from this, thus checking
|
||||
@ -413,96 +288,64 @@ BOOTSTRAP(function(){
|
||||
// NOTE: this will not trigger any props...
|
||||
var sources =
|
||||
module.sources =
|
||||
function(obj, name, callback){
|
||||
// get full chain...
|
||||
if(typeof(name) == 'function'){
|
||||
callback = name
|
||||
name = undefined
|
||||
}
|
||||
var i = 0
|
||||
var o
|
||||
var res = []
|
||||
function*(obj, name=undefined){
|
||||
while(obj != null){
|
||||
//if(obj.hasOwnProperty(name)){
|
||||
if(name === undefined
|
||||
|| obj.hasOwnProperty(name)
|
||||
|| (name == '__call__' && typeof(obj) == 'function')){
|
||||
// handle callback...
|
||||
o = callback
|
||||
&& callback(obj, i++)
|
||||
// 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() }
|
||||
|| obj.hasOwnProperty(name)
|
||||
|| (name == '__call__'
|
||||
&& typeof(obj) == 'function')){
|
||||
yield obj }
|
||||
obj = obj.__proto__ } }
|
||||
|
||||
|
||||
// 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...
|
||||
//
|
||||
// entries(obj, name)
|
||||
// -> iterator
|
||||
//
|
||||
// Get values...
|
||||
// values(obj, name)
|
||||
// values(obj, name, callback)
|
||||
// -> list
|
||||
// -> []
|
||||
//
|
||||
// Get propery descriptors...
|
||||
// values(obj, name, true)
|
||||
// values(obj, name, callback, true)
|
||||
// -> list
|
||||
// -> []
|
||||
//
|
||||
// callback(value/prop, obj)
|
||||
// -> STOP
|
||||
// -> STOP(value)
|
||||
// -> ..
|
||||
//
|
||||
// entries(obj, name, true)
|
||||
// -> iterator
|
||||
//
|
||||
//
|
||||
// Item format:
|
||||
// [
|
||||
// source,
|
||||
// value,
|
||||
// ]
|
||||
//
|
||||
//
|
||||
// Special case: name is given as '__call__'
|
||||
// This will return either the value the object if it is callable
|
||||
// or the value of .__call__ attribute...
|
||||
//
|
||||
//
|
||||
// NOTE: for more docs on the callback(..) see sources(..)
|
||||
var values =
|
||||
module.values =
|
||||
function(obj, name, callback, props){
|
||||
props = callback === true ?
|
||||
callback
|
||||
: props
|
||||
var _get = function(obj, name){
|
||||
return props ?
|
||||
// NOTE: for more docs see sources(..)
|
||||
var entries =
|
||||
module.entries =
|
||||
function*(obj, name, props=false){
|
||||
for(var obj of sources(obj, name)){
|
||||
yield [
|
||||
obj,
|
||||
props ?
|
||||
Object.getOwnPropertyDescriptor(obj, name)
|
||||
// handle callable instance...
|
||||
: !(name in obj)
|
||||
&& name == '__call__'
|
||||
&& typeof(obj) == 'function' ?
|
||||
obj
|
||||
// normal attr...
|
||||
: obj[name] }
|
||||
// wrap the callback if given...
|
||||
var c = typeof(callback) == 'function'
|
||||
&& function(obj, i){
|
||||
var val = _get(obj, name)
|
||||
var res = callback(val, obj, i)
|
||||
return res === module.STOP ?
|
||||
// wrap the expected stop result if the user did not do it...
|
||||
module.STOP(val)
|
||||
: res }
|
||||
return c ?
|
||||
// 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) }) }
|
||||
: obj[name],
|
||||
] }}
|
||||
|
||||
|
||||
// Get values/props set in source objects for a prop/attr name...
|
||||
//
|
||||
// NOTE: this is specialization of entries(..), see that for more info.
|
||||
var values =
|
||||
module.values =
|
||||
function*(obj, name, props=false){
|
||||
for(var [_, value] of entries(...arguments)){
|
||||
yield value }}
|
||||
|
||||
|
||||
// Find the next parent attribute in the prototype chain.
|
||||
@ -588,24 +431,22 @@ function(proto, name){
|
||||
if(name == ''){
|
||||
throw new Error('parent(..): need a method with non-empty .name') }
|
||||
// get first matching source...
|
||||
proto = sources(that, name,
|
||||
function(obj, i){
|
||||
// NOTE: the .hasOwnProperty(..) test is here so as
|
||||
// to skip the base callable when searching for
|
||||
// .__call__ that is returned as a special case
|
||||
// by sourcei(..) and this should have no effect
|
||||
// or other cases...
|
||||
// NOTE: this will only skip the root callable...
|
||||
return (i > 0 || obj.hasOwnProperty(name))
|
||||
&& obj[name] === proto
|
||||
&& module.STOP })
|
||||
.pop() }
|
||||
var i = 0
|
||||
for(var obj of sources(that, name)){
|
||||
// NOTE: the .hasOwnProperty(..) test is here so as
|
||||
// to skip the base callable when searching for
|
||||
// .__call__ that is returned as a special case
|
||||
// by source(..) and this should have no effect
|
||||
// or other cases...
|
||||
// NOTE: this will only skip the root callable...
|
||||
if((i++ > 0 || obj.hasOwnProperty(name))
|
||||
&& obj[name] === proto){
|
||||
proto = obj
|
||||
break }}}
|
||||
// get first source...
|
||||
var res = sources(proto, name,
|
||||
function(obj, i){
|
||||
return i == 1
|
||||
&& module.STOP })
|
||||
.pop()
|
||||
var res = sources(proto, name)
|
||||
res.next()
|
||||
res = res.next().value
|
||||
return !res ?
|
||||
undefined
|
||||
:(!(name in res) && typeof(res) == 'function') ?
|
||||
@ -624,11 +465,9 @@ var parentProperty =
|
||||
module.parentProperty =
|
||||
function(proto, name){
|
||||
// get second source...
|
||||
var res = sources(proto, name,
|
||||
function(obj, i){
|
||||
return i == 1
|
||||
&& module.STOP })
|
||||
.pop()
|
||||
var res = sources(proto, name)
|
||||
res.next()
|
||||
res = res.next().value
|
||||
return res ?
|
||||
// get next value...
|
||||
Object.getOwnPropertyDescriptor(res, name)
|
||||
@ -674,7 +513,7 @@ function(proto, name, that, ...args){
|
||||
var parentOf =
|
||||
module.parentOf =
|
||||
function(parent, child){
|
||||
return new Set(sources(child)).has(parent) }
|
||||
return new Set([...sources(child)]).has(parent) }
|
||||
|
||||
// Reverse of parentOf(..)
|
||||
var childOf =
|
||||
@ -711,7 +550,7 @@ function(func){
|
||||
: '__call__' in this ?
|
||||
this.__call__
|
||||
: this.__proto__)
|
||||
return module.normalizeIndent(f.toString(...args)) },
|
||||
return doc.normalizeIndent(f.toString(...args)) },
|
||||
enumerable: false,
|
||||
})
|
||||
return func }
|
||||
@ -955,28 +794,78 @@ function(context, constructor, ...args){
|
||||
// to be rooted in Function.
|
||||
// though the typeof(..) == 'function' will always work.
|
||||
// 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 =
|
||||
module.Constructor =
|
||||
// shorthand...
|
||||
module.C =
|
||||
function Constructor(name, a, b, c){
|
||||
var args = [...arguments].slice(1, 4)
|
||||
|
||||
// sanity check...
|
||||
if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){
|
||||
throw new Error(`Constructor(..): invalid name: "${name}"`) }
|
||||
|
||||
// parse args...
|
||||
var args = [...arguments].slice(1, 4)
|
||||
|
||||
// Constructor(name[[, constructor[, mixin]], proto])
|
||||
var proto = args.pop() || {}
|
||||
var constructor_proto = typeof(args[0]) == 'function' ?
|
||||
args.shift()
|
||||
: undefined
|
||||
var constructor_proto =
|
||||
typeof(args[0]) == 'function' ?
|
||||
args.shift()
|
||||
: undefined
|
||||
var constructor_mixin = args.pop()
|
||||
|
||||
// handle:
|
||||
// Constructor(name, constructor, ..)
|
||||
/* XXX EXPERIMENTAL...
|
||||
// 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
|
||||
// be more bulky and less readable...
|
||||
@ -1015,6 +904,8 @@ function Constructor(name, a, b, c){
|
||||
&& (proto.__proto__ = constructor_proto.prototype) }
|
||||
|
||||
// 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 */
|
||||
var _constructor = function Constructor(){
|
||||
// create raw instance...
|
||||
@ -1031,7 +922,9 @@ function Constructor(name, a, b, c){
|
||||
// Object.defineProperty(_constructor, 'name', { value: name })
|
||||
// because this does not affect the name displayed by the Chrome
|
||||
// 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
|
||||
// them a different offer ;)
|
||||
// NOTE: it is not possible to abstract this eval(..) into something
|
||||
@ -1044,6 +937,7 @@ function Constructor(name, a, b, c){
|
||||
_constructor
|
||||
.toString()
|
||||
.replace(/Constructor/g, name) +')'))
|
||||
//*/
|
||||
// set .toString(..)...
|
||||
// NOTE: this test is here to enable mixinFlat(..) to overwrite
|
||||
// .toString(..) below...
|
||||
@ -1065,7 +959,7 @@ function Constructor(name, a, b, c){
|
||||
.toString()
|
||||
.replace(/[^{]*{/, '{')
|
||||
: '{ .. }'
|
||||
return `${this.name}(${args})${module.normalizeIndent(code)}` },
|
||||
return `${this.name}(${args})${doc.normalizeIndent(code)}` },
|
||||
enumerable: false,
|
||||
})
|
||||
// set generic raw instance constructor...
|
||||
@ -1228,6 +1122,8 @@ function(base, ...objects){
|
||||
// NOTE: this will also match base...
|
||||
// NOTE: if base matches directly callback(..) will get undefined as parent
|
||||
// NOTE: for more docs on the callback(..) see sources(..)
|
||||
//
|
||||
// XXX should this be a generator???
|
||||
var mixins =
|
||||
module.mixins =
|
||||
function(base, object, callback){
|
||||
@ -1275,11 +1171,9 @@ function(base, object){
|
||||
mixins(base, object, function(){ return module.STOP })
|
||||
.length > 0
|
||||
// flat mixin search...
|
||||
|| sources(base, function(p){
|
||||
return matchPartial(p, object) ?
|
||||
module.STOP
|
||||
: [] })
|
||||
.length > 0 )}
|
||||
|| [...sources(base)]
|
||||
.some(function(p){
|
||||
return matchPartial(p, object) }) )}
|
||||
|
||||
|
||||
// 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