depend.js
var RB = {};
/**
* This allows you to declare a class in Javascript, list events on an HTML node, and have functionality from the JS class automatically wired to the node.
*
*
*
* The basic functionality is:
* - Extend RB.DomWire with your JS class
* - To auto-wire call YourClassName.autowire();
* - AutoWiring is done when the page is finished loading, NOT at the time you call autowire()
* - By Default looks for rb-YourNamespacedClassName on DOM Elements
* - To customize, you may:
* - call YourClassName.autowire('DomeClass');
* - declare YourClassName.querySelector = 'DomClass';
* - declare YourClassName.getquerySelector = function(){return 'DomClass';};
* - NOTE: Precedence is given to autoWire('DomClass'), then YourClassName.querySelector, then YourClassName.getquerySelector()
* - Set Event methods in YourClass corresponding to JS Event Listeners.
* - Inside your class, set methods like `onclick(event)`, `mouseover`, `mouseout`, etc & they will be auto-wired to Dom Nodes
* - Ex: class YourClass extends RB.DomWire { onclick(event){console.log(this); console.log(event); console.log(this.node)};
* - `this` refers to the instance of YourClass
* - `this.node` refers to the node which the listener was set on
* - `event` refers to the javascript event that was triggered
* - `event.target` may be different from `this.node` due to event propagation
* - Methods are assigned using theNode.addEventListener.
* - You may declare this.eventMode = 'static' in your __construct() method to use theNode.onclick = YourClass.onclick;
* - You are encouraged to override the methods __construct() && __attach() in your subclass. The super method does not do anything
* - __construct() is called before attach()
* - __attach() is called AFTER attach()
* - You may override constructor() && attach() but it is NOT recommended & super method will need to be called
* - Set any additional methods and paramaters that you like,
* - except for
* params: node, eventNode
* methods: getChildMethods, attachMethod (as well as the methods mentioned above)
*
*/
RB.DomWire = class {
constructor(node){
RB.DomWire.list.push({'node':node,'obj':this});
this.name = this.name ?? this.constructor.name;
this.eventMode = 'addEventListener';
this.node = node;
this.__construct();
this.attach();
this.__attach();
this.didAttach();
}
__construct(){}
__attach(){}
attach(){
const childMethods = this.getChildMethods();
for (const methodName of childMethods){
this.attachMethod(methodName);
}
}
didAttach(){}
getChildMethods(){
const methodNames = RB.Tools.getObjectMethodNames(this);
const rootMethods = RB.Tools.getObjectMethodNames(RB.DomWire.prototype);
const childMethods = methodNames.filter(method => rootMethods.indexOf(method)<0);
return childMethods;
}
attachMethod(methodName){
if (methodName.substring(0,2)!=='on')return false;
const method = this[methodName].bind(this);
if (this.eventMode=='addEventListener'){
const eventName = methodName.substring(2);
this.node.addEventListener(eventName,method);
return true;
} else if (this.eventMode=='static'){
this.node[methodName] = method;
return true;
} else {
throw "event mode must be 'addEventListener' or 'static'";
}
}
static autowire(querySelector=null) {
if (querySelector!=null)this.querySelector = querySelector;
// console.log("autowire, constructor name: "+this.prototype.constructor.name);
// console.log(this);
RB.Tools.onPageLoad(this.wire, this);
}
static wire(){
const nodes = this.getNodes();
for (const node of nodes){
this.wireNode(node);
}
}
static fromNode(nodeObj){
for (const row of this.list){
if (row.node===nodeObj)return row.obj;
}
}
};
RB.DomWire.list = [];
RB.DomWire.getQuerySelector = function(){
return this.querySelector || '.'+this.name;
}
RB.DomWire.getNodes = function(){
const querySelector = this.getQuerySelector();
let nodes = [];
try {
nodes = document.querySelectorAll(querySelector);
} catch (e){
nodes = [];
}
return nodes;
}
RB.DomWire.wireNode = function(node){
// console.log('wire node');
const object = new this(node);
return object;
// console.log('count:'+RB.DomWire.wireCount);
// RB.DomWire.wireCount--;
// console.log(object.prototype.__allReady);
}
RB.WireContext = class extends RB.DomWire {
// uniqueContextName(object){
// console.log(object);
// RB.WireContext.contextMap = RB.WireContext.contextMap ?? {};
// for (const key in RB.WireContext.contextMap){
// const ref = RB.WireContext.contextMap[key].anchor;
// if (object===ref)return key;
// }
// const newKey = Math.random().toString(36).substring(7);
// RB.WireContext.contextMap[newKey] = {"anchor":object};
// return newKey;
// }
get(objectName,contextName=null){
if (contextName==null)contextName = this.context.slice(0,1);
const map = RB.WireContext.contextMap[contextName];
const values = map[objectName];
// console.log(values);
if (values.length==1)return values[0];
else if (values.length==0)return null;
else return values;
}
didAttach(){
// console.log('didattach');
const map = RB.WireContext.contextMap ?? {};
if (typeof [] != typeof this.context)this.context = [this.context];
for (const name of this.context){
map[name] = map[name] ?? {};
map[name][this.name] = map[name][this.name] ?? [];
// console.log(this.name);
map[name][this.name].push(this);
}
RB.WireContext.contextMap = map;
// const props = Object.keys(this);
// console.log(this.constructor.prototype);
const proto = this.constructor.prototype;
let keys = [];
let obj = this.constructor.prototype;
// console.log(obj);
// console.log(Object.getPrototypeOf(obj));
do keys = keys.concat(Object.getOwnPropertyNames(obj));
while ((obj = Object.getPrototypeOf(obj))!=Object.prototype);
const props = keys.filter(function(value,index,self){return self.indexOf(value)===index;});
// console.log(keys);
// console.log(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(proto))))));
// console.log(proto.constructor.prototype.constructor.prototype);
// const props = Object.getOwnPropertyNames(this.constructor.prototype);
for (const prop of props){
if (prop.charAt(0)=='_'&&prop.charAt(1)!='_'){
const name = prop.slice(1);
const ret = this[prop]() ?? name;
Object.defineProperty( this, name, {
get : function(property){
if (typeof [] == typeof property){
let i = -1;
let output = '';
for (const val of property){
i++;
if (val==null)continue;
output = val;
break;
}
const contextEntry = this.context[i];
return this.get(output,contextEntry);
} else {
return this.get(property);
}
console.log(property);
console.log(this.context);
}.bind(this,ret)
} );
}
}
// console.log();//.constructor.prototype);
// console.log(this.context);
}
// allReady(){
// // console.log(RB.WireContext.contextMap);
// // console.log('allready');
// return;
// this.getter('parent',this.parent);
// // console.log('allready(). context:');
// const context = RB.Map.contextOfObject(this);
// // console.log(context);
// const names = {};
// for (const obj of context.objects){
// obj.contextObj = RB.Map.contextOfObject(obj);
// // console.log(obj.contextObj);
// let className = obj.constructor.name;
// className = className.charAt(0).toLowerCase()+className.slice(1);
// // console.log('classname:'+className);
// names[className] = names[className] ?? {"total":0,"children":0};
// names[className] = {"total":1+(names[className]['total'] ?? 0),"children":(names[className]['children']??0)+(this.node.contains(obj.node)?1:0)};
// // this.getter.call(this,)
// }
// for (const name in names){
// const total = names[name]['total'];
// console.log(name+':'+total);
// if (total==1
// ||names[name]['children']==1){
// // console.log('getter');
// this.getter(name,this.context.bind(this,name));
// }
// if (total>1){
// // for ()
// // for (const obj of context.)
// this.getter(name+'s',this.context.bind(this,name+'s'));
// }
// }
// // console.log('context:');
// // console.log(context);
// // console.log(RB.Map);
// }
// parent(){
// let parent = this.node;
// let obj;
// while (parent.parentNode!=null
// &&(parent=parent.parentNode)){
// if ((obj=RB.Map.getObj(parent))!==null){
// console.log(obj);
// return obj;
// }
// }
// // this.undefinedParent = this.undefinedParent ?? {'undefined-parent':'undefined', 'rand:':Math.random()};
// // console.log('undefined parent: '+RB.Tools.objectId(this.undefinedParent));
// // console.log();
// return null;
// if (this.contextObj == this.undefinedParent)return
// return this.undefinedParent;
// }
// context(objectName){
// console.log('looking for '+objectName);
// const objContext = RB.Map.contextOfObject(this);
// console.log(objContext);
// if (objContext==null)return null;
// // console.log('claled ongtext');
// // console.log(objContext.objects);
// // console.log('object context: ');
// // console.log(objContext);
// // const objects = RB.Map.objectsInContext(objContext.anchorObj);
// const objs = [];
// for (const obj of objContext.objects){
// // console.log(obj.node.classList);
// if (obj.node.classList.contains(objectName)){
// objs.push(obj);
// // console.log('pushed');
// }
// }
// console.log(objs);
// if (objs.length==0)return null;
// else if (objs.length==1)return objs.pop();
// else return objs;
// return null;
// }
// getter(name,fun){
// Object.defineProperty( this, name, {
// get : fun
// } );
// }
// static wire(){
// // console.log('child wire');
// console.log('might wire obj: '+RB.WireContext.wireCount);
// super.wire();
// if (--RB.WireContext.wireCount===0){
// // console.log('time to call the finals');
// // console.log(this.wiredObjs);
// for (const entry of this.wiredObjs){
// entry.obj.allReady();
// entry.obj.__allReady();
// }
// }
// };
// static autowire(querySelector=null){
// RB.WireContext.wireCount = 1+(RB.WireContext.wireCount ?? 0);
// super.autowire.apply(this,querySelector);
// // if (querySelector!=null)this.querySelector = querySelector;
// // console.log('child autowire');
// // RB.Tools.onPageLoad(this.wire, this);
// }
// static autowire(querySelector=null) {
// // RB.DomWire.wire.apply(this);
// RB.Tools.onPageLoad(this.wire, this);
// // RB.DomWire.pageLoadCount++;
// }
}
// RB.WireContext.wireNode = function(node){
// obj = new this(node);
// this.map = RB.Map;
// this.map.pushObjNode(obj,node);
// if (typeof obj.__allReady == typeof function(){}){//??null!==null){
// (RB.WireContext.wiredObjs = RB.WireContext.wiredObjs ?? []).push({'isFinalized':false, 'obj':obj});
// }
// // console.log(this.map.getNode(obj));
// };
RB.Map = new class {
nodeMap = [];
pushObjNode(obj,node){
this.nodeMap.push({"obj":obj,"node":node});
}
getNode(obj){
const entry = this.getEntry(obj);
return entry ? entry.node : null;
}
getObj(node){
const entry = this.getEntry(node);
return entry ? entry.obj : null;
}
getEntry(mixed){
for(const entry of this.nodeMap){
for (const key in entry){
const value = entry[key];
if (value===mixed)return entry;
}
}
return null;
}
contextMap = [];
pushContext(obj,contextAnchor){
let context;
if (!(context=this.getContextEntry(contextAnchor)))this.contextMap.push(context={'anchorObj':contextAnchor,'objects':[]});
if (obj!==null)context.objects.push(obj);
}
contextOfObject(object){
for (const entry of this.contextMap){
if (entry.objects.indexOf(object)!==-1){
return entry;
}
}
const nullContext = this.getContextEntry(null);
this.pushContext(object,nullContext);
// if (nullContext!==null){
// console.log('nullest context');
// }
return this.getContextEntry(null);
}
getContextEntry(contextAnchor=null){
if (contextAnchor==null)return (this.nullContext=this.nullContext ?? {'anchorObj':contextAnchor,'objects':[]});
for (const entry of this.contextMap){
if (contextAnchor===entry.anchorObj){
return entry;
}
}
// console.log('in getCOntextEntry()');
// console.log(this.contextMap);
// console.log(contextAnchor);
// const nullContext = this.getContextEntry(null);
// if (nullContext===null)this.pushContext(null,null);
return null;//this.getContextEntry(null);
// return null;
}
objectsInContext(contextAnchor){
const entry = this.getContextEntry(contextAnchor);
// console.log(entry);
return entry ? entry.objects : null;
}
}
RB.Tools = class {
};
RB.Tools.onPageLoad = function (func, thisArg, ...args) {
func.apply(thisArg,args);
return;
if (window.readyState == "complete") {
func.apply(thisArg, args);
} else if (window.addEventListener != null) {
document.addEventListener("DOMContentLoaded", function () {
if (document.readyState == "interactive") {
func.apply(thisArg, args);
}
});
} else {
window.onload = function () {
func.apply(func, args);
};
}
}
RB.Tools.getObjectMethodNames = function(object){
const properties = new Set();
let currentObj = object;
do {
Object.getOwnPropertyNames(currentObj).map(item => properties.add(item))
} while ((currentObj = Object.getPrototypeOf(currentObj)))
return [...properties.keys()].filter(item => typeof object[item] === 'function')
}
RB.Tools.objectId = function(object){
this.objectMap = this.objectMap ?? new WeakMap();
if (this.objectMap.has(object)){
return this.objectMap.get(object);
}
this.idMap = this.idMap ?? {};
this.counter = this.counter ?? 0;
const id = object.constructor.name+":"+(new Date()).getMilliseconds() + "-" + Object.keys(this.objectMap).length + "-" + this.counter++ + "-" + Math.random();
this.idMap[id] = object;
this.objectMap.set(object,id);
return id;
}.bind(RB.Tools);
RB.Tools.objectFor = function(id){
return this.idMap[id];
}.bind(RB.Tools);
if (RB===undefined)var RB = {};
RB.Dom = class {
static cycle(node,attribute,value=null){
if (value==null
&&node.hasAttribute(attribute)){
node.removeAttribute(attribute);
} else if(value==null){
node.setAttribute(attribute,'');
} else {
// const index = value.indexOf(node[attribute]);
// index++;
// index = index%value.length;
node[attribute] = value[ (1+value.indexOf(node[attribute]))%value.length ];
}
}
static is(tagName,node){
if (node.tagName.toLowerCase()==tagName.toLowerCase())return true;
return false;
}
}
if (typeof RB === 'undefined')var RB = {};
RB.Request = class {
constructor(url, method){
this.params = {};
this.url = url;
this.method = method ?? 'POST';
}
put(key,value){
if (key in this.params){
this.params[key] = (typeof this.params[key]==typeof []) ? this.params[key] : [this.params[key]];
this.params[key].push(value);
} else {
this.params[key] = value;
}
}
handleJson(func){
var formData = new FormData();
for(var key in this.params){
const param = this.params[key];
if (typeof param == typeof []){
for(const val of param){
formData.append(key,val);
}
} else formData.append(key,this.params[key]);
}
fetch(this.url, {
method: this.method,
mode: "cors",
// cache: "no-cache",
// credentials: "same-origin",
// redirect: "follow",
// referrerPolicy: "no-referrer",
// headers: {
// 'Content-Type': 'application/json'
// // 'Content-Type': 'application/x-www-form-urlencoded',
// },
body: formData
}).then(res => {
return res.json();
}).then(json => {
func(json);
});
}
handleText(func){
var formData = new FormData();
for(var key in this.params){
const param = this.params[key];
if (typeof param == typeof []){
for(const val of param){
formData.append(key,val);
}
} else formData.append(key,this.params[key]);
}
fetch(this.url, {
method: this.method,
mode: "cors",
body: formData
}).then(res => {
return res.text();
}).then(text => {
func(text);
});
}
}