mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 11:42:29 +00:00
248 lines
9.7 KiB
JavaScript
248 lines
9.7 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){
|
|
const eventName = event.triggerName || event.sourceAttribute;
|
|
let typePrefix = eventName.startsWith('clicked:') ?
|
|
'act_' :
|
|
event.removedInfo ?
|
|
'fieldset_' :
|
|
'attr_';
|
|
let cascName = `${typePrefix}${eventName.replace(/(?:removed|clicked):/,'')}`;
|
|
let cascObj = casc[cascName];
|
|
if(cascObj && typePrefix === 'attr_'){
|
|
cascObj.previousValue = event.previousValue;
|
|
}
|
|
return cascObj;
|
|
};
|
|
|
|
const triggerFunctions = function(obj,attributes,sections,casc){
|
|
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,casc}) :
|
|
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 alwaysFunctions = function(trigger,attributes,sections,casc){
|
|
Object.values(allHandlers).forEach((handler)=>{
|
|
handler({trigger,attributes,sections,casc});
|
|
});
|
|
};
|
|
const processChange = function({event,trigger,attributes,sections,casc}){
|
|
if(event && !trigger){
|
|
debug(`${event.sourceAttribute} change detected. No trigger found`);
|
|
return;
|
|
}
|
|
if(!attributes || !sections || !casc){
|
|
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
|
|
return;
|
|
}
|
|
debug({trigger});
|
|
if(event){
|
|
debug('checking for initial & always functions');
|
|
alwaysFunctions(trigger,attributes,sections,casc);//Functions that should be run for all events.
|
|
initialFunction(trigger,attributes,sections,casc);//functions that should only be run if the attribute was the thing changed by the user
|
|
}
|
|
if(trigger){
|
|
debug(`processing ${trigger.name}`);
|
|
triggerFunctions(trigger,attributes,sections,casc);
|
|
if(!event && trigger.calculation && funcs[trigger.calculation]){
|
|
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections,casc});
|
|
}else if(trigger.calculation && !funcs[trigger.calculation]){
|
|
debug(`K-Scaffold Error: No function named ${trigger.calculation} found`);
|
|
}
|
|
if(Array.isArray(trigger.affects)){
|
|
attributes.queue.push(...trigger.affects);
|
|
}
|
|
}
|
|
attributes.set({attributes,sections,casc});
|
|
};
|
|
const attrTarget = {
|
|
updates:{},
|
|
attributes:{...attrs},
|
|
repOrders:{},
|
|
queue: [],
|
|
casc:{},
|
|
alwaysFunctions,
|
|
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({updates:obj.updates});
|
|
let trueCallback = Object.keys(obj.repOrders).length ?
|
|
function(){
|
|
Object.entries(obj.repOrders).forEach(([section,order])=>{
|
|
_setSectionOrder(section,order,)
|
|
});
|
|
callback && callback();
|
|
}:
|
|
callback;
|
|
Object.keys(obj.updates).forEach((key)=>obj.attributes[key] = obj.updates[key]);
|
|
const update = obj.updates;
|
|
obj.updates = {};
|
|
set(update,vocal,trueCallback);
|
|
}
|
|
}
|
|
}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 !== ''){
|
|
retValue = numRetVal;
|
|
}else if(cascades[cascRef] && (typeof cascades[cascRef].defaultValue === 'number' || cascades[cascRef].type === '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);
|
|
};
|
|
|
|
|
|
/**
|
|
* Function that registers a function for being called via the funcs object. Returns true if the function was successfully registered, and false if it could not be registered for any reason.
|
|
* @param {object} funcObj - Object with keys that are names to register functions under and values that are functions.
|
|
* @param {object} optionsObj - Object that contains options to use for this registration.
|
|
* @param {string[]} optionsObj.type - Array that contains the types of specialized functions that apply to the functions being registered. Valid types are `"opener"`, `"updater"`, and `"default"`. `"default"` is always used, and never needs to be passed.
|
|
* @returns {boolean} - True if the registration succeeded, false if it failed.
|
|
* @example
|
|
* //Basic Registration
|
|
* const myFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({myFunc});
|
|
*
|
|
* //Register a function to run on sheet open
|
|
* const openFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({openFunc},{type:['opener']})
|
|
*
|
|
* //Register a function to run on all events
|
|
* const allFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({allFunc},{type:['all']})
|
|
*/
|
|
const registerFuncs = function(funcObj,optionsObj = {}){
|
|
if(typeof funcObj !== 'object' || typeof optionsObj !== 'object'){
|
|
debug(`!!!! K-scaffold error: Improper arguments to register functions !!!!`);
|
|
return false;
|
|
}
|
|
const typeArr = optionsObj.type ? ['default',...optionsObj.type] : ['default'];
|
|
const typeSwitch = {
|
|
'opener':openHandlers,
|
|
'updater':updateHandlers,
|
|
'new':initialSetups,
|
|
'all':allHandlers,
|
|
'default':funcs
|
|
};
|
|
let setState;
|
|
Object.entries(funcObj).map(([prop,value])=>{
|
|
typeArr.forEach((type)=>{
|
|
if(typeSwitch[type][prop]){
|
|
debug(`!!! Duplicate function name for ${prop} as ${type}!!!`);
|
|
setState = false;
|
|
}else if(typeof value === 'function'){
|
|
typeSwitch[type][prop] = value;
|
|
setState = setState !== false ? true : false;
|
|
}else{
|
|
debug(`!!! K-scaffold error: Function registration requires a function. Invalid value to register as ${type} !!!`);
|
|
setState = false;
|
|
}
|
|
});
|
|
});
|
|
return setState;
|
|
};
|
|
kFuncs.registerFuncs = registerFuncs;
|
|
|
|
const setActionCalls = function({attributes,sections}){
|
|
actionAttributes.forEach((base)=>{
|
|
let [section,,field] = k.parseTriggerName(base);
|
|
let fieldAction = field.replace(/_/g,'-');
|
|
if(section){
|
|
sections[section].forEach((id)=>{
|
|
attributes[`${section}_${id}_${field}`] = `%{${attributes.character_name}|${section}_${id}_${fieldAction}}`;
|
|
});
|
|
}else{
|
|
attributes[`${field}`] = `%{${attributes.character_name}|${fieldAction}}`;
|
|
}
|
|
});
|
|
};
|
|
funcs.setActionCalls = setActionCalls;
|
|
kFuncs.setActionCalls = setActionCalls;
|
|
|
|
/**
|
|
* Function to call a function previously registered to the funcs object. May not be used that much. Either returns the function or null if no function exists.
|
|
* @param {string} funcName - The name of the function to invoke.
|
|
* @param {...any} args - The arguments to call the function with.
|
|
* @returns {any}
|
|
* @example
|
|
* //Call myFunc with two arguments
|
|
* k.callFunc('myFunc','an argument','another argument');
|
|
*/
|
|
const callFunc = function(funcName,...args){
|
|
if(funcs[funcName]){
|
|
debug(`calling ${funcName}`);
|
|
return funcs[funcName](...args);
|
|
}else{
|
|
debug(`Invalid function name: ${funcName}`);
|
|
return null;
|
|
}
|
|
};
|
|
kFuncs.callFunc = callFunc; |