oda-framework
Version:
959 lines (939 loc) • 42.1 kB
JavaScript
if (!globalThis.ROCKS) {
if (typeof requestAnimationFrame === 'undefined')
globalThis.requestAnimationFrame = setTimeout;
const _addEventListener = EventTarget.prototype.addEventListener
const _removeEventListener = EventTarget.prototype.removeEventListener;
let _curId = 0;
function incrementId(){
return ++_curId;
}
Object.defineProperties(EventTarget.prototype, {
addEventListener: {
configurable: true,
enumerable: true,
writable: true,
value: function addEventListener(event, handler, ...args) {
if (!this.__events) {
this.__events = new Map();
}
let /**@type {Set<Function>} */ handlers = this.__events.get(event);
if (!handlers) {
handlers = new Set;
this.__events.set(event, handlers);
}
handlers.add(handler);
return _addEventListener.call(this, event, handler, ...args)
}
},
removeEventListener: {
configurable: true,
enumerable: true,
writable: true,
value: function removeEventListener(event, handler, ...args) {
if (this.__events && this.__events.has(event)) {
const /**@type {Set<Function>} */ handlers = this.__events.get(event);
const curHandler = Array.from(handlers).find(h => h === handler);
if (curHandler) {
handlers.delete(curHandler);
}
if (!handlers.size)
this.__events.delete(event);
}
return _removeEventListener.call(this, event, handler, ...args)
}
},
})
const OBS_PREFIX = '__obs__';
const ID_KEY = Symbol('__id__')
globalThis.CORE_KEY = Symbol('core');
const THROTTLES = Symbol('THROTTLES');
globalThis.ROCKS = function (prototype){
const rocks = class extends (this || EventTarget) {
constructor() {
super(...arguments);
this[ID_KEY] = incrementId();
if (Object.keys(this.constructor.__rocks__.prototype.$observers).length){
this.async(()=>{
for(let obs in this.constructor.__rocks__.prototype.$observers){
this[OBS_PREFIX+obs];
}
})
}
}
toJSON() {
const props = Object.values(this.constructor.__rocks__.descrs).filter(i => i.enumerable);
const result = props.reduce((res, i) => {
const val = this[i.name];
res[i.name] = this[i.name];
return res;
}, {})
return result;
}
[CORE_KEY] = {
events: new Map(),
callbacks: new Map(),
attributes: new Map(),
debounces: new Map(),
}
async(handler, delay = 0) {
if (typeof handler === 'string')
handler = this[handler].bind(this);
// this.throttle('', handler, delay)
const fn = (delay? setTimeout : requestAnimationFrame) || setTimeout;
return fn(()=>{
handler();
}, delay)
}
debounce(key, handler, delay = 0) {
if (typeof handler === 'string')
handler = this[handler].bind(this);
key += '.' + delay;
let db = this[CORE_KEY].debounces.get(key);
if (db){
const clr = (delay ? clearTimeout : cancelAnimationFrame) || clearTimeout;
clr(db);
}
const fn = (delay ? setTimeout : requestAnimationFrame) || setTimeout;
const t = fn(() => {
this[CORE_KEY].debounces.delete(key);
handler();
}, delay);
this[CORE_KEY].debounces.set(key, t);
}
throttle(key, handler, delay = 0) {
this[THROTTLES] ??= Object.create(null);
key += '.' + delay;
if (this[THROTTLES][key]){
this[THROTTLES][key] = handler;
}
else{
this[THROTTLES][key] = handler;
const fn = (delay ? setTimeout : queueMicrotask) || setTimeout;
fn(() => {
this[THROTTLES][key]();
this[THROTTLES][key] = undefined;
}, delay);
}
}
listen(event, callback, props = { target: this, once: false, useCapture: false }) {
if (typeof callback === 'string')
callback = this[callback];
if (!callback) throw new Error (`Undefined event handler for "${event}"`);
if(this[CORE_KEY].callbacks.has(callback))
callback = this[CORE_KEY].callbacks.get(callback)
else
this[CORE_KEY].callbacks.set(callback, (callback = callback.bind(this)))
if (!this[CORE_KEY].events.has(props.target)) {
this[CORE_KEY].events.set(props.target, {});
}
const handlers = this[CORE_KEY].events.get(props.target)[event] ??= [];
if (handlers.includes(callback)) return;
if (!props.once)
handlers.push(callback);
(props.target || this).addEventListener?.(event, callback, props);
}
unlisten(event, callback, props = { target: this }) {
const handlers = this[CORE_KEY].events.get(props.target)?.[event];
if (!handlers) return;
if (typeof callback === 'string')
callback = this[callback];
callback = this[CORE_KEY].callbacks.get(callback)
if (callback){
const idx = handlers.indexOf(callback)
if (~idx) handlers.splice(idx, 1);
(props.target || this).removeEventListener?.(event, callback);
}
else{
while (callback = handlers.pop()){
(props.target || this).removeEventListener?.(event, callback);
}
}
}
fire(event, value){
if (!this.__events?.has(event)) return;
const ev = new CustomEvent(event, { detail: { value, target: this }, composed: true });
this.dispatchEvent(ev);
}
$super(name, ...args) {
// rocks class rocks class
let proto = super.__proto__.__proto__;
const myDs = Object.getOwnPropertyDescriptor(this.__proto__.__proto__, name);
let ds;
while (!ds && proto) {
ds = Object.getOwnPropertyDescriptor(proto, name);
if (ds){
if(ds.set?.setter && args.length && ds.set?.setter !== myDs.set?.setter )
return ds.set.setter.call(this, ...args);
if (ds.get?.getter && ds.get?.getter !== myDs.get?.getter )
return ds.get.getter.call(this);
if (typeof ds.value === "function" && ds.value !== myDs.value)
return ds.value.call(this, ...args);
ds = null;
}
proto = proto.__proto__;
}
}
static get name() {
let parent = this.prototype.__proto__.constructor
let name = parent.name;
while (name === 'rocks') {
parent = parent.prototype.__proto__.constructor
name = parent.name
}
return name;
}
}
const descrs = {}
prototype.extends = str2arr(prototype.extends);
prototype.extends.forEach(ext => {
joinProps.call(this, descrs, prototype, ext.__rocks__);
})
joinProps.call(this, descrs, prototype, this?.__rocks__)
if (prototype.$observers) {
for (const name in prototype.$observers) {
let func = prototype.$observers[name]
let expr;
let args;
if (Array.isArray(func) || typeof func === 'string') {
args = str2arr(func);
func = prototype[name] || descrs[name]?.value;
}
if (!args) {
expr = func.toString();
expr = expr.replace(/(\/\*[\w\s\r\n\*]*\*\/)|(\/\/.*(?=[\r\n]))/mg, ''); //remove comments
const argsStart = expr.indexOf('(') + 1;
const argsEnd = expr.indexOf(')', argsStart);
args = str2arr(expr.slice(argsStart, argsEnd));
}
const checkArgs = args.map(s => s.split('=')[0].trim()).map(a => a.split('.')[0].replace('?', ''));
const obsName = `${OBS_PREFIX}${name}`
prototype[obsName] = {
get() {
checkArgs.forEach(a => {
if (!(a in this)) {
let message = `undefined property "${a}" in ${prototype.is ? `<${prototype.is}>` : this.constructor.name} for observer ${name}`;
let host = this.domHost;
while (host){
if (a in host){
message += `
But this problem can be fixed by set
the property modifier $pdp: true
in the <${host.localName}>`;
break;
}
host = host.domHost;
}
throw new Error(message);
}
})
const props = args.map(a => this[a]);
if (props.every(p => p !== undefined)) {
this.debounce(obsName, () => {
this[name](...props);
})
}
return true;
}
};
if (func) prototype[name] = func;
}
}
joinProps.call(this, descrs, prototype, {descrs: convertor(prototype)}, true)
Object.defineProperty(rocks, '__rocks__', {configurable: false, value: {descrs, prototype}})
try{
Object.defineProperties(rocks.prototype, rocks.__rocks__.descrs);
}
catch (e){
switch (e.name){
case 'TypeError':{
const err = Object.values(rocks.__rocks__.descrs).find(i=>{
return i.value && (i.get || i.set || i.writable)
})
if (err)
throw new Error(e.name +' on "' + err.name+'" property: '+e.message)
else
throw e;
} break;
}
}
return rocks;
}
const ROCKS = globalThis.ROCKS;
Object.defineProperty(Object.__proto__, 'ROCKS', {enumerable: false, configurable: false, value: ROCKS})
function joinProps(descrs, proto, source, last = false){
for (let d in source?.descrs){
if (PROTOTYPE_SPECIAL_GROUPS.includes(d))
continue;
if (!descrs[d] || descrs[d].configurable) {
if (!last || !descrs[d]){
descrs[d] ??= Object.create(null);
}
else if (source.descrs[d]?.$def){
descrs[d].getter = undefined;
}
Object.assign(descrs[d], source.descrs[d]);
continue;
}
throw new Error(`Cannot override protected property "${d}"`); //todo указывать первоисточник где protected
}
for (let p of PROTOTYPE_SPECIAL_GROUPS)
proto[p] = Object.assign({}, source?.prototype?.[p] || {}, proto[p] || {})
}
function str2arr(str) {
if (typeof str === 'string') {
str = str.split(',').map(s => s.trim());
}
if (Array.isArray(str)) {
str = str.filter(Boolean);
}
if (!str) {
str = [];
}
return str;
}
globalThis.str2arr = str2arr;
const KEY = Symbol.for('ROCKS-PROPS');
const IS_ROCKS_PROXY = Symbol.for('IS_ROCKS_PROXY');
ROCKS.KEY = KEY;
ROCKS.IS_ROCKS_PROXY = IS_ROCKS_PROXY;
function reactor(target) {
if (!Object.isExtensible(target) || (target.constructor !== Object && target.constructor !== Array ) || target?.constructor === Promise)
return target;
if (target[Symbol.for('IS_DATA_PROXY')]) {
return target[Symbol.for('DATA')].proxy;
}
let op = target[KEY];
if (op){
op.hosts.add(this);
return op.proxy;
}
op = Object.create(null);
const handlers = {
get: (target, key, resolver) => {
if (typeof key === 'symbol') {
switch (key) {
case KEY: return op;
case IS_ROCKS_PROXY: return true;
}
}
let val = op.target[key];
if (val){
if (op === val || op.target === val || key.constructor === Symbol || (val instanceof Object && val.constructor?.prototype === val) || val instanceof Function)
return val;
}
const $prop = op.props[key] ??= {deps: new Set()}
$prop.deps.add(ROCKS.DEP_TARGET || target);
return reactor.call(this, val);
},
set: (target, key, value) => {
const old = (Array.isArray(op.target) && key === 'length')?undefined:op.target[key];
if (Object.equal(old, value)) return true;
const $prop = op.props[key] ??= { deps: new Set() }
$prop.deps.add(ROCKS.DEP_TARGET || target);
op.target[key] = reactor.call(this, value);
ROCKS.resetDeps($prop);
return true;
},
deleteProperty: (target, key) => {
const $prop = op.props[key] ??= { deps: new Set() }
op.target[key] = reactor.call(this, undefined);
delete op.target[key];
ROCKS.resetDeps($prop);
return true;
}
};
const proxy = new Proxy(op?.target || target, handlers);
if (!target[KEY]) {
op.target = target;
op.hosts = new Set();
op.proxy = proxy;
op.props = Object.create(null);
Object.defineProperty(target, KEY, {
enumerable: false,
configurable: false,
value: op
});
}
op.hosts.add(this);
return proxy;
}
ROCKS.reactor = reactor;
Object.equal ??= function (a, b, recurse) {
if (a === b) return true;
if (!(a instanceof Object) || !(b instanceof Object))
return false;
if(a?.[Symbol.for('IS_DATA_PROXY')]){
if(a[Symbol.for('DATA')] === b[Symbol.for('DATA')])
return true;
}
else{
if ((a?.[KEY] || a) === (b?.[KEY] || b))
return true;
}
if (a instanceof Function && a.constructor === b.constructor)
return a.toString() === b.toString();
if (a instanceof Date && a.constructor === b.constructor) {
return a.valueOf() === b.valueOf();
}
if (recurse) {
try{
const join = Object.assign({}, a, b)
for (let key in join)
if (!Object.equal(b[key], a[key], recurse)) return false;
return true;
}
catch (e){
}
}
return false;
};
function isObject(obj) {
return obj && typeof obj === 'object';
}
function isNativeObject(obj) {
return obj && (obj.constructor === Object);
}
function convertor(proto, decor, result){
const descriptors = Object.getOwnPropertyDescriptors(proto);
result ??= Object.create(null);
for (let name in descriptors){
if (PROTOTYPE_RESERVED_WORDS.includes(name))
continue;
if (decor && PROTOTYPE_SPECIAL_GROUPS.includes(name))
continue;
if (decor && PROPERTY_ATTRIBUTES.includes(name))
continue;
const d = descriptors[name];
let prop = Object.create(null);
switch (typeof d.value){
case 'object':{
if(d.value === null){
prop.$type = Object;
prop.$def = null;
}
else if (Array.isArray(d.value)){
prop.$type = Array;
prop.$def = d.value;
}
else{
const attrs = Object.keys(d.value);
if (attrs.some(i=>PROPERTY_SIGNS.includes(i))) { // it is property
for (let attr of attrs){
if (!PROPERTY_ATTRIBUTES.includes(attr) && !PROPERTY_SIGNS.includes(attr)){
throw new Error(`Unknown attribute "${attr}" in description for property "${name}".`)
}
prop[attr] ??= d.value[attr];
}
if (!prop.$type && prop.$def !== undefined){
switch (typeof prop.$def){
case 'object':{
if (Array.isArray(prop.$def))
prop.$type = Array;
else if(prop.$def === null)
prop.$type = Object;
else{
if(prop.$def.__proto__?.constructor === Object){
let isStructure = false;
for (let n in prop.$def){
const obj = prop.$def[n]
if (obj?.constructor === Object){
if (Object.keys(obj).some(i=>PROPERTY_SIGNS.includes(i))){
obj.$public ??= true;
isStructure = true;
}
}
}
if (isStructure){
class RocksObject extends ROCKS(prop.$def){
constructor(parent) {
super();
this.$parent = parent;
}
}
prop.$def = function (){
return new RocksObject(this)
}
}
}
prop.$type = prop.$def.__proto__?.constructor || Object;
}
} break;
default:{
prop.$type = prop.$def.__proto__.constructor;
}
}
}
}
else if (ROCKS.PROPERTY_FLAGS.includes(name)){
for (let attr of attrs){
if (ROCKS.PROPERTY_FLAGS.includes(attr)){
prop[attr] ??= d.value[attr];
}
}
prop[name] = true;
convertor(d.value, prop, result);
continue;
}
else if (attrs.some(i => PROPERTY_ATTRIBUTES.includes(i))) { // it is group
for (let attr of attrs){
if (PROPERTY_ATTRIBUTES.includes(attr)){
prop[attr] ??= d.value[attr];
}
}
prop.$group ??= name;
convertor(d.value, prop, result);
continue;
}
else{
prop.$type = d.value.__proto__?.constructor || Object;
prop.$def = d.value;
}
}
} break;
case 'function':{
if (PROPERTY_TYPES.includes(d.value.name))
prop.$type = globalThis[d.value.name];
else
prop.value = d.value;
} break;
case 'undefined':{
if (d.get)
prop.get = d.get;
if (d.set)
prop.set = d.set;
} break;
default:{
prop.$type = d.value.__proto__.constructor;
prop.$def = d.value;
}
}
if (name in result)
throw new Error(`${prop.value?'Method':'Property'} "${name}" redeclared.`);
prop = Object.assign(prop, decor || {})
prop.name = name;
if (name.startsWith(OBS_PREFIX)){
prop.isObserver = true;
}
if (prop.$attr){
if (prop.$attr === true)
prop.$attr = name.toKebabCase();
}
if (!prop.value){
if (prop.$def !== undefined){
const def = prop.$def;
switch (prop.$type) {
case Object: {
prop.$def = function () { return def?Object.assign({}, def):def};
} break;
case Array: {
prop.$def = function () { return Array.from(def)};
} break;
default:{
if (typeof prop.$def !== 'function') {
if (def !== undefined)
prop.$def = function () {
return def
};
}
}
}
}
const typeFunc = getTypeConverter(prop.$type);
prop.toType = (val)=>{
return val === undefined?val:typeFunc(val);
}
prop.event = name.toKebabCase()+'-changed';
const key = '#'+name;
if ('get' in prop)
prop.getter = prop.get;
prop.get = function (){
let $prop = joinPropDescriptors.call(this, name);
if (this === this.constructor.prototype)
return $prop.$def?.call(this);
let val = this[key];
if (val === undefined){
if($prop.getter){
$prop.depTarget = ROCKS.DEP_TARGET;
ROCKS.DEP_TARGET = $prop;
val = $prop.getter.call(this);
if ($prop.$type && val?.then && $prop.$type !== Promise){
if (val.result === undefined){
setTimeout(resetDepTarget);
const prom = val;
prom.then(res=>{
// if (ROCKS.DEP_TARGET === $prop)
//
// resetDepTarget();
prom.result = this[key] = res;
if (!$prop.$freeze)
ROCKS.resetDeps($prop);
})
.catch(error=>{
console.warn(error)
prom.result = this[key] = $prop.$def?.() || null;
if (!$prop.$freeze)
ROCKS.resetDeps($prop);
})
}
else {
val = this[key] = val.result;
}
}
ROCKS.DEP_TARGET = $prop.depTarget;
}
if (!(key in this)){
const def = $prop.$def?.call(this);
if($prop.$save && this.$loadPropValue){
let saved = this.$loadPropValue(name);
if (saved !== undefined && def !== saved){
$prop.old = val; //todo подумать
val = saved;
$prop.setter?.call(this, val, $prop.old);
}
}
if (def !== undefined && (val === undefined || Number.isNaN(val))) {
val = def;
}
if (!$prop.$freeze)
val = reactor.call(this, val);
val = $prop.toType(val)
$prop.old = this[key] = val;
this.fire($prop.event, val);
// this.$notify?.($prop, val);
}
if (Object.equal(val, $prop.old)){
this[key] = $prop.old;
// this.$notify?.($prop, val);
}
else if (!(val instanceof Promise)){
if (!$prop.$freeze)
val = reactor.call(this, val);
val = $prop.toType(val)
$prop.old = this[key] = val;
if (!$prop.$freeze)
ROCKS.resetDeps($prop);
if($prop.$public)
this.fire($prop.event, val);
}
else
$prop.old = this[key] = val;
this.$notify?.($prop, val);
if (Array.isArray(val))
val?.length; //todo никогда не удалять
}
if (!$prop.isObserver)
$prop.deps.add(ROCKS.DEP_TARGET || this);
return $prop.toType(val);
}
// нужно в $supper
prop.get.getter = prop.getter;
if ('set' in prop)
prop.setter = prop.set;
prop.set = function (val){
let $prop = joinPropDescriptors.call(this, name);
if (!$prop.setter && $prop.$readOnly && this.constructor.prototype !== this)
throw new Error('Read only!!! ' + name);
val = $prop.toType(val);
$prop.old = this[key];
if ($prop.old === undefined && !(key in this))
$prop.old = $prop.$def?.call(this);
if (Object.equal(val, $prop.old)) return;
if (!$prop.$freeze)
val = reactor.call(this, val);
this[key] = val;
$prop.setter?.call(this, val, $prop.old);
if($prop.$save && this.$savePropValue)
this.$savePropValue(name, val);
if (!$prop.$freeze)
ROCKS.resetDeps($prop);
this.fire($prop.event, val);
this.$notify?.($prop, val);
}
// нужно в $supper
prop.set.setter = prop.setter;
}
else{
if (name.startsWith('__'))
prop.$final = true;
else if (!name.startsWith('_')){
prop.$public = true;
prop.$pdp = true;
}
}
prop.enumerable = prop.$public || prop.$pdp || false;
prop.configurable = !prop.$final;
result[name] = prop;
}
return result;
}
function resetDepTarget(){
ROCKS.DEP_TARGET = null;
}
function joinPropDescriptors(name){
const prop = this.constructor.__rocks__.descrs[name];
prop.hosts ??= new WeakMap();
let res = prop.hosts.get(this);
if (!res){
res = Object.assign({host: this, key: '#'+name, deps: new Set()}, prop);
prop.hosts.set(this, res);
}
return res;
}
ROCKS.resetDeps = ($prop, stack = new WeakSet) =>{
for (let p of $prop.deps || []){
if (p === $prop || p.$freeze)
continue;
if (p.host && typeof p.host === 'object') // todo опасно привязываться к хосту
p.host[p.key] = undefined;
if (stack.has(p)) continue;
stack.add(p);
ROCKS.resetDeps(p, stack);
}
if (($prop.isObserver || $prop.$attr) && $prop.host[$prop.key] === undefined){
$prop.host.throttle($prop.name, ()=>{
$prop.host[$prop.name];
})
}
if ($prop?.$render){
$prop.throttle('$render', ()=> {
$prop.$render();
})
}
else{
for (let h of $prop[KEY]?.hosts || []){
if (h?.throttle && h?.$render){
h.throttle('$render', ()=>{
h.$render();
})
}
}
}
}
function toDate(v){return new Date(v)}
function toString(v){return v?.toString() || ''}
function toNumber(v){return (v !== undefined)?Number(v):undefined}
function toBigInt(v) {
if( typeof v === 'bigint' )
return v;
if( typeof v === 'string' ) {
const val = /^[\-\+]?[0-9]+/.exec(v);
return val===null ? undefined : BigInt(val[0]);
}
const val = Math.round( Number(v) );
return isFinite(val) ? BigInt(val): undefined;
}
const toBool = globalThis.toBool = (v, def = false) => {
if (v === undefined || v === null)
return def;
switch (typeof v) {
case 'object': return true;
case 'string': return v.toLowerCase() === 'true';
case 'boolean': return v;
case 'number': return v !== 0;
case 'bigint': return v !== 0n;
}
return false;
}
function getTypeConverter(type){
switch (type) {
case Boolean: return toBool;
case Number: return toNumber;
case String: return toString;
case Date: return toDate;
case BigInt: return toBigInt;
}
return (val)=>{
return val;
}
}
// let DEP_TARGET;
const PROTOTYPE_RESERVED_WORDS = ['is', 'imports', 'extends'];
ROCKS.PROPERTY_FLAGS = ['$save', '$final', '$public', '$pdp', '$readOnly', '$freeze', '$group'];
const PROTOTYPE_SPECIAL_GROUPS = ['$listeners', '$observers', '$keyBindings', '$innerEvents'];
const PROPERTY_TYPES = ['Boolean', 'Number', 'String', 'Array', 'Object', 'Date', 'BigInt'];
const PROPERTY_SIGNS = ['$def', '$type', 'get', 'set'];
const PROPERTY_ATTRIBUTES = [...ROCKS.PROPERTY_FLAGS, '$label', '$list', '$multiSelect', '$attr', '$hidden', '$editor', '$description'];
Array:{
const push = Array.prototype.push;
Array.prototype.push = function (...item){
const res = push.call(this, ...item);
let deps = this[ROCKS.KEY]?.props?.length;
if(deps)
ROCKS.resetDeps(deps);
return res;
}
const splice = Array.prototype.splice;
Array.prototype.splice = function (...item){
const res = splice.call(this, ...item);
let deps = this[ROCKS.KEY]?.props?.length;
if(deps)
ROCKS.resetDeps(deps);
return res;
}
Object.defineProperty(Array.prototype, 'has', {
enumerable: false, configurable: true, value: Array.prototype.includes
});
Object.defineProperty(Array.prototype, 'clear', {
enumerable: false, configurable: true, value: function () {
this.splice(0);
}
});
Object.defineProperty(Array.prototype, 'last', {
enumerable: false, configurable: true, get() {
return this[this.length - 1];
}
});
Object.defineProperty(Array.prototype, 'first', {
enumerable: false, configurable: true, get() {
return this[0];
}
});
Object.defineProperty(Array.prototype, 'add', {
enumerable: false, configurable: true, value: function (...item) {
let index = -1;
for (let i of item) {
index = this.indexOf(i);
if (index>-1) continue;
index = this.push(i);
index--;
}
return index;
}
});
Object.defineProperty(Array.prototype, 'remove', {
enumerable: false, configurable: true, value: function (...items) {
for (const item of items) {
let idx = this.indexOf(item);
if (~idx)
this.splice(idx, 1);
}
}
});
Object.defineProperty(Array.prototype, 'swap', {
enumerable: false, configurable: true,
value: function (i1, i2) {
return [this[i1], this[i2]] = [this[i2], this[i1]];
}
});
Object.defineProperty(Array.prototype, 'sum', {
enumerable: false, configurable: true,
value: function () {
return this.reduce((r,v)=>r + (v || 0), 0);
}
});
Object.defineProperty(Array.prototype, 'mul', {
enumerable: false, configurable: true,
value: function () {
return !this.length?0:this.reduce((r,v)=>r * (v || 0), 1);
}
});
Object.defineProperty(Array.prototype, 'avg', {
enumerable: false, configurable: true,
value: function () {
return this.reduce((r,v)=>r + (v || 0), 0)/(this.length || 1);
}
});
Object.defineProperty(Array.prototype, 'mean', {
enumerable: false, configurable: true,
value: function () {
return this.reduce((r,v)=>r + (v || 0), 0)/(this.length || 1);
}
});
Object.defineProperty(Array.prototype, 'unique', {
enumerable: false, configurable: true,
value: function () {
return this.filter((v,i,items) => items.indexOf(v) === i);
}
});
Object.defineProperty(Array.prototype, 'uniqueObject', {
enumerable: false, configurable: true,
value: function () {
return this.filter((v,i,items) => items.indexOf(v) === i || typeof v !== 'object');
}
});
globalThis.AsyncPromise = class AsyncPromise{
constructor(handler) {
return new Promise((resolve, reject)=>{
queueMicrotask(async ()=>{
try{
resolve(await handler());
}
catch (e){
reject(e);
}
})
})
}
}
}
String:{
const kebabGlossary = Object.create(null);
function toKebab(str) {
return kebabGlossary[str] ??= str.replace(/\B([A-Z])/g, '-$1').toLowerCase();
}
if (!String.toKebabCase) {
Object.defineProperty(String.prototype, 'toKebabCase', {
enumerable: false, value: function () {
return toKebab(this.toString());
}
});
}
const camelGlossary = Object.create(null);
function toCamel(str) {
return camelGlossary[str] ??= str.replace(/-(\w)/g, function (_, c) { return c ? c.toUpperCase() : '' })
}
if (!String.toCamelCase) {
Object.defineProperty(String.prototype, 'toCamelCase', {
enumerable: false, value: function () {
return toCamel(this.toString());
}
});
}
const capitalGlossary = Object.create(null);
function toCapital(str) {
if (!str) return '';
return capitalGlossary[str] ??= str[0].toUpperCase() + str.slice(1);
}
if (!String.toCapitalCase) {
Object.defineProperty(String.prototype, 'toCapitalCase', {
enumerable: false, value: function () {
return toCapital(this.toString());
}
});
}
if (!String.toQName) {
Object.defineProperty(String.prototype, 'toQName', {
enumerable: false, value: function () {
return this.toLowerCase().split(' ')
.map((s, i) => {
if (i === 0) return (s === 'the') ? '' : s;
return s;
})
.join('-')
.replace(/-{2,}/g, '-')
.replace(/(^\d)/, '_$1')
.replace(/\./g, '');
}
})
}
if (!String.prototype.hashCode) {
const cyrb53 = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for(let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
String.prototype.hashCode = function (seed) {
return cyrb53(this, seed);
}
}
}
}
export default globalThis.ROCKS;