/*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); }; } });