I don’t think I’m handling them in any way different then normal equations. I just use the same mathjs.compile
on each line of math. Here’s the full logic of my code:
define(function(require, exports, module) {
'use strict';
var Extensions = require('ft/core/extensions').Extensions,
Pasteboard = require('ft/system/pasteboard').Pasteboard,
NodeSet = require('ft/core/nodeset').NodeSet,
mathjs = require("./math.js"),
idsToCompiledExpressions = {},
idsToResults = {},
idsToErrors = {};
function refreshCalc(editor, node) {
var each = node.firstChild,
compiledExpression,
scope = {},
newResult,
line,
id;
while (each) {
id = each.id;
line = each.line().trim();
compiledExpression = idsToCompiledExpressions[id];
idsToErrors[id] = false;
if (!line || line[0] === '#') {
idsToCompiledExpressions[id] = undefined;
newResult = undefined;
} else {
try {
if (!compiledExpression) {
compiledExpression = mathjs.compile(line);
idsToCompiledExpressions[id] = compiledExpression;
}
if (compiledExpression) {
newResult = compiledExpression.eval(scope);
}
} catch (e) {
newResult = e.message;
idsToErrors[id] = true;
}
}
var oldResult = idsToResults[id],
oldResultString = oldResult ? oldResult.toString() : '',
newResultString = newResult ? newResult.toString() : '';
if (oldResultString !== newResultString) {
idsToResults[id] = newResult;
editor.setNeedsRender(each);
}
each = each.nextSibling;
}
}
Extensions.addMode({
name: 'calc'
});
Extensions.addTreeChanged(function(editor, e) {
var deltas = e.deltas,
effectedCalcs = new NodeSet();
for (var i = 0; i < deltas.length; i++) {
var delta = deltas[i],
insertedNodes = delta.insertedNodes,
removedNodes = delta.removedNodes,
updatedNode = delta.updatedNode,
length,
each,
j;
length = removedNodes.length;
for (j = 0; j < length; j++) {
each = removedNodes[j];
delete idsToCompiledExpressions[each.id];
var previousParent = e.previousParent(each);
if (previousParent) {
if (previousParent.mode() === 'calc') {
effectedCalcs.addNode(previousParent);
}
}
}
length = insertedNodes.length;
for (j = 0; j < length; j++) {
each = insertedNodes[j];
if (each.mode() === 'calc') {
effectedCalcs.addNode(each);
} else if (each.modeContext() === 'calc' && each.parent.mode() === 'calc') {
effectedCalcs.addNode(each.parent);
}
}
if (updatedNode) {
delete idsToCompiledExpressions[updatedNode.id];
if (updatedNode.modeContext() === 'calc' && updatedNode.parent.mode() === 'calc') {
effectedCalcs.addNode(updatedNode.parent);
}
}
}
if (effectedCalcs.count > 0) {
effectedCalcs.forEachNodeInSet(function (each) {
if (each.mode() === 'calc') {
refreshCalc(editor, each);
}
});
}
});
function createWidget(className, textContent) {
var widget = document.createElement('span');
widget.className = className;
widget.textContent = textContent;
widget.addEventListener('mousedown', function (e) {
e.preventDefault();
});
widget.addEventListener('mouseup', function (e) {
e.preventDefault();
Pasteboard.writeString(widget.textContent);
});
return widget;
}
Extensions.addRenderNode(function (editor, node, nodeRenderer) {
if (node.modeContext() === 'calc' && node.parent.mode() === 'calc') {
var result = idsToResults[node.id];
if (result !== undefined) {
var widget;
if (idsToErrors[node.id]) {
widget = createWidget('cm-calc-renderwidget cm-error', result);
} else {
widget = createWidget('cm-calc-renderwidget', mathjs.format(result, {
precision: 14
}));
}
nodeRenderer.renderLineWidget(widget, {
showIfHidden : false,
handleMouseEvents: true,
positionWidget : function (widgetWidth, widgetHeight) {
var line = node.lineNumber(),
leadingSpace = node.line().match(/\s*/)[0],
coords = editor.charCoords({ line : line, ch : leadingSpace.length}, 'div'),
left = Math.round(coords.left + (editor.defaultSpaceWidth() * 4)) + 'px';
return {
marginLeft: left
};
}
});
}
}
});
Extensions.addCommand({
name: 'calc',
description: 'Evaluate selected text and replace with result.',
performCommand: function (editor) {
var range = editor.selectedRange(),
text = range.textInRange(),
parser = mathjs.parser(),
result;
try {
text.split('\n').forEach(function (each) {
result = parser.eval(each);
});
} catch (e) {
result = e;
}
editor.replaceSelection(mathjs.format(result), 'around');
}
});
});