mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 19:52:29 +00:00
152 lines
5.4 KiB
JavaScript
152 lines
5.4 KiB
JavaScript
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//# Attribute Obj Proxy handler
|
|
const createAttrProxy = function(attrs){
|
|
//creates a proxy for the attributes object so that values can be worked with more easily.
|
|
const getCascObj = function(event,casc){
|
|
let typePrefix = event.htmlAttributes ? 'act_' : 'attr_';
|
|
let cascName = `${typePrefix}${event.sourceAttribute}`;
|
|
let cascObj = casc[cascName];
|
|
return cascObj;
|
|
};
|
|
|
|
const triggerFunctions = function(obj,attributes,sections){
|
|
if(obj.triggeredFuncs && obj.triggeredFuncs.length){
|
|
debug(`triggering functions for ${obj.name}`);
|
|
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>funcs[func] ?
|
|
funcs[func]({trigger:obj,attributes,sections}) :
|
|
debug(`!!!Warning!!! no function named ${func} found. Triggered function not called for ${obj.name}`,true));
|
|
}
|
|
};
|
|
|
|
const initialFunction = function(obj,attributes,sections){
|
|
if(obj.initialFunc){
|
|
debug(`initial functions for ${obj.name}`);
|
|
funcs[obj.initialFunc] ?
|
|
funcs[obj.initialFunc]({trigger:obj,attributes,sections}) :
|
|
debug(`!!!Warning!!! no function named ${obj.initialFunc} found. Initial function not called for ${obj.name}`,true);
|
|
}
|
|
};
|
|
const processChange = function({event,trigger,attributes,sections,casc}){
|
|
debug({trigger});
|
|
if(event && !trigger){
|
|
debug('initial change detected. No trigger found');
|
|
return;
|
|
}
|
|
if(!attributes || !sections || !casc){
|
|
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
|
|
return;
|
|
}
|
|
//store the queue in attributes.
|
|
if(event){
|
|
debug('checking for initial functions');
|
|
initialFunction(trigger,attributes,sections);//functions that should only be run if the attribute was the thing changed by the user
|
|
attributes.queue = [...trigger.affects];
|
|
}
|
|
if(trigger){
|
|
debug(`processing ${trigger.name}`);
|
|
triggerFunctions(trigger,attributes,sections);
|
|
if(!event && trigger.calculation){
|
|
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections});
|
|
}
|
|
}
|
|
if(event || attributes.updates.hasOwnProperty(trigger.name)){
|
|
attributes.queue.push(...trigger.affects);
|
|
}
|
|
attributes.set({attributes,sections,casc});
|
|
};
|
|
const attrTarget = {
|
|
updates:{},
|
|
attributes:{...attrs},
|
|
repOrders:{},
|
|
queue: [],
|
|
casc:{},
|
|
processChange,
|
|
triggerFunctions,
|
|
initialFunction,
|
|
getCascObj
|
|
};
|
|
const attrHandler = {
|
|
get:function(obj,prop){//gets the most value of the attribute.
|
|
//If it is a repeating order, returns the array, otherwise returns the update value or the original value
|
|
if(prop === 'set'){
|
|
return function(){
|
|
let {attributes,sections,casc,callback,vocal} = arguments[0] ? arguments[0] : {};
|
|
if(attributes && attributes.queue.length && sections && casc){
|
|
let triggerName = attributes.queue.shift();
|
|
let trigger = getCascObj({sourceAttribute:triggerName},casc);
|
|
attributes.processChange({trigger,attributes,sections,casc});
|
|
}else{
|
|
debug('sheet accessed, all changes handled, setting attributes');
|
|
set(obj.updates,vocal,callback);
|
|
}
|
|
}
|
|
}else if(Object.keys(obj).some(key=>key===prop)){
|
|
return Reflect.get(...arguments)
|
|
}else{
|
|
let retValue;
|
|
switch(true){
|
|
case obj.repOrders.hasOwnProperty(prop):
|
|
retValue = obj.repOrders[prop];
|
|
break;
|
|
case obj.updates.hasOwnProperty(prop):
|
|
retValue = obj.updates[prop];
|
|
break;
|
|
default:
|
|
retValue = obj.attributes[prop];
|
|
break;
|
|
}
|
|
let cascRef = `attr_${prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$x')}`;
|
|
let numRetVal = +retValue;
|
|
if(!Number.isNaN(numRetVal)){
|
|
retValue = numRetVal;
|
|
}else if(cascades[cascRef] && typeof cascades[cascRef].defaultValue === 'number'){
|
|
retValue = cascades[cascRef].defaultValue;
|
|
}
|
|
return retValue;
|
|
}
|
|
},
|
|
set:function(obj,prop,value){
|
|
//Sets the value. Also verifies that the value is a valid attribute value
|
|
//e.g. not undefined, null, or NaN
|
|
if(value || value===0 || value===''){
|
|
if(/reporder|^repeating_[^_]+$/.test(prop)){
|
|
let section = prop.replace(/_reporder_/,'');
|
|
obj.repOrders[section] = value;
|
|
}else if(`${obj.attributes}` !== `${value}` ||
|
|
(obj.updates[prop] && `${obj.updates}` !== `${value}`)
|
|
){
|
|
obj.updates[prop] = value;
|
|
}
|
|
}else{
|
|
debug(`!!!Warning: Attempted to set ${prop} to an invalid value:${value}; value not stored!!!`);
|
|
}
|
|
return true;
|
|
},
|
|
deleteProperty(obj,prop){
|
|
//removes the property from the original attributes, updates, and the reporders
|
|
Object.keys(obj).forEach((key)=>{
|
|
delete obj[key][prop.toLowerCase()];
|
|
});
|
|
}
|
|
};
|
|
return new Proxy(attrTarget,attrHandler);
|
|
};
|
|
|
|
const funcs = new Proxy({},{
|
|
set:function(obj,prop,value){
|
|
if(obj[prop]){
|
|
debug(`!!! Duplicate function name for ${prop} !!!`);
|
|
return false;
|
|
}else{
|
|
obj[prop] = value;
|
|
return true;
|
|
}
|
|
},
|
|
get:function(obj,prop){
|
|
return function(){
|
|
debug(`running ${prop}`);
|
|
return obj[prop](...arguments);
|
|
};
|
|
}
|
|
}); |