/** This is an old version of the notes. You should view the docs instead.
*
* 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 Autowire.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 Autowire.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)
*
*/
Autowire = class {
constructor(node){
this.context = 'default';
Autowire.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 = Autowire.Tools.getObjectMethodNames(this);
const rootMethods = Autowire.Tools.getObjectMethodNames(Autowire.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'";
}
}
nodeContext(node,name='default'){
return name+"-"+Autowire.Tools.objectId(this.node.parentNode);
}
get(objectName,contextName=null){
if (contextName==null)contextName = this.context.slice(0,1);
const map = Autowire.contextMap[contextName];
const values = map[objectName];
if (values==null||values.length==0)return [];
else return values;
}
didAttach(){
const map = Autowire.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] || [];
map[name][this.name].push(this);
}
Autowire.contextMap = map;
const proto = this.constructor.prototype;
let keys = [];
let obj = this.constructor.prototype;
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;});
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);
}
}.bind(this,ret)
} );
const oneName = 'one'+name.charAt(0).toUpperCase() + name.slice(1);
Object.defineProperty( this, oneName, {
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];
const array = this.get(output,contextEntry);
if (array!=null&&array.length>0)return array[0];
else return null;
} else {
const array = this.get(property);
if (array!=null&&array.length>0)return array[0];
else return null;
}
}.bind(this,ret)
} );
}
}
}
send(url,data,responseMethod,method="POST"){
var req = new Autowire.Request(url,method);
req.handleJson(responseMethod.bind(this));
}
static autowire(querySelector=null) {
Autowire.readyCount++;
Autowire.ready = false;
if (querySelector!=null)this.querySelector = querySelector;
Autowire.Tools.onPageLoad(this.wire, this);
}
static wire(){
const nodes = this.getNodes();
const pending = [];
for (const node of nodes){
const obj = this.wireNode(node);
pending.push(obj);
}
Autowire.readyCount--;
Autowire.ready = Autowire.readyCount===0;
if (Autowire.ready){
for (const obj of pending){
if (obj.__readyCalled
||typeof obj.__ready !== typeof function(){})continue;
obj.__readyCalled = true;
obj.__ready();
}
for (const obj of Autowire.readyPending){
if (obj.__readyCalled
||typeof obj.__ready !== typeof function(){})continue;
obj.__readyCalled = true;
obj.__ready();
}
Autowire.readyPending = [];
}
}
static fromNode(nodeObj){
for (const row of this.list){
if (row.node===nodeObj)return row.obj;
}
}
static getQuerySelector(){
return this.querySelector || '.'+(this.className || this.name);
}
static getNodes(){
const querySelector = this.getQuerySelector();
let nodes = [];
try {
nodes = document.querySelectorAll(querySelector);
} catch (e){
nodes = [];
}
return nodes;
}
static wireNode(node){
const obj = new this(node);
if (Autowire.ready
&&(typeof obj.__ready === typeof function(){})){
obj.__ready();
} else if (!Autowire.ready){
// should the readycount be incremented here???
// I don't think so
Autowire.readyPending.push(obj);
}
return obj;
}
}
Autowire.readyCount = 0;
Autowire.ready = true;
Autowire.readyPending = [];
Autowire.list = [];