function atomise({
mode = 'BFS', depth = 5, slots = 2 ** 15,
dist = false, cycle = false , tree = false, split = 2,
query = (``), filter = (k,c,p) => true, take = Infinity, separator = '.', meta,
format = (v)=> v , parse = (v) => v, leaves = null, prefix = '@', ... rest } = {}) {
let iters = -1;
let object = rest;
let outer = [], queue = [];
let t1 = performance.now();
let maxDepth = depth;
let prior = new Set(),
cache = new Map(),
nodes = new Map(),
types = new Map();
let its = (prop) => Object.prototype.toString.call(prop);
let any = (prop, ...types) => types.some((type) => its(prop) === its(type));
let not = (prop, ...types) => !any(prop,...types) ;
let see = (prop) => ext(prop) ? 'Class' : its(prop).slice(1,-1).split(' ')[1]
let ext = (prop) => its(prop).includes('Func') && Function.prototype.toString.call(prop).startsWith('class')
let end = (prop) => leaves === null ? ~leaves : leaves ^ typeof prop === typeof {};
let bin = (item) => Object.fromEntries(Object.entries(item).map(d=>[d[0],'']));
let get = (path) => path.slice(1).reduce((path,item)=> path && path[item] || null,
(object))
let sum = (list, all = {}) => {
if ( list.length ) {
for ( let [ key, val ] of list)
{ let type = types.get(val) ?? see(val?.value || val);
types.set(val,type); all[type.concat('s')] = (all[type.concat('s')] || 0) + 1}}
return [all,list.length] }
/* ---------------- */
let sep = (types, left = '', right = '') => {
for ( let [key,val] of types)
{
if ( key.length > split && !isFinite(key)) {
left += key + '|' } else { right += key + '|' }}
return [ left.slice(0,-1), right.slice(0,-1) ] }
/* ---------------- */
let add = (item) => {
let node = document.createElement(item.own);
Object.defineProperty(node,'out',{
get:()=>decodeURI(format(node.outerHTML))})
Object.entries(item)
.filter(([key, val], i, f) => ["key", "cid","sub","dim","sig","all","ext"].includes(key) && val)
.forEach(([key, val]) => any(val, Object())
? Object.entries(val).forEach(([key,val])=> node.setAttribute(key,val))
: node.setAttribute(key, val));
if(not(item.val, Array(),Object(),Symbol())) { node.setAttribute('content', encodeURI(item.val.toString().replaceAll(/\n+/g,'')))}
return node };
/* ---------------- */
function getProp(object, depth = 0) {
let chain = [], forward; queue.push([object, chain, depth]);
switch (mode) {
case "BFS": forward = true , mode = "shift" ; break;
case "DFS": forward = false , mode = "pop" ; break;}
while ( queue.length )
{[ object , chain , depth ] = queue[mode](); // console.log(chain);
for ( let [ key, val ] of forward ? spy(object) : spy(object).reverse().sort((a,b)=>parseInt(a[0]) - parseInt(b[0]))) { ++iters;
if ( outer.length > take - 1 )
continue
let own = types.get(val) ?? see(val) , set, sig, all, dim, child ; types.set(val,own);
let entry = { key, own, val,
cid: (val?.name ?? val?.constructor?.name)?.toString().replace // escape reserved rgx chars
(new RegExp(`^${[own,key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')].join('|')}$`),''),
set, sig, sub:depth, dim, pad:chain,all };
if( typeof meta == 'function' )
{ Object.assign(entry, { ext:meta({...entry},iters)}) }
Object.defineProperties(entry , {
parse: { value:()=>not(val, Object(),Symbol()) ? parse(val.toString()) : {}},
path: { get:()=>(prop)=>entry.pad.map(d=>d[prop||'key'])}})
if ( dist ) {
let counts = cache.get(val)
if( counts === undefined )
{ counts = spy(val); [ set , sig , all, dim ] = sep(counts).concat(sum(counts));
cache.set(val , [ set , sig , all, dim ])} else {[ set , sig , all , dim ] = counts};
Object.assign(entry, { set , sig , all, dim })}
if ( tree || query.length > 0 ) {
let parentPath = chain.map(d=>d.key),
childsPath = parentPath.concat(key),
parent = nodes.get(parentPath.join(separator)) ;
child = nodes.get(childsPath.join(separator)) ?? add(entry);
nodes.set(childsPath.join(separator),child)
parent?.appendChild(child) }
if ( filter(key, entry, chain.at(-1) ?? bin(entry)) && end(val))
{ outer.push( Object.defineProperties(
{ [[prefix].concat(entry.path()).join(separator)]: key, above:chain.at(-1), below:entry, chain, child, iters,depth },
{ out: { get:()=>child.out}, path:{ get:()=>entry.path }}))
if ( tree || query.length > 0 ){ child.setAttribute('include', true)}}
if ((typeof val == 'object' ? prior.has(val) : prior.has(key)) && !cycle || prior.size > slots - 1 )
{ continue }
if ((typeof val == 'object' || typeof val == 'function') && depth < maxDepth)
{ prior.add(val); queue.push([val, [...chain, entry], depth + 1]) }
else { prior.add(key); }
}
}
}
getProp(object);
let root = nodes.values().next().value;
if ( query.length > 0 ) { let result = root.querySelectorAll(query);
for ( let i = 0; i < result.length; ++i)
{ result[i].setAttribute('select', true) }
outer = outer.filter(d=>{
let inc = d.child.getAttribute('select') ;
return inc })}
Object.defineProperties(outer,{
ticks: { get:()=> performance.now() - t1 },
root: { get:()=> root },
crop: { get:()=>
{ nodes.forEach(node=> {
if (!node.getAttribute('include') || (!node.getAttribute('select') && query.length > 0) )
{ node.replaceWith(...node.children) }
else { node.removeAttribute('include');
node.removeAttribute('select') }});
root.removeAttribute('include');root.removeAttribute('select');
return root?.out }},
out: { get:()=> root?.out }
})
return outer
}