diff --git a/app/imports/api/creature/actions/applyAttack.js b/app/imports/api/creature/actions/applyAttack.js index a501085f..236b7c62 100644 --- a/app/imports/api/creature/actions/applyAttack.js +++ b/app/imports/api/creature/actions/applyAttack.js @@ -1,4 +1,4 @@ -import math from '/imports/math.js'; +import roll from '/imports/parser/roll.js'; import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js'; export default function applyAttack({ @@ -8,7 +8,7 @@ export default function applyAttack({ //targets, //actionContext }){ - let result = math.roll(1, 20) + prop.rollBonusResult; + let result = roll(1, 20) + prop.rollBonusResult; insertCreatureLog.call({ log: { text: `${prop.name} attack. ${result} to hit`, diff --git a/app/imports/api/creature/actions/applyDamage.js b/app/imports/api/creature/actions/applyDamage.js index 0cb3d611..86a211b2 100644 --- a/app/imports/api/creature/actions/applyDamage.js +++ b/app/imports/api/creature/actions/applyDamage.js @@ -1,4 +1,4 @@ -import evaluateAndRollString from '/imports/api/creature/computation/afterComputation/evaluateAndRollString.js'; +import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js'; //if (Meteor.isServer){ // var sendWebhook = require('/imports/server/discord/webhook.js').default; @@ -15,7 +15,7 @@ export default function applyDamage({ ...creature.variables, ...actionContext, }; - let {result, errors} = evaluateAndRollString(prop.amount, scope); + let {result, errors} = evaluateString(prop.amount, scope, 'reduce'); if (Meteor.isClient){ errors.forEach(e => console.error(e)); console.log(`${result} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`); diff --git a/app/imports/api/creature/computation/EffectAggregator.js b/app/imports/api/creature/computation/EffectAggregator.js index f9c2c364..bb127e6d 100644 --- a/app/imports/api/creature/computation/EffectAggregator.js +++ b/app/imports/api/creature/computation/EffectAggregator.js @@ -4,10 +4,10 @@ export default class EffectAggregator{ constructor(stat, memo){ delete this.baseValueErrors; if (stat.baseValueCalculation){ - let {value, errors} = evaluateCalculation(stat.baseValueCalculation, memo); - this.statBaseValue = value; - if (errors.length){ - this.baseValueErrors = errors; + let {result, context} = evaluateCalculation(stat.baseValueCalculation, memo); + this.statBaseValue = result.value; + if (context.errors.length){ + this.baseValueErrors = context.errors; } this.base = this.statBaseValue; } else { diff --git a/app/imports/api/creature/computation/afterComputation/evaluateAndRollString.js b/app/imports/api/creature/computation/afterComputation/evaluateAndRollString.js deleted file mode 100644 index 23802246..00000000 --- a/app/imports/api/creature/computation/afterComputation/evaluateAndRollString.js +++ /dev/null @@ -1,77 +0,0 @@ -import math from '/imports/math.js'; -import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js'; -import substituteRollsWithFunctions from '/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js' - -export default function evaluateAndRollString(string, scope){ - let errors = []; - if (!string){ - errors.push('No string provided'); - return {result: string, errors}; - } - - if (!scope) errors.push('No scope provided'); - - // Parse the string using mathjs - let calc; - try { - calc = math.parse(string); - } catch (e) { - errors.push(e); - return {result: string, errors}; - } - - // Replace all bare symbols with symbol.value - let transformedCalc = calc.transform(bareSymbolSubtitutor(scope)); - // Replace all rolls with the function to call them - transformedCalc = calc.transform(substituteRollsWithFunctions); - - // Evaluate the expression to a number or return with substitutions - try { - let result = transformedCalc.evaluate(scope); - return {result, errors}; - } catch (e1){ - errors.push(e1); - try { - let result = simplifyWithAccessors(transformedCalc, scope).toHTML(); - return {result, errors}; - } catch (e2){ - errors.push(e2); - return {result: transformedCalc.toHTML(), errors}; - } - } -} - -function simplifyWithAccessors(calc, scope){ - let noAccessorCalc = calc.transform(substituteAccessors(scope)); - return math.simplify(noAccessorCalc); -} - -// returns a function to replace all accessors with either their resolved value -// or a symbol to simplify with -function substituteAccessors(scope){ - return function(node){ - if (node.isAccessorNode){ - try { - return evaluateAccessor(node, scope); - } catch (e) { - return replaceAccessorWithSymbol(node); - } - } else { - return node; - } - } -} - -// Throws error if symbol is undefined in scope -function evaluateAccessor(node, scope){ - let value = node.evaluate(scope); - if (value === undefined){ - throw 'Undefined symbol' - } - return new math.ConstantNode(value); -} - -function replaceAccessorWithSymbol(node){ - let symbolNode = new math.SymbolNode(node.toString()); - return symbolNode; -} diff --git a/app/imports/api/creature/computation/afterComputation/evaluateString.js b/app/imports/api/creature/computation/afterComputation/evaluateString.js index 98543fc2..9753d963 100644 --- a/app/imports/api/creature/computation/afterComputation/evaluateString.js +++ b/app/imports/api/creature/computation/afterComputation/evaluateString.js @@ -1,7 +1,7 @@ -import math from '/imports/math.js'; -import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js'; +import { parse, CompilationContext } from '/imports/parser/parser.js'; +import ConstantNode from '/imports/parser/parseTree/ConstantNode.js'; -export default function evaluateString(string, scope){ +export default function evaluateString(string, scope, fn = 'compile'){ let errors = []; if (!string){ errors.push('No string provided'); @@ -11,88 +11,19 @@ export default function evaluateString(string, scope){ if (!scope) errors.push('No scope provided'); // Parse the string using mathjs - let calc; + let node; try { - calc = math.parse(string); + node = parse(string); } catch (e) { errors.push(e); return {result: string, errors}; } - // Replace all bare symbols with symbol.value - let transformedCalc = calc.transform(bareSymbolSubtitutor(scope)); - - // Evaluate the expression to a number or return with substitutions - try { - let result = transformedCalc.evaluate(scope); - return {result, errors}; - } catch (e1){ - errors.push(e1); - try { - let result = simplifyWithAccessors(transformedCalc, scope).toHTML(); - return {result, errors}; - } catch (e2){ - errors.push(e2); - return {result: transformedCalc.toHTML(), errors}; - } + let context = new CompilationContext(); + let result = node[fn](scope, context); + if (result instanceof ConstantNode){ + return {result: result.value, errors: context.errors} + } else { + return {result: result.toString(), errors: context.errors}; } } - -function simplifyWithAccessors(calc, scope){ - let noAccessorCalc = calc.transform(substituteAccessors(scope)); - return math.simplify(noAccessorCalc); -} - -// returns a function to replace all accessors with either their resolved value -// or a symbol to simplify with -function substituteAccessors(scope){ - return function(node){ - if (node.isAccessorNode){ - try { - return evaluateAccessor(node, scope); - } catch (e) { - return replaceAccessorWithSymbol(node); - } - } else { - return node; - } - } -} - -// Throws error if symbol is undefined in scope -function evaluateAccessor(node, scope){ - let value = node.evaluate(scope); - if (value === undefined){ - throw 'Undefined symbol' - } - return new math.ConstantNode(value); -} - -function replaceAccessorWithSymbol(node){ - let symbolNode = new math.SymbolNode(node.toString()); - return symbolNode; -} - -/* -function overrideSymbolNodeHTML(symbolNode){ - let safeName = escape(symbolNode.name); - symbolNode.toHTML = function(){ - console.log('running custom tohtml function') - return `${safeName}` - } - return symbolNode; -} - -// Escape special HTML characters -// Copied directly from math.js source to help with overriding toHTML -function escape (value) { - let text = String(value) - text = text.replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(//g, '>') - - return text -} -*/ diff --git a/app/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js b/app/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js deleted file mode 100644 index 2c3ddf63..00000000 --- a/app/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js +++ /dev/null @@ -1,28 +0,0 @@ -import math from '/imports/math.js'; - -const diceRegex = /d\d+/; - -export default function substituteRollsWithFunctions(node){ - // TODO also replace dx as 1dx - if ( - node.isOperatorNode && - node.fn === 'multiply' && - node.implicit && - node.args[1].isSymbolNode && - diceRegex.test(node.args[1].name) - ){ - let diceSize = node.args[1].name.slice(1); - let diceSizeNode = new math.ConstantNode(diceSize); - return new math.FunctionNode('roll', [node.args[0], diceSizeNode]); - } else if ( - node.isSymbolNode && - diceRegex.test(node.name) - ) { - let diceSize = node.name.slice(1); - let diceSizeNode = new math.ConstantNode(diceSize); - let diceNumberNode = new math.ConstantNode(1); - return new math.FunctionNode('roll', [diceNumberNode, diceSizeNode]); - } else { - return node; - } -} diff --git a/app/imports/api/creature/computation/combineStat.js b/app/imports/api/creature/computation/combineStat.js index 873eade2..cbea7567 100644 --- a/app/imports/api/creature/computation/combineStat.js +++ b/app/imports/api/creature/computation/combineStat.js @@ -37,9 +37,9 @@ function combineAttribute(stat, aggregator, memo){ stat.modifier = Math.floor((stat.value - 10) / 2); } if (stat.attributeType === 'spellSlot'){ - let {value, errors} = evaluateCalculation(stat.spellSlotLevelCalculation, memo); - stat.spellSlotLevelValue = value, - stat.spellSlotLevelErrors = errors; + let {result, context} = evaluateCalculation(stat.spellSlotLevelCalculation, memo); + stat.spellSlotLevelValue = result.value; + stat.spellSlotLevelErrors = context.errors; } stat.currentValue = stat.value - (stat.damage || 0); stat.hide = aggregator.hasNoEffects && diff --git a/app/imports/api/creature/computation/computeEffect.js b/app/imports/api/creature/computation/computeEffect.js index df743fcf..b4507bc6 100644 --- a/app/imports/api/creature/computation/computeEffect.js +++ b/app/imports/api/creature/computation/computeEffect.js @@ -34,10 +34,10 @@ export default function computeEffect(effect, memo){ } else if(_.contains(['advantage', 'disadvantage', 'fail'], effect.operation)){ effect.result = 1; } else { - let {value, errors} = evaluateCalculation(effect.calculation, memo); - effect.result = value; - if (errors.length){ - effect.errors = errors; + let {result, context} = evaluateCalculation(effect.calculation, memo); + effect.result = result.value; + if (context.errors.length){ + effect.errors = context.errors; } } effect.computationDetails.computed = true; diff --git a/app/imports/api/creature/computation/computeEndStepProperty.js b/app/imports/api/creature/computation/computeEndStepProperty.js index 091cc2bb..eceb8fd4 100644 --- a/app/imports/api/creature/computation/computeEndStepProperty.js +++ b/app/imports/api/creature/computation/computeEndStepProperty.js @@ -23,10 +23,10 @@ export default function computeEndStepProperty(prop, memo){ function computeAction(prop, memo){ // Uses - let {value, errors} = evaluateCalculation(prop.uses, memo); - prop.usesResult = value; - if (errors.length){ - prop.usesErrors = errors; + let {result, context} = evaluateCalculation(prop.uses, memo); + prop.usesResult = result.value; + if (context.errors.length){ + prop.usesErrors = context.errors; } else { delete prop.usesErrors; } @@ -69,40 +69,40 @@ function computeAction(prop, memo){ function computeAttack(prop, memo){ // Roll bonus - let {value, errors} = evaluateCalculation(prop.rollBonus, memo); - prop.rollBonusResult = value; - if (errors.length){ - prop.rollBonusErrors = errors; + let {result, context} = evaluateCalculation(prop.rollBonus, memo); + prop.rollBonusResult = result.value; + if (context.errors.length){ + prop.rollBonusErrors = context.errors; } else { delete prop.rollBonusErrors; } } function computeSavingThrow(prop, memo){ - let {value, errors} = evaluateCalculation(prop.dc, memo); - prop.dcResult = value; - if (errors.length){ - prop.dcErrors = errors; + let {result, context} = evaluateCalculation(prop.dc, memo); + prop.dcResult = result.value; + if (context.errors.length){ + prop.dcErrors = context.errors; } else { delete prop.dcErrors; } } function computeSpellList(prop, memo){ - let {value, errors} = evaluateCalculation(prop.maxPrepared, memo); - prop.maxPreparedResult = value; - if (errors.length){ - prop.maxPreparedErrors = errors; + let {result, context} = evaluateCalculation(prop.maxPrepared, memo); + prop.maxPreparedResult = result.value; + if (context.errors.length){ + prop.maxPreparedErrors = context.errors; } else { delete prop.maxPreparedErrors; } } function computeSlot(prop, memo){ - let {value, errors} = evaluateCalculation(prop.slotCondition, memo); - prop.slotConditionResult = value; - if (errors.length){ - prop.slotConditionErrors = errors; + let {result, context} = evaluateCalculation(prop.slotCondition, memo); + prop.slotConditionResult = result.value; + if (context.errors.length){ + prop.slotConditionErrors = context.errors; } else { delete prop.slotConditionErrors; } diff --git a/app/imports/api/creature/computation/computeToggle.js b/app/imports/api/creature/computation/computeToggle.js index c766b012..04b8b801 100644 --- a/app/imports/api/creature/computation/computeToggle.js +++ b/app/imports/api/creature/computation/computeToggle.js @@ -26,10 +26,10 @@ export default function computeToggle(toggle, memo){ } else if (Number.isFinite(+toggle.condition)){ toggle.toggleResult = !!+toggle.condition; } else { - let {value, errors} = evaluateCalculation(toggle.condition, memo); - toggle.toggleResult = !!value; - if (errors.length){ - toggle.errors = errors; + let {result, context} = evaluateCalculation(toggle.condition, memo); + toggle.toggleResult = !!result.value; + if (context.errors.length){ + toggle.errors = context.errors; } } toggle.computationDetails.computed = true; diff --git a/app/imports/api/creature/computation/evaluateCalculation.js b/app/imports/api/creature/computation/evaluateCalculation.js index a6909417..02bff69f 100644 --- a/app/imports/api/creature/computation/evaluateCalculation.js +++ b/app/imports/api/creature/computation/evaluateCalculation.js @@ -1,98 +1,41 @@ import computeStat from '/imports/api/creature/computation/computeStat.js'; -import math from '/imports/math.js'; +import { parse, CompilationContext } from '/imports/parser/parser.js'; +import SymbolNode from '/imports/parser/parseTree/SymbolNode.js'; +import AccessorNode from '/imports/parser/parseTree/AccessorNode.js'; +import ConstantNode from '/imports/parser/parseTree/ConstantNode.js'; /* Convert a calculation into a constant output and errors*/ -export default function evaluateCalculation(string, memo){ - if (!string) return {errors: [], value: string}; +export default function evaluateCalculation(string, memo, fn = 'reduce'){ + if (!string) return { + context: {errors: []}, + result: new ConstantNode({value: string, type: 'string'}), + }; let errors = []; - // Parse the string using mathjs + // Parse the string let calc; try { - calc = math.parse(string); + calc = parse(string); } catch (e) { errors.push({ type: 'parsing', message: e.message || e }); - return {errors, value: string}; + return { + context: {errors}, + result: new ConstantNode({value: string, type: 'string'}), + }; } // Ensure all symbol nodes are defined and coputed calc.traverse(node => { - if (node.isSymbolNode){ + if (node instanceof SymbolNode || node instanceof AccessorNode){ let stat = memo.statsByVariableName[node.name]; if (stat && !stat.computationDetails.computed){ computeStat(stat, memo); } } }); - // Replace all symbols with their subtitution - let substitutedCalc = calc.transform( - symbolSubtitutor(memo.statsByVariableName, errors) - ); - // Evaluate the expression to a number or return with substitutions - try { - let value = substitutedCalc.evaluate(memo.statsByVariableName); - if (typeof value === 'object') value = value.toString(); - return {errors, value}; - } catch (e){ - errors.push({ - type: 'evaluation', - message: e.message || e - }); - let value = substitutedCalc.toString(); - return {errors, value}; - } -} - -// returns a function to replace all symbols with either their resolved value -// or zero, keeping the errors -function symbolSubtitutor(scope, errors){ - return function(node){ - // mark symbol nodes that are children of function nodes to be skipped - if (node.isFunctionNode){ - let fn = node.fn; - if (fn && fn.isSymbolNode){ - fn.skipReplacement = true; - } - return node; - } else if (node.isSymbolNode && node.skipReplacement !== true){ - //bare symbols of name "stat", should search for stat.value - let stat = scope[node.name]; - if (stat){ - if (stat.value === undefined){ - errors.push({ - type: 'subsitution', - message: `${node.name} does not have a value, set to 0` - }); - return new math.ConstantNode(0); - } else { - return new math.ConstantNode(stat.value); - } - } else { - try { - return new math.ConstantNode(node.evaluate(scope)); - } catch (e) { - errors.push({ - type: 'subsitution', - message: `${node.name} not found, set to 0` - }); - return new math.ConstantNode(0); - } - } - } else if (node.isAccessorNode && node.object.isSymbolNode){ - try { - let value = node.evaluate(scope); - if (value === undefined) throw 'Not found'; - return new math.ConstantNode(value); - } catch (e) { - errors.push({ - type: 'subsitution', - message: `${node.toString()} not found, set to 0` - }); - return new math.ConstantNode(0); - } - } else { - return node; - } - } + // Evaluate + let context = new CompilationContext(); + let result = calc[fn](memo.statsByVariableName, context); + return {result, context}; } diff --git a/app/imports/api/creature/computation/utility/bareSymbolSubtitutor.js b/app/imports/api/creature/computation/utility/bareSymbolSubtitutor.js deleted file mode 100644 index cb0bccd2..00000000 --- a/app/imports/api/creature/computation/utility/bareSymbolSubtitutor.js +++ /dev/null @@ -1,24 +0,0 @@ -import math from '/imports/math.js'; - -export default function bareSymbolSubtitutor(scope){ - return function(node, path){ - if (!scope) return node; - if (node.isFunctionNode){ - let fn = node.fn; - if (fn && fn.isSymbolNode){ - fn.skipReplacement = true; - } - return node; - } else if ( - node.isSymbolNode && - path !== 'object' && - node.skipReplacement !== true - ) { - let stat = scope[node.name]; - if (!stat) return node; - return new math.ConstantNode(stat.value); - } else { - return node; - } - } -} diff --git a/app/imports/parser/parseTree/ArrayNode.js b/app/imports/parser/parseTree/ArrayNode.js index 76e20a4f..66b3edab 100644 --- a/app/imports/parser/parseTree/ArrayNode.js +++ b/app/imports/parser/parseTree/ArrayNode.js @@ -12,4 +12,8 @@ export default class ArrayNode extends ParseNode { toString(){ return `[${this.values.map(node => node.toString()).join(', ')}]`; } + traverse(fn){ + fn(this); + this.values.forEach(value => value.traverse(fn)); + } } diff --git a/app/imports/parser/parseTree/CallNode.js b/app/imports/parser/parseTree/CallNode.js index 3580950b..a603c6aa 100644 --- a/app/imports/parser/parseTree/CallNode.js +++ b/app/imports/parser/parseTree/CallNode.js @@ -50,6 +50,10 @@ export default class CallNode extends ParseNode { toString(){ return `${this.functionName}(${this.args.map(node => node.toString()).join(', ')})`; } + traverse(fn){ + fn(this); + this.args.forEach(arg => arg.traverse(fn)); + } } function castArgsToType({fn, scope, context, args, type}){ @@ -65,6 +69,5 @@ function castArgsToType({fn, scope, context, args, type}){ }) } if (resolvedArgs.failed) return resolvedArgs; - console.log({result}) return result; } diff --git a/app/imports/parser/parseTree/IfNode.js b/app/imports/parser/parseTree/IfNode.js index 2505fe57..8ffe7831 100644 --- a/app/imports/parser/parseTree/IfNode.js +++ b/app/imports/parser/parseTree/IfNode.js @@ -28,4 +28,10 @@ export default class IfNode extends ParseNode { }); } } + traverse(fn){ + fn(this); + this.condition.traverse(fn); + this.consequent.traverse(fn); + this.alternative.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/IndexNode.js b/app/imports/parser/parseTree/IndexNode.js index 4535c6d6..8f7684f2 100644 --- a/app/imports/parser/parseTree/IndexNode.js +++ b/app/imports/parser/parseTree/IndexNode.js @@ -24,4 +24,9 @@ export default class IndexNode extends ParseNode { toString(){ return `${this.array.toString()}[${this.index.toString()}]`; } + traverse(fn){ + fn(this); + this.array.traverse(fn); + this.index.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/NotOperatorNode.js b/app/imports/parser/parseTree/NotOperatorNode.js index e1ef4f62..64568f59 100644 --- a/app/imports/parser/parseTree/NotOperatorNode.js +++ b/app/imports/parser/parseTree/NotOperatorNode.js @@ -24,4 +24,8 @@ export default class NotOperatorNode extends ParseNode { let {right} = this; return `!${right.toString()}`; } + traverse(fn){ + fn(this); + this.right.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/OperatorNode.js b/app/imports/parser/parseTree/OperatorNode.js index 376dddc5..586b60e4 100644 --- a/app/imports/parser/parseTree/OperatorNode.js +++ b/app/imports/parser/parseTree/OperatorNode.js @@ -53,4 +53,9 @@ export default class OperatorNode extends ParseNode { let {left, right, operator} = this; return `${left.toString()} ${operator} ${right.toString()}`; } + traverse(fn){ + fn(this); + this.left.traverse(fn); + this.right.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/ParenthesisNode.js b/app/imports/parser/parseTree/ParenthesisNode.js index 1a99e45a..cdf6ac8d 100644 --- a/app/imports/parser/parseTree/ParenthesisNode.js +++ b/app/imports/parser/parseTree/ParenthesisNode.js @@ -20,4 +20,8 @@ export default class ParenthesisNode extends ParseNode { toString(){ return `(${this.content.toString()})`; } + traverse(fn){ + fn(this); + this.content.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/ParseNode.js b/app/imports/parser/parseTree/ParseNode.js index 8f3ebd23..2379081a 100644 --- a/app/imports/parser/parseTree/ParseNode.js +++ b/app/imports/parser/parseTree/ParseNode.js @@ -26,4 +26,8 @@ export default class ParseNode { return this.roll(scope, context); } } + // If traverse isn't implemented, just apply it to the current node + traverse(fn){ + fn(this); + } } diff --git a/app/imports/parser/parseTree/RollNode.js b/app/imports/parser/parseTree/RollNode.js index d59b900f..d44bff4c 100644 --- a/app/imports/parser/parseTree/RollNode.js +++ b/app/imports/parser/parseTree/RollNode.js @@ -59,4 +59,9 @@ export default class RollNode extends ParseNode { reduce(scope, context){ return this.roll(scope, context).reduce(scope, context); } + traverse(fn){ + fn(this); + this.left.traverse(fn); + this.right.traverse(fn); + } } diff --git a/app/imports/parser/parseTree/UnaryOperatorNode.js b/app/imports/parser/parseTree/UnaryOperatorNode.js index 30844cfe..3f53014c 100644 --- a/app/imports/parser/parseTree/UnaryOperatorNode.js +++ b/app/imports/parser/parseTree/UnaryOperatorNode.js @@ -30,4 +30,8 @@ export default class UnaryOperatorNode extends ParseNode { let {right, operator} = this; return `${operator}${right.toString()}`; } + traverse(fn){ + fn(this); + this.right.traverse(fn); + } } diff --git a/app/imports/ui/utility/debounceUpdate.js b/app/imports/ui/utility/debounceUpdate.js deleted file mode 100644 index 76915732..00000000 --- a/app/imports/ui/utility/debounceUpdate.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Creates a function that takes an object which represents the updates to be - * made as key: value pairs. The function collects these updates, (overwriting - * duplicates), and runs the given callback with the collated updates after the - * `time` has passed without the function being called. - */ -export default function debounceUpdate(callback, time = 300){ - let collatedUpdate = {}; - let interval; - - return (update, ...rest) => { - // Every time we're called, collect the update - for (key in update){ - collatedUpdate[key] = update[key]; - } - // Reset the clock and set it again to run the callback - clearTimeout(interval); - interval = setTimeout(() => { - // Clock timed out, apply all the collated updates - callback(collatedUpdate, ...rest); - // Reset the collation - collatedUpdate = {}; - }, time); - }; -}; - -// Example use -// -// let f = update => { -// console.log({wroteUpdate: update}); -// }; -// let df = debounceUpdate(f, 300); -// df({name: 'john'}); -// df({name: 'jack'}); -// df({age: 24, country: 'Ethiopia'}); -// df({transport: 'bus'}); -// -// --> { wroteUpdate: { -// name: 'jack', -// age: 24, -// country: 'Ethiopia', -// transport: 'bus' -// }} diff --git a/app/package-lock.json b/app/package-lock.json index 02cf5ac4..c5ee0239 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -414,11 +414,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "complex.js": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz", - "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -499,11 +494,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -583,11 +573,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, - "escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -897,11 +882,6 @@ "mime-types": "^2.1.12" } }, - "fraction.js": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", - "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==" - }, "fs-minipass": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", @@ -1251,11 +1231,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1525,21 +1500,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" }, - "mathjs": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-6.6.4.tgz", - "integrity": "sha512-fvmP89ujJbDAC8ths7FZh7PWdA71dfA5WJVAzJbQhSDCHK1aBk8WRf1XcTw51ERs+sKx9nYBGsRshqmb/oe8Ag==", - "requires": { - "complex.js": "^2.0.11", - "decimal.js": "^10.2.0", - "escape-latex": "^1.2.0", - "fraction.js": "^4.0.12", - "javascript-natural-sort": "^0.7.1", - "seed-random": "^2.2.0", - "tiny-emitter": "^2.1.0", - "typed-function": "^1.1.1" - } - }, "mem": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/mem/-/mem-6.0.1.tgz", @@ -1828,7 +1788,8 @@ }, "elliptic": { "version": "6.4.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2768,11 +2729,6 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "seed-random": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=" - }, "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", @@ -3008,11 +2964,6 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -3065,11 +3016,6 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, - "typed-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.1.tgz", - "integrity": "sha512-RbN7MaTQBZLJYzDENHPA0nUmWT0Ex80KHItprrgbTPufYhIlTePvCXZxyQK7wgn19FW5bnuaBIKcBb5mRWjB1Q==" - }, "underscore": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", diff --git a/app/package.json b/app/package.json index 609b4713..79160460 100644 --- a/app/package.json +++ b/app/package.json @@ -29,7 +29,6 @@ "dompurify": "^2.0.10", "lodash": "^4.17.20", "marked": "^0.8.2", - "mathjs": "^6.6.4", "meteor-node-stubs": "^0.3.3", "moo": "^0.5.1", "nearley": "^2.19.1",