diff --git a/Makefile b/Makefile
index 46f811553..38851776c 100644
--- a/Makefile
+++ b/Makefile
@@ -24,36 +24,38 @@ SRC_DIR = ./src
INTRO = $(SRC_DIR)/intro.js
OUTRO = $(SRC_DIR)/outro.js
-PJS_SRC = ./node_modules/pjs/src/p.js
-
BASE_SOURCES = \
- $(PJS_SRC) \
- $(SRC_DIR)/tree.js \
- $(SRC_DIR)/cursor.js \
- $(SRC_DIR)/controller.js \
- $(SRC_DIR)/publicapi.js \
- $(SRC_DIR)/services/parser.util.js \
- $(SRC_DIR)/services/saneKeyboardEvents.util.js \
- $(SRC_DIR)/services/aria.js \
- $(SRC_DIR)/services/exportText.js \
- $(SRC_DIR)/services/focusBlur.js \
- $(SRC_DIR)/services/keystroke.js \
- $(SRC_DIR)/services/latex.js \
- $(SRC_DIR)/services/mouse.js \
- $(SRC_DIR)/services/scrollHoriz.js \
- $(SRC_DIR)/services/textarea.js
+ $(SRC_DIR)/utils.ts \
+ $(SRC_DIR)/services/aria.ts \
+ $(SRC_DIR)/tree.ts \
+ $(SRC_DIR)/cursor.ts \
+ $(SRC_DIR)/controller.ts \
+ $(SRC_DIR)/publicapi.ts \
+ $(SRC_DIR)/services/parser.util.ts \
+ $(SRC_DIR)/services/saneKeyboardEvents.util.ts \
+ $(SRC_DIR)/services/exportText.ts \
+ $(SRC_DIR)/services/focusBlur.ts \
+ $(SRC_DIR)/services/keystroke.ts \
+ $(SRC_DIR)/services/latex.ts \
+ $(SRC_DIR)/services/mouse.ts \
+ $(SRC_DIR)/services/scrollHoriz.ts \
+ $(SRC_DIR)/services/textarea.ts
SOURCES_FULL = \
$(BASE_SOURCES) \
- $(SRC_DIR)/commands/math.js \
- $(SRC_DIR)/commands/text.js \
- $(SRC_DIR)/commands/math/*.js
+ $(SRC_DIR)/commands/math.ts \
+ $(SRC_DIR)/commands/text.ts \
+ $(SRC_DIR)/commands/math/advancedSymbols.ts \
+ $(SRC_DIR)/commands/math/basicSymbols.ts \
+ $(SRC_DIR)/commands/math/commands.ts \
+ $(SRC_DIR)/commands/math/LatexCommandInput.ts
+
SOURCES_BASIC = \
$(BASE_SOURCES) \
- $(SRC_DIR)/commands/math.js \
- $(SRC_DIR)/commands/math/basicSymbols.js \
- $(SRC_DIR)/commands/math/commands.js
+ $(SRC_DIR)/commands/math.ts \
+ $(SRC_DIR)/commands/math/basicSymbols.ts \
+ $(SRC_DIR)/commands/math/commands.ts
CSS_DIR = $(SRC_DIR)/css
CSS_MAIN = $(CSS_DIR)/main.less
@@ -113,10 +115,8 @@ font: $(FONT_TARGET)
clean:
rm -rf $(BUILD_DIR)
-$(PJS_SRC): $(NODE_MODULES_INSTALLED)
-
$(BUILD_JS): $(INTRO) $(SOURCES_FULL) $(OUTRO) $(BUILD_DIR_EXISTS)
- cat $^ | ./script/escape-non-ascii > $@
+ cat $^ | ./script/escape-non-ascii | ./script/tsc-emit-only > $@
perl -pi -e s/mq-/$(MQ_CLASS_PREFIX)mq-/g $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@
@@ -124,7 +124,7 @@ $(UGLY_JS): $(BUILD_JS) $(NODE_MODULES_INSTALLED)
$(UGLIFY) $(UGLIFY_OPTS) < $< > $@
$(BASIC_JS): $(INTRO) $(SOURCES_BASIC) $(OUTRO) $(BUILD_DIR_EXISTS)
- cat $^ | ./script/escape-non-ascii > $@
+ cat $^ | ./script/escape-non-ascii | ./script/tsc-emit-only > $@
perl -pi -e s/mq-/$(MQ_CLASS_PREFIX)mq-/g $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@
@@ -157,14 +157,20 @@ $(FONT_TARGET): $(FONT_SOURCE) $(BUILD_DIR_EXISTS)
#
# -*- Test tasks -*-
#
+.PHONY:
+lint:
+ npx tsc --noEmit
-.PHONY: test server run-server
+.PHONY: test server benchmark
server:
node script/test_server.js
test: dev $(BUILD_TEST) $(BASIC_JS) $(BASIC_CSS)
@echo
@echo "** now open test/{unit,visual}.html in your browser to run the {unit,visual} tests. **"
+benchmark: dev $(BUILD_TEST) $(BASIC_JS) $(BASIC_CSS)
+ @echo
+ @echo "** now open benchmark/select.html in your browser. **"
$(BUILD_TEST): $(INTRO) $(SOURCES_FULL) $(UNIT_TESTS) $(OUTRO) $(BUILD_DIR_EXISTS)
- cat $^ > $@
+ cat $^ | ./script/tsc-emit-only > $@
perl -pi -e s/{VERSION}/v$(VERSION)/ $@
diff --git a/benchmark/select.html b/benchmark/select.html
new file mode 100644
index 000000000..e77904d66
--- /dev/null
+++ b/benchmark/select.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+MathQuill Select benchmark
+
+
+
+
+
+
+Benchmark inserting and then selecting n characters
+
+
+
+
+
+
+
+ | nchars |
+ render (ms) |
+ select (ms) |
+
+
+
+
+
+
+
+
diff --git a/docs/Config.md b/docs/Config.md
index b8125ef49..5f2736e12 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -101,24 +101,24 @@ For example, [Desmos](https://www.desmos.com/calculator) substitutes `')[0], { handlers: this });
$(math.el()).appendTo(this.el);
math.data.i = this.maths.length;
this.maths.push(math);
};
- _.moveOutOf = function(dir, math) {
+ moveOutOf (dir, math) {
var adjacentI = (dir === MQ.L ? math.data.i - 1 : math.data.i + 1);
var adjacentMath = this.maths[adjacentI];
if (adjacentMath) adjacentMath.focus().moveToDirEnd(-dir);
};
...
-});
+};
```
It's common to just ignore the last argument, like if the handlers close over the math field:
diff --git a/package-lock.json b/package-lock.json
index 489f69df9..6c41440cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -651,12 +651,6 @@
"dev": true,
"optional": true
},
- "pjs": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pjs/-/pjs-4.0.0.tgz",
- "integrity": "sha1-aMp9me0z1KZSuLe0P5lvOR71Efk=",
- "dev": true
- },
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
@@ -826,6 +820,12 @@
"dev": true,
"optional": true
},
+ "typescript": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
+ "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
+ "dev": true
+ },
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
diff --git a/package.json b/package.json
index 475a7c727..62ef80687 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"devDependencies": {
"less": ">=1.5.1 <3.0.0",
"mocha": ">=2.4.1",
- "pjs": ">=3.1.0 <5.0.0",
+ "typescript": "^4.5.2",
"uglify-js": "2.x"
}
}
diff --git a/script/tsc-emit-only b/script/tsc-emit-only
new file mode 100755
index 000000000..527ec982d
--- /dev/null
+++ b/script/tsc-emit-only
@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+
+var ts = require('typescript');
+
+function compileTypescript (tsSource) {
+ compilerOptions = {
+ target: 'es5'
+ };
+
+ var jsSource = ts.transpileModule(tsSource, {
+ compilerOptions: compilerOptions
+ }).outputText;
+
+ return jsSource;
+}
+
+let contents = '';
+
+process.stdin.setEncoding('utf8');
+process.stdin.on('data', function(data) {
+ contents += data;
+});
+process.stdin.on('end', function () {
+ var ts = compileTypescript(contents);
+ console.log(ts);
+})
+process.stdin.resume();
diff --git a/src/commands/math.js b/src/commands/math.ts
similarity index 59%
rename from src/commands/math.js
rename to src/commands/math.ts
index 48bfd0e8d..24dea4b82 100644
--- a/src/commands/math.js
+++ b/src/commands/math.ts
@@ -4,13 +4,11 @@
/**
* Math tree node base class.
- * Some math-tree-specific extensions to Node.
+ * Some math-tree-specific extensions to MQNode.
* Both MathBlock's and MathCommand's descend from it.
*/
-var MathElement = P(Node, function(_, super_) {
- _.finalizeInsert = function(options, cursor) { // `cursor` param is only for
- // SupSub::contactWeld, and is deliberately only passed in by writeLatex,
- // see ea7307eb4fac77c149a11ffdf9a831df85247693
+class MathElement extends MQNode {
+ finalizeInsert (options:CursorOptions, cursor:Cursor) {
var self = this;
self.postOrder(function (node) { node.finalizeTree(options) });
self.postOrder(function (node) { node.contactWeld(cursor) });
@@ -19,17 +17,22 @@ var MathElement = P(Node, function(_, super_) {
// empty elements need the empty box provided by blur to
// be present in order for their dimensions to be measured
// correctly by 'reflow' handlers.
- self.postOrder(function (node) { node.blur(); });
+ self.postOrder(function (node) { node.blur(cursor); });
self.postOrder(function (node) { node.reflow(); });
- if (self[R].siblingCreated) self[R].siblingCreated(options, L);
- if (self[L].siblingCreated) self[L].siblingCreated(options, R);
- self.bubble(function (node) { node.reflow(); });
+ var selfR = self[R];
+ var selfL = self[L];
+ if (selfR) selfR.siblingCreated(options, L);
+ if (selfL) selfL.siblingCreated(options, R);
+ self.bubble(function (node) {
+ node.reflow();
+ return undefined;
+ });
};
// If the maxDepth option is set, make sure
// deeply nested content is truncated. Just return
// false if the cursor is already too deep.
- _.prepareInsertionAt = function(cursor) {
+ prepareInsertionAt (cursor:Cursor) {
var maxDepth = cursor.options.maxDepth;
if (maxDepth !== undefined) {
var cursorDepth = cursor.depth();
@@ -42,99 +45,104 @@ var MathElement = P(Node, function(_, super_) {
};
// Remove nodes that are more than `cutoff`
// blocks deep from this node.
- _.removeNodesDeeperThan = function (cutoff) {
+ removeNodesDeeperThan (cutoff:number) {
var depth = 0;
- var queue = [[this, depth]];
- var current;
+ var queue:[[MQNode, number]] = [[this, depth]];
+ var current:[MQNode, number] | undefined;
// Do a breadth-first search of this node's descendants
// down to cutoff, removing anything deeper.
- while (queue.length) {
- current = queue.shift();
- current[0].children().each(function (child) {
+ while (current = queue.shift()) {
+ var c = current;
+ c[0].children().each(function (child) {
var i = (child instanceof MathBlock) ? 1 : 0;
- depth = current[1]+i;
+ depth = c[1]+i;
if (depth <= cutoff) {
queue.push([child, depth]);
} else {
(i ? child.children() : child).remove();
}
+ return undefined;
});
}
};
-});
+}
/**
* Commands and operators, like subscripts, exponents, or fractions.
* Descendant commands are organized into blocks.
*/
-var MathCommand = P(MathElement, function(_, super_) {
- _.init = function(ctrlSeq, htmlTemplate, textTemplate) {
- var cmd = this;
- super_.init.call(cmd);
+class MathCommand extends MathElement {
+ replacedFragment:Fragment | undefined;
- if (!cmd.ctrlSeq) cmd.ctrlSeq = ctrlSeq;
- if (htmlTemplate) cmd.htmlTemplate = htmlTemplate;
- if (textTemplate) cmd.textTemplate = textTemplate;
- };
+ constructor (ctrlSeq?:string, htmlTemplate?:string, textTemplate?:string[]) {
+ super();
+ this.setCtrlSeqHtmlAndText(ctrlSeq, htmlTemplate, textTemplate);
+ }
+
+ setCtrlSeqHtmlAndText (ctrlSeq?:string, htmlTemplate?:string, textTemplate?:string[]) {
+ if (!this.ctrlSeq) this.ctrlSeq = ctrlSeq;
+ if (htmlTemplate) this.htmlTemplate = htmlTemplate;
+ if (textTemplate) this.textTemplate = textTemplate;
+ }
// obvious methods
- _.replaces = function(replacedFragment) {
+ replaces (replacedFragment:Fragment) {
replacedFragment.disown();
this.replacedFragment = replacedFragment;
};
- _.isEmpty = function() {
+ isEmpty () {
return this.foldChildren(true, function(isEmpty, child) {
return isEmpty && child.isEmpty();
});
};
- _.parser = function() {
+ parser ():Parser {
var block = latexMathParser.block;
- var self = this;
- return block.times(self.numBlocks()).map(function(blocks) {
- self.blocks = blocks;
+ return block.times(this.numBlocks()).map((blocks) => {
+ this.blocks = blocks;
for (var i = 0; i < blocks.length; i += 1) {
- blocks[i].adopt(self, self.ends[R], 0);
+ blocks[i].adopt(this, this.ends[R], 0);
}
- return self;
+ return this;
});
};
// createLeftOf(cursor) and the methods it calls
- _.createLeftOf = function(cursor) {
+ createLeftOf (cursor:Cursor) {
var cmd = this;
var replacedFragment = cmd.replacedFragment;
cmd.createBlocks();
- super_.createLeftOf.call(cmd, cursor);
+ super.createLeftOf(cursor);
if (replacedFragment) {
- replacedFragment.adopt(cmd.ends[L], 0, 0);
- replacedFragment.jQ.appendTo(cmd.ends[L].jQ);
+ const cmdEndsL = cmd.ends[L] as MQNode;
+ replacedFragment.adopt(cmdEndsL, 0, 0);
+ replacedFragment.jQ.appendTo(cmdEndsL.jQ);
cmd.placeCursor(cursor);
cmd.prepareInsertionAt(cursor);
}
- cmd.finalizeInsert(cursor.options);
+ cmd.finalizeInsert(cursor.options, cursor);
cmd.placeCursor(cursor);
};
- _.createBlocks = function() {
+ createBlocks () {
var cmd = this,
numBlocks = cmd.numBlocks(),
blocks = cmd.blocks = Array(numBlocks);
for (var i = 0; i < numBlocks; i += 1) {
- var newBlock = blocks[i] = MathBlock();
+ var newBlock = blocks[i] = new MathBlock();
newBlock.adopt(cmd, cmd.ends[R], 0);
}
};
- _.placeCursor = function(cursor) {
+ placeCursor (cursor:Cursor) {
//insert the cursor at the right end of the first empty child, searching
//left-to-right, or if none empty, the right end child
- cursor.insAtRightEnd(this.foldChildren(this.ends[L], function(leftward, child) {
+ cursor.insAtRightEnd(this.foldChildren(this.ends[L] as MQNode, function(leftward, child) {
return leftward.isEmpty() ? leftward : child;
}));
};
@@ -142,31 +150,42 @@ var MathCommand = P(MathElement, function(_, super_) {
// editability methods: called by the cursor for editing, cursor movements,
// and selection of the MathQuill tree, these all take in a direction and
// the cursor
- _.moveTowards = function(dir, cursor, updown) {
- var updownInto = updown && this[updown+'Into'];
- cursor.insAtDirEnd(-dir, updownInto || this.ends[-dir]);
- aria.queueDirEndOf(-dir).queue(cursor.parent, true);
+ moveTowards (dir:Direction, cursor:Cursor, updown?:'up'|'down') {
+ var updownInto:NodeRef | undefined;
+ if (updown === 'up') {
+ updownInto = this.upInto;
+ } else if (updown === 'down') {
+ updownInto = this.downInto;
+ }
+
+ const el = (updownInto || this.ends[-dir as Direction]) as MQNode;
+ cursor.insAtDirEnd(-dir as Direction, el);
+ cursor.controller.aria.queueDirEndOf(-dir as Direction).queue(cursor.parent, true);
};
- _.deleteTowards = function(dir, cursor) {
+ deleteTowards (dir:Direction, cursor:Cursor) {
if (this.isEmpty()) cursor[dir] = this.remove()[dir];
- else this.moveTowards(dir, cursor, null);
+ else this.moveTowards(dir, cursor);
};
- _.selectTowards = function(dir, cursor) {
- cursor[-dir] = this;
+ selectTowards (dir:Direction, cursor:Cursor) {
+ cursor[-dir as Direction] = this;
cursor[dir] = this[dir];
};
- _.selectChildren = function() {
- return Selection(this, this);
- };
- _.unselectInto = function(dir, cursor) {
- cursor.insAtDirEnd(-dir, cursor.anticursor.ancestors[this.id]);
- };
- _.seek = function(pageX, cursor) {
- function getBounds(node) {
- var bounds = {}
- bounds[L] = node.jQ.offset().left;
- bounds[R] = bounds[L] + node.jQ.outerWidth();
- return bounds;
+ selectChildren ():MQSelection {
+ return new MQSelection(this, this);
+ };
+ unselectInto (dir:Direction, cursor:Cursor) {
+ const antiCursor = cursor.anticursor as Anticursor;
+ const ancestor = antiCursor.ancestors[this.id] as MQNode;
+ cursor.insAtDirEnd(-dir as Direction, ancestor);
+ };
+ seek (pageX:number, cursor:Cursor) {
+ function getBounds(node:MQNode) {
+ var l:number = node.jQ.offset().left;
+ var r:number = l + node.jQ.outerWidth();
+ return {
+ [L]: l,
+ [R]: r
+ }
}
var cmd = this;
@@ -181,7 +200,7 @@ var MathCommand = P(MathElement, function(_, super_) {
if (pageX < blockBounds[L]) {
// closer to this block's left bound, or the bound left of that?
if (pageX - leftLeftBound < blockBounds[L] - pageX) {
- if (block[L]) cursor.insAtRightEnd(block[L]);
+ if (block[L]) cursor.insAtRightEnd(block[L] as MQNode);
else cursor.insLeftOf(cmd);
}
else cursor.insAtLeftEnd(block);
@@ -196,12 +215,15 @@ var MathCommand = P(MathElement, function(_, super_) {
}
else cursor.insAtRightEnd(block);
}
+ return undefined;
}
else {
block.seek(pageX, cursor);
return false;
}
});
+
+ return undefined;
}
// methods involved in creating and cross-linking with HTML DOM nodes
@@ -230,11 +252,11 @@ var MathCommand = P(MathElement, function(_, super_) {
Note that & isn't well-formed HTML; if you wanted a literal '&123',
your HTML template would have to have '&123'.
*/
- _.numBlocks = function() {
- var matches = this.htmlTemplate.match(/&\d+/g);
+ numBlocks () {
+ var matches = (this.htmlTemplate as string).match(/&\d+/g);
return matches ? matches.length : 0;
};
- _.html = function() {
+ html () {
// Render the entire math subtree rooted at this command, as HTML.
// Expects .createBlocks() to have been called already, since it uses the
// .blocks array of child blocks.
@@ -271,9 +293,9 @@ var MathCommand = P(MathElement, function(_, super_) {
// production without pray(), because it will then TypeError on .slice().
var cmd = this;
- var blocks = cmd.blocks;
+ var blocks = cmd.blocks as MathBlock[];
var cmdId = ' mathquill-command-id=' + cmd.id;
- var tokens = cmd.htmlTemplate.match(/<[^<>]+>|[^<>]+/g);
+ var tokens = (cmd.htmlTemplate as string).match(/<[^<>]+>|[^<>]+/g) as string[];
pray('no unmatched angle brackets', tokens.join('') === this.htmlTemplate);
@@ -307,19 +329,20 @@ var MathCommand = P(MathElement, function(_, super_) {
} while (nesting > 0);
}
}
- return tokens.join('').replace(/>&(\d+)/g, function($0, $1) {
- return ' mathquill-block-id=' + blocks[$1].id + ' aria-hidden="true">' + blocks[$1].join('html');
+ return tokens.join('').replace(/>&(\d+)/g, function(_$0:string, $1:string) {
+ var num1 = parseInt($1, 10);
+ return ' mathquill-block-id=' + blocks[num1].id + ' aria-hidden="true">' + blocks[num1].join('html');
});
};
// methods to export a string representation of the math tree
- _.latex = function() {
- return this.foldChildren(this.ctrlSeq, function(latex, child) {
+ latex () {
+ return this.foldChildren(this.ctrlSeq || '', function(latex, child) {
return latex + '{' + (child.latex() || ' ') + '}';
});
};
- _.textTemplate = [''];
- _.text = function() {
+ textTemplate = [''];
+ text () {
var cmd = this, i = 0;
return cmd.foldChildren(cmd.textTemplate[i], function(text, child) {
i += 1;
@@ -330,100 +353,122 @@ var MathCommand = P(MathElement, function(_, super_) {
return text + child_text + (cmd.textTemplate[i] || '');
});
};
- _.mathspeakTemplate = [];
- _.mathspeak = function() {
+ mathspeakTemplate = [''];
+ mathspeak () {
var cmd = this, i = 0;
return cmd.foldChildren(cmd.mathspeakTemplate[i] || 'Start'+cmd.ctrlSeq+' ', function(speech, block) {
i += 1;
return speech + ' ' + block.mathspeak() + ' ' + (cmd.mathspeakTemplate[i]+' ' || 'End'+cmd.ctrlSeq+' ');
});
};
-});
+};
/**
* Lightweight command without blocks or children.
*/
-var Symbol = P(MathCommand, function(_, super_) {
- _.init = function(ctrlSeq, html, text, mathspeak) {
- if (!text && !!ctrlSeq) text = ctrlSeq.replace(/^\\/, '');
+class MQSymbol extends MathCommand {
+ constructor (ctrlSeq?:string, html?:string, text?:string, mathspeak?:string) {
+ super();
+ this.setCtrlSeqHtmlTextAndMathspeak(ctrlSeq, html, text, mathspeak);
+ };
+
+ setCtrlSeqHtmlTextAndMathspeak (ctrlSeq?:string, html?:string, text?:string, mathspeak?:string) {
+ if (!text && !!ctrlSeq) {
+ text = ctrlSeq.replace(/^\\/, '');
+ }
this.mathspeakName = mathspeak || text;
- super_.init.call(this, ctrlSeq, html, [ text ]);
- };
+ super.setCtrlSeqHtmlAndText(ctrlSeq, html, [text || '']);
+ }
- _.parser = function() { return Parser.succeed(this); };
- _.numBlocks = function() { return 0; };
+ parser () { return Parser.succeed(this); };
+ numBlocks () { return 0; };
- _.replaces = function(replacedFragment) {
+ replaces (replacedFragment:Fragment) {
replacedFragment.remove();
};
- _.createBlocks = noop;
+ createBlocks () {};
- _.moveTowards = function(dir, cursor) {
+ moveTowards (dir:Direction, cursor:Cursor) {
cursor.jQ.insDirOf(dir, this.jQ);
- cursor[-dir] = this;
+ cursor[-dir as Direction] = this;
cursor[dir] = this[dir];
- aria.queue(this);
+ cursor.controller.aria.queue(this);
};
- _.deleteTowards = function(dir, cursor) {
+ deleteTowards (dir:Direction, cursor:Cursor) {
cursor[dir] = this.remove()[dir];
};
- _.seek = function(pageX, cursor) {
+ seek (pageX:number, cursor:Cursor) {
// insert at whichever side the click was closer to
if (pageX - this.jQ.offset().left < this.jQ.outerWidth()/2)
cursor.insLeftOf(this);
else
cursor.insRightOf(this);
- };
- _.latex = function(){ return this.ctrlSeq; };
- _.text = function(){ return this.textTemplate.join(''); };
- _.mathspeak = function(){ return this.mathspeakName; };
- _.placeCursor = noop;
- _.isEmpty = function(){ return true; };
-});
-var VanillaSymbol = P(Symbol, function(_, super_) {
- _.init = function(ch, html, mathspeak) {
- super_.init.call(this, ch, ''+(html || ch)+'', undefined, mathspeak);
+ return cursor;
};
-});
-var BinaryOperator = P(Symbol, function(_, super_) {
- _.init = function(ctrlSeq, html, text, mathspeak) {
- super_.init.call(this,
- ctrlSeq, ''+html+'', text, mathspeak
- );
+
+ latex (){ return this.ctrlSeq || ''; };
+ text (){ return this.textTemplate.join(''); };
+ mathspeak (_opts?:MathspeakOptions){ return this.mathspeakName || ''; };
+ placeCursor () {};
+ isEmpty (){ return true; };
+};
+class VanillaSymbol extends MQSymbol {
+ constructor (ch:string, html?:string, mathspeak?:string) {
+ super(ch, ''+(html || ch)+'', undefined, mathspeak);
+ };
+}
+function bindVanillaSymbol (ch:string, html?:string, mathspeak?:string) {
+ return () => new VanillaSymbol(ch, html, mathspeak);
+}
+
+class BinaryOperator extends MQSymbol {
+ constructor (ctrlSeq?:string, html?:string, text?:string, mathspeak?:string, treatLikeSymbol?:boolean) {
+ if (treatLikeSymbol) {
+ super(ctrlSeq, ''+(html || ctrlSeq)+'', undefined, mathspeak);
+ } else {
+ super(ctrlSeq, ''+html+'', text, mathspeak);
+ }
};
-});
+};
+function bindBinaryOperator (ctrlSeq?:string, html?:string, text?:string, mathspeak?:string) {
+ return () => new BinaryOperator(ctrlSeq, html, text, mathspeak);
+}
/**
* Children and parent of MathCommand's. Basically partitions all the
* symbols and operators that descend (in the Math DOM tree) from
* ancestor operators.
*/
-var MathBlock = P(MathElement, function(_, super_) {
- _.join = function(methodName) {
+class MathBlock extends MathElement {
+ controller?:Controller;
+
+ join (methodName:JoinMethod) {
return this.foldChildren('', function(fold, child) {
return fold + child[methodName]();
});
};
- _.html = function() { return this.join('html'); };
- _.latex = function() { return this.join('latex'); };
- _.text = function() {
- return (this.ends[L] === this.ends[R] && this.ends[L] !== 0) ?
- this.ends[L].text() :
+ html () { return this.join('html'); };
+ latex () { return this.join('latex'); };
+ text () {
+ var endsL = this.ends[L];
+ var endsR = this.ends[R];
+ return (endsL === endsR && endsL !== 0) ?
+ endsL.text() :
this.join('text')
;
};
- _.mathspeak = function() {
+ mathspeak () {
var tempOp = '';
- var autoOps = {};
+ var autoOps:CursorOptions['autoOperatorNames'] = {};
if (this.controller) autoOps = this.controller.options.autoOperatorNames;
- return this.foldChildren([], function(speechArray, cmd) {
+ return this.foldChildren([], function(speechArray, cmd) {
if (cmd.isPartOfOperator) {
tempOp += cmd.mathspeak();
} else {
if(tempOp!=='') {
- if(autoOps !== {} && autoOps._maxLength > 0) {
+ if(autoOps._maxLength! > 0) {
var x = autoOps[tempOp.toLowerCase()];
if(typeof x === 'string') tempOp = x;
}
@@ -433,7 +478,7 @@ var MathBlock = P(MathElement, function(_, super_) {
var mathspeakText = cmd.mathspeak();
var cmdText = cmd.ctrlSeq;
if (
- isNaN(cmdText) &&
+ isNaN(cmdText as any) && // TODO - revisit this to improve the isNumber() check
cmdText !== '.' &&
(!cmd.parent || !cmd.parent.parent || !cmd.parent.parent.isTextBlock())
) {
@@ -448,147 +493,168 @@ var MathBlock = P(MathElement, function(_, super_) {
// For Apple devices in particular, split out digits after a decimal point so they aren't read aloud as whole words.
// Not doing so makes 123.456 potentially spoken as "one hundred twenty-three point four hundred fifty-six."
// Instead, add spaces so it is spoken as "one hundred twenty-three point four five six."
- .replace(/(\.)([0-9]+)/g, function(match, p1, p2) {
+ .replace(/(\.)([0-9]+)/g, function(_match, p1, p2) {
return p1 + p2.split('').join(' ').trim();
});
};
- _.ariaLabel = 'block';
- _.keystroke = function(key, e, ctrlr) {
+ ariaLabel = 'block';
+
+ keystroke (key:string, e:KeyboardEvent, ctrlr:Controller) {
if (ctrlr.options.spaceBehavesLikeTab
&& (key === 'Spacebar' || key === 'Shift-Spacebar')) {
e.preventDefault();
ctrlr.escapeDir(key === 'Shift-Spacebar' ? L : R, key, e);
return;
}
- return super_.keystroke.apply(this, arguments);
+ return super.keystroke(key, e, ctrlr);
};
// editability methods: called by the cursor for editing, cursor movements,
// and selection of the MathQuill tree, these all take in a direction and
// the cursor
- _.moveOutOf = function(dir, cursor, updown) {
- var updownInto = updown && this.parent[updown+'Into'];
+ moveOutOf (dir:Direction, cursor:Cursor, updown?:'up'|'down') {
+ var updownInto:NodeRef | undefined;
+ if (updown === 'up') {
+ updownInto = this.parent.upInto;
+ } else if (updown === 'down') {
+ updownInto = this.parent.downInto;
+ }
+
if (!updownInto && this[dir]) {
- cursor.insAtDirEnd(-dir, this[dir]);
- aria.queueDirEndOf(-dir).queue(cursor.parent, true);
+ const otherDir = -dir as Direction;
+ cursor.insAtDirEnd(otherDir, this[dir] as MQNode);
+ cursor.controller.aria.queueDirEndOf(otherDir).queue(cursor.parent, true);
}
else {
cursor.insDirOf(dir, this.parent);
- aria.queueDirOf(dir).queue(this.parent);
+ cursor.controller.aria.queueDirOf(dir).queue(this.parent);
}
};
- _.selectOutOf = function(dir, cursor) {
+ selectOutOf (dir:Direction, cursor:Cursor) {
cursor.insDirOf(dir, this.parent);
};
- _.deleteOutOf = function(dir, cursor) {
+ deleteOutOf (_dir:Direction, cursor:Cursor) {
cursor.unwrapGramp();
};
- _.seek = function(pageX, cursor) {
+ seek (pageX:number, cursor:Cursor) {
var node = this.ends[R];
if (!node || node.jQ.offset().left + node.jQ.outerWidth() < pageX) {
return cursor.insAtRightEnd(this);
}
- if (pageX < this.ends[L].jQ.offset().left) return cursor.insAtLeftEnd(this);
- while (pageX < node.jQ.offset().left) node = node[L];
+
+ var endsL = this.ends[L] as MQNode;
+ if (pageX < endsL.jQ.offset().left) return cursor.insAtLeftEnd(this);
+ while (pageX < node.jQ.offset().left) node = node[L] as MQNode;
return node.seek(pageX, cursor);
};
- _.chToCmd = function(ch, options) {
+ chToCmd (ch:string, options:CursorOptions) {
var cons;
// exclude f because it gets a dedicated command with more spacing
if (ch.match(/^[a-eg-zA-Z]$/))
- return Letter(ch);
+ return new Letter(ch);
else if (/^\d$/.test(ch))
- return Digit(ch);
+ return new Digit(ch);
else if (options && options.typingSlashWritesDivisionSymbol && ch === '/')
- return LatexCmds['÷'](ch);
+ return (LatexCmds as LatexCmdsSingleCharBuilder)['÷'](ch);
else if (options && options.typingAsteriskWritesTimesSymbol && ch === '*')
- return LatexCmds['×'](ch);
+ return (LatexCmds as LatexCmdsSingleCharBuilder)['×'](ch);
else if (options && options.typingPercentWritesPercentOf && ch === '%')
- return LatexCmds.percentof(ch);
- else if (cons = CharCmds[ch] || LatexCmds[ch])
- return cons(ch);
+ return (LatexCmds as LatexCmdsSingleCharBuilder).percentof(ch);
+ else if (cons = (CharCmds as CharCmdsAny)[ch] || (LatexCmds as LatexCmdsAny)[ch]) {
+ if (cons.constructor) {
+ return new cons(ch);
+ } else {
+ return cons(ch);
+ }
+ }
else
- return VanillaSymbol(ch);
+ return new VanillaSymbol(ch);
};
- _.write = function(cursor, ch) {
+ write (cursor:Cursor, ch:string) {
var cmd = this.chToCmd(ch, cursor.options);
if (cursor.selection) cmd.replaces(cursor.replaceSelection());
if (!cursor.isTooDeep()) {
cmd.createLeftOf(cursor.show());
// special-case the slash so that fractions are voiced while typing
if (ch === '/') {
- aria.alert('over');
+ cursor.controller.aria.alert('over');
} else {
- aria.alert(cmd.mathspeak({ createdLeftOf: cursor }));
+ cursor.controller.aria.alert(cmd.mathspeak({ createdLeftOf: cursor }));
}
}
};
- _.writeLatex = function(cursor, latex) {
+ writeLatex (cursor:Cursor, latex:string) {
var all = Parser.all;
var eof = Parser.eof;
- var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex);
+ var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex);
if (block && !block.isEmpty() && block.prepareInsertionAt(cursor)) {
- block.children().adopt(cursor.parent, cursor[L], cursor[R]);
+ block.children().adopt(cursor.parent, cursor[L] as NodeRef, cursor[R] as NodeRef); // TODO - masking undefined. should be 0
var jQ = block.jQize();
jQ.insertBefore(cursor.jQ);
cursor[L] = block.ends[R];
block.finalizeInsert(cursor.options, cursor);
- if (block.ends[R][R].siblingCreated) block.ends[R][R].siblingCreated(cursor.options, L);
- if (block.ends[L][L].siblingCreated) block.ends[L][L].siblingCreated(cursor.options, R);
- cursor.parent.bubble(function (node) { node.reflow(); });
+ var blockEndsR = block.ends[R];
+ var blockEndsL = block.ends[L];
+ var blockEndsRR = (blockEndsR as MQNode)[R];
+ var blockEndsLL = (blockEndsL as MQNode)[L];
+ if (blockEndsRR) blockEndsRR.siblingCreated(cursor.options, L);
+ if (blockEndsLL) blockEndsLL.siblingCreated(cursor.options, R);
+ cursor.parent.bubble(function (node) {
+ node.reflow();
+ return undefined;
+ });
}
};
- _.focus = function() {
+ focus () {
this.jQ.addClass('mq-hasCursor');
this.jQ.removeClass('mq-empty');
return this;
};
- _.blur = function() {
+ blur (cursor:Cursor) {
this.jQ.removeClass('mq-hasCursor');
if (this.isEmpty()) {
this.jQ.addClass('mq-empty');
- if (this.isEmptyParens()) {
- this.jQ.addClass('mq-empty-parens');
- } else if (this.isEmptySquareBrackets()) {
- this.jQ.addClass('mq-empty-square-brackets');
+ if (cursor && this.isQuietEmptyDelimiter(cursor.options.quietEmptyDelimiters)) {
+ this.jQ.addClass('mq-quiet-delimiter');
}
}
return this;
};
-});
+}
+
+Options.prototype.mouseEvents = true;
+API.StaticMath = function(APIClasses:APIClasses) {
+ return class StaticMath extends APIClasses.AbstractMathQuill {
+ static RootBlock = MathBlock;
-Options.p.mouseEvents = true;
-API.StaticMath = function(APIClasses) {
- return P(APIClasses.AbstractMathQuill, function(_, super_) {
- this.RootBlock = MathBlock;
- _.__mathquillify = function(opts, interfaceVersion) {
+ __mathquillify (opts:CursorOptions, _interfaceVersion:number) {
this.config(opts);
- super_.__mathquillify.call(this, 'mq-math-mode');
+ super.__mathquillify('mq-math-mode');
if (this.__options.mouseEvents) {
this.__controller.delegateMouseEvents();
this.__controller.staticMathTextareaEvents();
}
return this;
};
- _.init = function() {
- super_.init.apply(this, arguments);
+ constructor (el:MQNode) {
+ super(el);
var innerFields = this.innerFields = [];
- this.__controller.root.postOrder(function (node) {
+ this.__controller.root.postOrder(function (node:MQNode) {
node.registerInnerField(innerFields, APIClasses.InnerMathField);
});
};
- _.latex = function() {
- var returned = super_.latex.apply(this, arguments);
+ latex () {
+ var returned = super.latex.apply(this, arguments);
if (arguments.length > 0) {
var innerFields = this.innerFields = [];
- this.__controller.root.postOrder(function (node) {
+ this.__controller.root.postOrder(function (node:MQNode) {
node.registerInnerField(innerFields, APIClasses.InnerMathField);
});
// Force an ARIA label update to remain in sync with the new LaTeX value.
@@ -596,43 +662,46 @@ API.StaticMath = function(APIClasses) {
}
return returned;
};
- _.setAriaLabel = function(ariaLabel) {
+ setAriaLabel (ariaLabel:string) {
this.__controller.setAriaLabel(ariaLabel);
return this;
};
- _.getAriaLabel = function () {
+ getAriaLabel () {
return this.__controller.getAriaLabel();
};
- });
+ };
};
-var RootMathBlock = P(MathBlock, RootBlockMixin);
-API.MathField = function(APIClasses) {
- return P(APIClasses.EditableField, function(_, super_) {
- this.RootBlock = RootMathBlock;
- _.__mathquillify = function(opts, interfaceVersion) {
+class RootMathBlock extends MathBlock {}
+RootBlockMixin(RootMathBlock.prototype); // adds methods to RootMathBlock
+
+API.MathField = function(APIClasses:APIClasses) {
+ return class MathField extends APIClasses.EditableField {
+ static RootBlock = RootMathBlock;
+
+ __mathquillify (opts:CursorOptions, interfaceVersion:number) {
this.config(opts);
if (interfaceVersion > 1) this.__controller.root.reflow = noop;
- super_.__mathquillify.call(this, 'mq-editable-field mq-math-mode');
+ super.__mathquillify('mq-editable-field mq-math-mode');
delete this.__controller.root.reflow;
return this;
};
- });
+ };
};
-API.InnerMathField = function(APIClasses) {
- return P(APIClasses.MathField, function(_, super_) {
- _.makeStatic = function() {
+API.InnerMathField = function(APIClasses:APIClasses) {
+ return class extends APIClasses.MathField {
+ makeStatic () {
this.__controller.editable = false;
this.__controller.root.blur();
this.__controller.unbindEditablesEvents();
this.__controller.container.removeClass('mq-editable-field');
};
- _.makeEditable = function() {
+ makeEditable () {
this.__controller.editable = true;
this.__controller.editablesTextareaEvents();
this.__controller.cursor.insAtRightEnd(this.__controller.root);
this.__controller.container.addClass('mq-editable-field');
};
- });
+ };
};
diff --git a/src/commands/math/LatexCommandInput.js b/src/commands/math/LatexCommandInput.js
deleted file mode 100644
index dca839ab7..000000000
--- a/src/commands/math/LatexCommandInput.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/****************************************
- * Input box to type backslash commands
- ***************************************/
-
-var LatexCommandInput =
-CharCmds['\\'] = P(MathCommand, function(_, super_) {
- _.ctrlSeq = '\\';
- _.replaces = function(replacedFragment) {
- this._replacedFragment = replacedFragment.disown();
- this.isEmpty = function() { return false; };
- };
- _.htmlTemplate = '\\&0';
- _.textTemplate = ['\\'];
- _.createBlocks = function() {
- super_.createBlocks.call(this);
- this.ends[L].focus = function() {
- this.parent.jQ.addClass('mq-hasCursor');
- if (this.isEmpty())
- this.parent.jQ.removeClass('mq-empty');
-
- return this;
- };
- this.ends[L].blur = function() {
- this.parent.jQ.removeClass('mq-hasCursor');
- if (this.isEmpty())
- this.parent.jQ.addClass('mq-empty');
-
- return this;
- };
- this.ends[L].write = function(cursor, ch) {
- cursor.show().deleteSelection();
-
- if (ch.match(/[a-z]/i)) {
- VanillaSymbol(ch).createLeftOf(cursor);
- // TODO needs tests
- aria.alert(ch);
- }
- else {
- var cmd = this.parent.renderCommand(cursor);
- // TODO needs tests
- aria.queue(cmd.mathspeak({ createdLeftOf: cursor }));
- if (ch !== '\\' || !this.isEmpty()) cursor.parent.write(cursor, ch);
- else aria.alert();
- }
- };
- this.ends[L].keystroke = function(key, e, ctrlr) {
- if (key === 'Tab' || key === 'Enter' || key === 'Spacebar') {
- var cmd = this.parent.renderCommand(ctrlr.cursor);
- // TODO needs tests
- aria.alert(cmd.mathspeak({ createdLeftOf: ctrlr.cursor }));
- e.preventDefault();
- return;
- }
- return super_.keystroke.apply(this, arguments);
- };
- };
- _.createLeftOf = function(cursor) {
- super_.createLeftOf.call(this, cursor);
-
- if (this._replacedFragment) {
- var el = this.jQ[0];
- this.jQ =
- this._replacedFragment.jQ.addClass('mq-blur').bind(
- 'mousedown mousemove', //FIXME: is monkey-patching the mousedown and mousemove handlers the right way to do this?
- function(e) {
- $(e.target = el).trigger(e);
- return false;
- }
- ).insertBefore(this.jQ).add(this.jQ);
- }
- };
- _.latex = function() {
- return '\\' + this.ends[L].latex() + ' ';
- };
- _.renderCommand = function(cursor) {
- this.jQ = this.jQ.last();
- this.remove();
- if (this[R]) {
- cursor.insLeftOf(this[R]);
- } else {
- cursor.insAtRightEnd(this.parent);
- }
-
- var latex = this.ends[L].latex();
- if (!latex) latex = ' ';
- var cmd = LatexCmds[latex];
- if (cmd) {
- cmd = cmd(latex);
- if (this._replacedFragment) cmd.replaces(this._replacedFragment);
- cmd.createLeftOf(cursor);
- }
- else {
- cmd = TextBlock();
- cmd.replaces(latex);
- cmd.createLeftOf(cursor);
- cursor.insRightOf(cmd);
- if (this._replacedFragment)
- this._replacedFragment.remove();
- }
- return cmd;
- };
-});
-
diff --git a/src/commands/math/LatexCommandInput.ts b/src/commands/math/LatexCommandInput.ts
new file mode 100644
index 000000000..528d57743
--- /dev/null
+++ b/src/commands/math/LatexCommandInput.ts
@@ -0,0 +1,119 @@
+/****************************************
+ * Input box to type backslash commands
+ ***************************************/
+
+CharCmds['\\'] = class LatexCommandInput extends MathCommand {
+ ctrlSeq = '\\';
+ _replacedFragment?:Fragment;
+
+ replaces (replacedFragment:Fragment) {
+ this._replacedFragment = replacedFragment.disown();
+ this.isEmpty = function() { return false; };
+ };
+ htmlTemplate = '\\&0';
+ textTemplate = ['\\'];
+ createBlocks () {
+ super.createBlocks();
+ const endsL = this.ends[L] as MQNode;
+
+ endsL.focus = function() {
+ this.parent.jQ.addClass('mq-hasCursor');
+ if (this.isEmpty())
+ this.parent.jQ.removeClass('mq-empty');
+
+ return this;
+ };
+ endsL.blur = function() {
+ this.parent.jQ.removeClass('mq-hasCursor');
+ if (this.isEmpty())
+ this.parent.jQ.addClass('mq-empty');
+
+ return this;
+ };
+ endsL.write = function(cursor, ch) {
+ cursor.show().deleteSelection();
+
+ if (ch.match(/[a-z]/i)) {
+ new VanillaSymbol(ch).createLeftOf(cursor);
+ // TODO needs tests
+ cursor.controller.aria.alert(ch);
+ }
+ else {
+ var cmd = (this.parent as LatexCommandInput).renderCommand(cursor);
+ // TODO needs tests
+ cursor.controller.aria.queue(cmd.mathspeak({ createdLeftOf: cursor }));
+ if (ch !== '\\' || !this.isEmpty()) cursor.parent.write(cursor, ch);
+ else cursor.controller.aria.alert();
+ }
+ };
+
+ var originalKeystroke = endsL.keystroke;
+ endsL.keystroke = function(key, e, ctrlr) {
+ if (key === 'Tab' || key === 'Enter' || key === 'Spacebar') {
+ var cmd = (this.parent as LatexCommandInput).renderCommand(ctrlr.cursor);
+ // TODO needs tests
+ ctrlr.aria.alert(cmd.mathspeak({ createdLeftOf: ctrlr.cursor }));
+ e.preventDefault();
+ return;
+ }
+
+ return originalKeystroke.call(this, key, e, ctrlr);
+ };
+ };
+ createLeftOf (cursor:Cursor) {
+ super.createLeftOf(cursor);
+
+ if (this._replacedFragment) {
+ var el = this.jQ[0];
+ this.jQ =
+ this._replacedFragment.jQ.addClass('mq-blur').bind(
+ 'mousedown mousemove', //FIXME: is monkey-patching the mousedown and mousemove handlers the right way to do this?
+ function(e) {
+ // TODO - overwritting e.target
+ (e as any).target = el
+ $(el).trigger(e);
+ return false;
+ }
+ ).insertBefore(this.jQ).add(this.jQ);
+ }
+ };
+ latex () {
+ return '\\' + (this.ends[L] as MQNode).latex() + ' ';
+ };
+ renderCommand (cursor:Cursor) {
+ this.jQ = this.jQ.last();
+ this.remove();
+ if (this[R]) {
+ cursor.insLeftOf(this[R] as MQNode);
+ } else {
+ cursor.insAtRightEnd(this.parent);
+ }
+
+ var latex = (this.ends[L] as MQNode).latex();
+ if (!latex) latex = ' ';
+ var cmd = LatexCmds[latex];
+
+ if (cmd) {
+ let node:MQNode;
+ if (isMQNodeClass(cmd)) {
+ node = new (cmd as typeof TempSingleCharNode)(latex);
+ } else {
+ node = cmd(latex);
+ }
+ if (this._replacedFragment) (node as MathCommand).replaces(this._replacedFragment);
+ node.createLeftOf(cursor);
+ return node;
+ }
+ else {
+ const node = new TextBlock();
+ node.replaces(latex);
+ node.createLeftOf(cursor);
+ cursor.insRightOf(node);
+ if (this._replacedFragment) {
+ this._replacedFragment.remove();
+ }
+ return node;
+ }
+ };
+};
+
diff --git a/src/commands/math/advancedSymbols.js b/src/commands/math/advancedSymbols.js
deleted file mode 100644
index 865edfc9c..000000000
--- a/src/commands/math/advancedSymbols.js
+++ /dev/null
@@ -1,338 +0,0 @@
-/************************************
- * Symbols for Advanced Mathematics
- ***********************************/
-
-LatexCmds.notin =
-LatexCmds.cong =
-LatexCmds.equiv =
-LatexCmds.oplus =
-LatexCmds.otimes = P(BinaryOperator, function(_, super_) {
- _.init = function(latex) {
- super_.init.call(this, '\\'+latex+' ', '&'+latex+';');
- };
-});
-
-LatexCmds['∗'] = LatexCmds.ast = LatexCmds.star = LatexCmds.loast = LatexCmds.lowast =
- bind(BinaryOperator,'\\ast ','∗', 'low asterisk');
-LatexCmds.therefor = LatexCmds.therefore =
- bind(BinaryOperator,'\\therefore ','∴', 'therefore');
-
-LatexCmds.cuz = // l33t
-LatexCmds.because = bind(BinaryOperator,'\\because ','∵', 'because');
-
-LatexCmds.prop = LatexCmds.propto = bind(BinaryOperator,'\\propto ','∝', 'proportional to');
-
-LatexCmds['≈'] = LatexCmds.asymp = LatexCmds.approx = bind(BinaryOperator,'\\approx ','≈'), 'approximately equal to';
-
-LatexCmds.isin = LatexCmds['in'] = bind(BinaryOperator,'\\in ','∈', 'is in');
-
-LatexCmds.ni = LatexCmds.contains = bind(BinaryOperator,'\\ni ','∋', 'is not in');
-
-LatexCmds.notni = LatexCmds.niton = LatexCmds.notcontains = LatexCmds.doesnotcontain =
- bind(BinaryOperator,'\\not\\ni ','∌', 'does not contain');
-
-LatexCmds.sub = LatexCmds.subset = bind(BinaryOperator,'\\subset ','⊂', 'subset');
-
-LatexCmds.sup = LatexCmds.supset = LatexCmds.superset =
- bind(BinaryOperator,'\\supset ','⊃', 'superset');
-
-LatexCmds.nsub = LatexCmds.notsub =
-LatexCmds.nsubset = LatexCmds.notsubset =
- bind(BinaryOperator,'\\not\\subset ','⊄', 'not a subset');
-
-LatexCmds.nsup = LatexCmds.notsup =
-LatexCmds.nsupset = LatexCmds.notsupset =
-LatexCmds.nsuperset = LatexCmds.notsuperset =
- bind(BinaryOperator,'\\not\\supset ','⊅', 'not a superset');
-
-LatexCmds.sube = LatexCmds.subeq = LatexCmds.subsete = LatexCmds.subseteq =
- bind(BinaryOperator,'\\subseteq ','⊆', 'subset or equal to');
-
-LatexCmds.supe = LatexCmds.supeq =
-LatexCmds.supsete = LatexCmds.supseteq =
-LatexCmds.supersete = LatexCmds.superseteq =
- bind(BinaryOperator,'\\supseteq ','⊇', 'superset or equal to');
-
-LatexCmds.nsube = LatexCmds.nsubeq =
-LatexCmds.notsube = LatexCmds.notsubeq =
-LatexCmds.nsubsete = LatexCmds.nsubseteq =
-LatexCmds.notsubsete = LatexCmds.notsubseteq =
- bind(BinaryOperator,'\\not\\subseteq ','⊈', 'not subset or equal to');
-
-LatexCmds.nsupe = LatexCmds.nsupeq =
-LatexCmds.notsupe = LatexCmds.notsupeq =
-LatexCmds.nsupsete = LatexCmds.nsupseteq =
-LatexCmds.notsupsete = LatexCmds.notsupseteq =
-LatexCmds.nsupersete = LatexCmds.nsuperseteq =
-LatexCmds.notsupersete = LatexCmds.notsuperseteq =
- bind(BinaryOperator,'\\not\\supseteq ','⊉', 'not superset or equal to');
-
-//the canonical sets of numbers
-LatexCmds.mathbb = P(MathCommand, function(_) {
- _.createLeftOf = noop;
- _.numBlocks = function() { return 1; };
- _.parser = function() {
- var string = Parser.string;
- var regex = Parser.regex;
- var optWhitespace = Parser.optWhitespace;
- return optWhitespace.then(string('{'))
- .then(optWhitespace)
- .then(regex(/^[NPZQRCH]/))
- .skip(optWhitespace)
- .skip(string('}'))
- .map(function(c) {
- // instantiate the class for the matching char
- return LatexCmds[c]();
- });
- };
-});
-
-LatexCmds.N = LatexCmds.naturals = LatexCmds.Naturals =
- bind(VanillaSymbol,'\\mathbb{N}','ℕ', 'naturals');
-
-LatexCmds.P =
-LatexCmds.primes = LatexCmds.Primes =
-LatexCmds.projective = LatexCmds.Projective =
-LatexCmds.probability = LatexCmds.Probability =
- bind(VanillaSymbol,'\\mathbb{P}','ℙ', 'P');
-
-LatexCmds.Z = LatexCmds.integers = LatexCmds.Integers =
- bind(VanillaSymbol,'\\mathbb{Z}','ℤ', 'integers');
-
-LatexCmds.Q = LatexCmds.rationals = LatexCmds.Rationals =
- bind(VanillaSymbol,'\\mathbb{Q}','ℚ', 'rationals');
-
-LatexCmds.R = LatexCmds.reals = LatexCmds.Reals =
- bind(VanillaSymbol,'\\mathbb{R}','ℝ', 'reals');
-
-LatexCmds.C =
-LatexCmds.complex = LatexCmds.Complex =
-LatexCmds.complexes = LatexCmds.Complexes =
-LatexCmds.complexplane = LatexCmds.Complexplane = LatexCmds.ComplexPlane =
- bind(VanillaSymbol,'\\mathbb{C}','ℂ', 'complexes');
-
-LatexCmds.H = LatexCmds.Hamiltonian = LatexCmds.quaternions = LatexCmds.Quaternions =
- bind(VanillaSymbol,'\\mathbb{H}','ℍ', 'quaternions');
-
-//spacing
-LatexCmds.quad = LatexCmds.emsp = bind(VanillaSymbol,'\\quad ',' ', '4 spaces');
-LatexCmds.qquad = bind(VanillaSymbol,'\\qquad ',' ', '8 spaces');
-/* spacing special characters, gonna have to implement this in LatexCommandInput::onText somehow
-case ',':
- return VanillaSymbol('\\, ',' ', 'comma');
-case ':':
- return VanillaSymbol('\\: ',' ', 'colon');
-case ';':
- return VanillaSymbol('\\; ',' ', 'semicolon');
-case '!':
- return Symbol('\\! ','', 'exclamation point');
-*/
-
-//binary operators
-LatexCmds.diamond = bind(VanillaSymbol, '\\diamond ', '◇', 'diamond');
-LatexCmds.bigtriangleup = bind(VanillaSymbol, '\\bigtriangleup ', '△', 'triangle up');
-LatexCmds.ominus = bind(VanillaSymbol, '\\ominus ', '⊖', 'o minus');
-LatexCmds.uplus = bind(VanillaSymbol, '\\uplus ', '⊎', 'disjoint union');
-LatexCmds.bigtriangledown = bind(VanillaSymbol, '\\bigtriangledown ', '▽', 'triangle down');
-LatexCmds.sqcap = bind(VanillaSymbol, '\\sqcap ', '⊓', 'greatest lower bound');
-LatexCmds.triangleleft = bind(VanillaSymbol, '\\triangleleft ', '⊲', 'triangle left');
-LatexCmds.sqcup = bind(VanillaSymbol, '\\sqcup ', '⊔', 'least upper bound');
-LatexCmds.triangleright = bind(VanillaSymbol, '\\triangleright ', '⊳', 'triangle right');
-//circledot is not a not real LaTex command see https://github.com/mathquill/mathquill/pull/552 for more details
-LatexCmds.odot = LatexCmds.circledot = bind(VanillaSymbol, '\\odot ', '⊙', 'circle dot');
-LatexCmds.bigcirc = bind(VanillaSymbol, '\\bigcirc ', '◯', 'circle');
-LatexCmds.dagger = bind(VanillaSymbol, '\\dagger ', '', 'dagger');
-LatexCmds.ddagger = bind(VanillaSymbol, '\\ddagger ', '', 'big dagger');
-LatexCmds.wr = bind(VanillaSymbol, '\\wr ', '≀', 'wreath');
-LatexCmds.amalg = bind(VanillaSymbol, '\\amalg ', '∐', 'amalgam');
-
-//relationship symbols
-LatexCmds.models = bind(VanillaSymbol, '\\models ', '⊨', 'models');
-LatexCmds.prec = bind(VanillaSymbol, '\\prec ', '≺', 'precedes');
-LatexCmds.succ = bind(VanillaSymbol, '\\succ ', '≻', 'succeeds');
-LatexCmds.preceq = bind(VanillaSymbol, '\\preceq ', '≼', 'precedes or equals');
-LatexCmds.succeq = bind(VanillaSymbol, '\\succeq ', '≽', 'succeeds or equals');
-LatexCmds.simeq = bind(VanillaSymbol, '\\simeq ', '≃', 'similar or equal to');
-LatexCmds.mid = bind(VanillaSymbol, '\\mid ', '∣', 'divides');
-LatexCmds.ll = bind(VanillaSymbol, '\\ll ', '≪', 'll');
-LatexCmds.gg = bind(VanillaSymbol, '\\gg ', '≫', 'gg');
-LatexCmds.parallel = bind(VanillaSymbol, '\\parallel ', '∥', 'parallel with');
-LatexCmds.nparallel = bind(VanillaSymbol, '\\nparallel ', '∦', 'not parallel with');
-LatexCmds.bowtie = bind(VanillaSymbol, '\\bowtie ', '⋈', 'bowtie');
-LatexCmds.sqsubset = bind(VanillaSymbol, '\\sqsubset ', '⊏', 'square subset');
-LatexCmds.sqsupset = bind(VanillaSymbol, '\\sqsupset ', '⊐', 'square superset');
-LatexCmds.smile = bind(VanillaSymbol, '\\smile ', '⌣', 'smile');
-LatexCmds.sqsubseteq = bind(VanillaSymbol, '\\sqsubseteq ', '⊑', 'square subset or equal to');
-LatexCmds.sqsupseteq = bind(VanillaSymbol, '\\sqsupseteq ', '⊒', 'square superset or equal to');
-LatexCmds.doteq = bind(VanillaSymbol, '\\doteq ', '≐', 'dotted equals');
-LatexCmds.frown = bind(VanillaSymbol, '\\frown ', '⌢', 'frown');
-LatexCmds.vdash = bind(VanillaSymbol, '\\vdash ', '⊦', 'v dash');
-LatexCmds.dashv = bind(VanillaSymbol, '\\dashv ', '⊣', 'dash v');
-LatexCmds.nless = bind(VanillaSymbol, '\\nless ', '≮', 'not less than');
-LatexCmds.ngtr = bind(VanillaSymbol, '\\ngtr ', '≯', 'not greater than');
-
-//arrows
-LatexCmds.longleftarrow = bind(VanillaSymbol, '\\longleftarrow ', '←', 'left arrow');
-LatexCmds.longrightarrow = bind(VanillaSymbol, '\\longrightarrow ', '→', 'right arrow');
-LatexCmds.Longleftarrow = bind(VanillaSymbol, '\\Longleftarrow ', '⇐', 'left arrow');
-LatexCmds.Longrightarrow = bind(VanillaSymbol, '\\Longrightarrow ', '⇒', 'right arrow');
-LatexCmds.longleftrightarrow = bind(VanillaSymbol, '\\longleftrightarrow ', '↔', 'left and right arrow');
-LatexCmds.updownarrow = bind(VanillaSymbol, '\\updownarrow ', '↕', 'up and down arrow');
-LatexCmds.Longleftrightarrow = bind(VanillaSymbol, '\\Longleftrightarrow ', '⇔', 'left and right arrow');
-LatexCmds.Updownarrow = bind(VanillaSymbol, '\\Updownarrow ', '⇕', 'up and down arrow');
-LatexCmds.mapsto = bind(VanillaSymbol, '\\mapsto ', '↦', 'maps to');
-LatexCmds.nearrow = bind(VanillaSymbol, '\\nearrow ', '↗', 'northeast arrow');
-LatexCmds.hookleftarrow = bind(VanillaSymbol, '\\hookleftarrow ', '↩', 'hook left arrow');
-LatexCmds.hookrightarrow = bind(VanillaSymbol, '\\hookrightarrow ', '↪', 'hook right arrow');
-LatexCmds.searrow = bind(VanillaSymbol, '\\searrow ', '↘', 'southeast arrow');
-LatexCmds.leftharpoonup = bind(VanillaSymbol, '\\leftharpoonup ', '↼', 'left harpoon up');
-LatexCmds.rightharpoonup = bind(VanillaSymbol, '\\rightharpoonup ', '⇀', 'right harpoon up');
-LatexCmds.swarrow = bind(VanillaSymbol, '\\swarrow ', '↙', 'southwest arrow');
-LatexCmds.leftharpoondown = bind(VanillaSymbol, '\\leftharpoondown ', '↽', 'left harpoon down');
-LatexCmds.rightharpoondown = bind(VanillaSymbol, '\\rightharpoondown ', '⇁', 'right harpoon down');
-LatexCmds.nwarrow = bind(VanillaSymbol, '\\nwarrow ', '↖', 'northwest arrow');
-
-//Misc
-LatexCmds.ldots = bind(VanillaSymbol, '\\ldots ', '…', 'l dots');
-LatexCmds.cdots = bind(VanillaSymbol, '\\cdots ', '⋯', 'c dots');
-LatexCmds.vdots = bind(VanillaSymbol, '\\vdots ', '⋮', 'v dots');
-LatexCmds.ddots = bind(VanillaSymbol, '\\ddots ', '⋱', 'd dots');
-LatexCmds.surd = bind(VanillaSymbol, '\\surd ', '√', 'unresolved root');
-LatexCmds.triangle = bind(VanillaSymbol, '\\triangle ', '△', 'triangle');
-LatexCmds.ell = bind(VanillaSymbol, '\\ell ', 'ℓ', 'ell');
-LatexCmds.top = bind(VanillaSymbol, '\\top ', '⊤', 'top');
-LatexCmds.flat = bind(VanillaSymbol, '\\flat ', '♭', 'flat');
-LatexCmds.natural = bind(VanillaSymbol, '\\natural ', '♮', 'natural');
-LatexCmds.sharp = bind(VanillaSymbol, '\\sharp ', '♯', 'sharp');
-LatexCmds.wp = bind(VanillaSymbol, '\\wp ', '℘', 'wp');
-LatexCmds.bot = bind(VanillaSymbol, '\\bot ', '⊥', 'bot');
-LatexCmds.clubsuit = bind(VanillaSymbol, '\\clubsuit ', '♣', 'club suit');
-LatexCmds.diamondsuit = bind(VanillaSymbol, '\\diamondsuit ', '♢', 'diamond suit');
-LatexCmds.heartsuit = bind(VanillaSymbol, '\\heartsuit ', '♡', 'heart suit');
-LatexCmds.spadesuit = bind(VanillaSymbol, '\\spadesuit ', '♠', 'spade suit');
-//not real LaTex command see https://github.com/mathquill/mathquill/pull/552 for more details
-LatexCmds.parallelogram = bind(VanillaSymbol, '\\parallelogram ', '▱', 'parallelogram');
-LatexCmds.square = bind(VanillaSymbol, '\\square ', '⬜', 'square');
-
-//variable-sized
-LatexCmds.oint = bind(VanillaSymbol, '\\oint ', '∮', 'o int');
-LatexCmds.bigcap = bind(VanillaSymbol, '\\bigcap ', '∩', 'big cap');
-LatexCmds.bigcup = bind(VanillaSymbol, '\\bigcup ', '∪', 'big cup');
-LatexCmds.bigsqcup = bind(VanillaSymbol, '\\bigsqcup ', '⊔', 'big square cup');
-LatexCmds.bigvee = bind(VanillaSymbol, '\\bigvee ', '∨', 'big vee');
-LatexCmds.bigwedge = bind(VanillaSymbol, '\\bigwedge ', '∧', 'big wedge');
-LatexCmds.bigodot = bind(VanillaSymbol, '\\bigodot ', '⊙', 'big o dot');
-LatexCmds.bigotimes = bind(VanillaSymbol, '\\bigotimes ', '⊗', 'big o times');
-LatexCmds.bigoplus = bind(VanillaSymbol, '\\bigoplus ', '⊕', 'big o plus');
-LatexCmds.biguplus = bind(VanillaSymbol, '\\biguplus ', '⊎', 'big u plus');
-
-//delimiters
-LatexCmds.lfloor = bind(VanillaSymbol, '\\lfloor ', '⌊', 'left floor');
-LatexCmds.rfloor = bind(VanillaSymbol, '\\rfloor ', '⌋', 'right floor');
-LatexCmds.lceil = bind(VanillaSymbol, '\\lceil ', '⌈', 'left ceiling');
-LatexCmds.rceil = bind(VanillaSymbol, '\\rceil ', '⌉', 'right ceiling');
-LatexCmds.opencurlybrace = LatexCmds.lbrace = bind(VanillaSymbol, '\\lbrace ', '{', 'left brace');
-LatexCmds.closecurlybrace = LatexCmds.rbrace = bind(VanillaSymbol, '\\rbrace ', '}', 'right brace');
-LatexCmds.lbrack = bind(VanillaSymbol, '[', 'left bracket');
-LatexCmds.rbrack = bind(VanillaSymbol, ']', 'right bracket');
-
-//various symbols
-LatexCmds.slash = bind(VanillaSymbol, '/', 'slash');
-LatexCmds.vert = bind(VanillaSymbol,'|', 'vertical bar');
-LatexCmds.perp = LatexCmds.perpendicular = bind(VanillaSymbol,'\\perp ','⊥', 'perpendicular');
-LatexCmds.nabla = LatexCmds.del = bind(VanillaSymbol,'\\nabla ','∇');
-LatexCmds.hbar = bind(VanillaSymbol,'\\hbar ','ℏ', 'horizontal bar');
-
-LatexCmds.AA = LatexCmds.Angstrom = LatexCmds.angstrom =
- bind(VanillaSymbol,'\\text\\AA ','Å', 'AA');
-
-LatexCmds.ring = LatexCmds.circ = LatexCmds.circle =
- bind(VanillaSymbol,'\\circ ','∘', 'circle');
-
-LatexCmds.bull = LatexCmds.bullet = bind(VanillaSymbol,'\\bullet ','•', 'bullet');
-
-LatexCmds.setminus = LatexCmds.smallsetminus =
- bind(VanillaSymbol,'\\setminus ','∖', 'set minus');
-
-LatexCmds.not = //bind(Symbol,'\\not ','/', 'not');
-LatexCmds['¬'] = LatexCmds.neg = bind(VanillaSymbol,'\\neg ','¬', 'not');
-
-LatexCmds['…'] = LatexCmds.dots = LatexCmds.ellip = LatexCmds.hellip =
-LatexCmds.ellipsis = LatexCmds.hellipsis =
- bind(VanillaSymbol,'\\dots ','…', 'ellipsis');
-
-LatexCmds.converges =
-LatexCmds.darr = LatexCmds.dnarr = LatexCmds.dnarrow = LatexCmds.downarrow =
- bind(VanillaSymbol,'\\downarrow ','↓', 'converges with');
-
-LatexCmds.dArr = LatexCmds.dnArr = LatexCmds.dnArrow = LatexCmds.Downarrow =
- bind(VanillaSymbol,'\\Downarrow ','⇓', 'down arrow');
-
-LatexCmds.diverges = LatexCmds.uarr = LatexCmds.uparrow =
- bind(VanillaSymbol,'\\uparrow ','↑', 'diverges from');
-
-LatexCmds.uArr = LatexCmds.Uparrow = bind(VanillaSymbol,'\\Uparrow ','⇑', 'up arrow');
-
-LatexCmds.rarr = LatexCmds.rightarrow = bind(VanillaSymbol,'\\rightarrow ','→', 'right arrow');
-
-LatexCmds.implies = bind(BinaryOperator,'\\Rightarrow ','⇒', 'implies');
-
-LatexCmds.rArr = LatexCmds.Rightarrow = bind(VanillaSymbol,'\\Rightarrow ','⇒', 'right arrow');
-
-LatexCmds.gets = bind(BinaryOperator,'\\gets ','←', 'gets');
-
-LatexCmds.larr = LatexCmds.leftarrow = bind(VanillaSymbol,'\\leftarrow ','←', 'left arrow');
-
-LatexCmds.impliedby = bind(BinaryOperator,'\\Leftarrow ','⇐', 'implied by');
-
-LatexCmds.lArr = LatexCmds.Leftarrow = bind(VanillaSymbol,'\\Leftarrow ','⇐', 'left arrow');
-
-LatexCmds.harr = LatexCmds.lrarr = LatexCmds.leftrightarrow =
- bind(VanillaSymbol,'\\leftrightarrow ','↔', 'left and right arrow');
-
-LatexCmds.iff = bind(BinaryOperator,'\\Leftrightarrow ','⇔', 'if and only if');
-
-LatexCmds.hArr = LatexCmds.lrArr = LatexCmds.Leftrightarrow =
- bind(VanillaSymbol,'\\Leftrightarrow ','⇔', 'left and right arrow');
-
-LatexCmds.Re = LatexCmds.Real = LatexCmds.real = bind(VanillaSymbol,'\\Re ','ℜ', 'real');
-
-LatexCmds.Im = LatexCmds.imag =
-LatexCmds.image = LatexCmds.imagin = LatexCmds.imaginary = LatexCmds.Imaginary =
- bind(VanillaSymbol,'\\Im ','ℑ', 'imaginary');
-
-LatexCmds.part = LatexCmds.partial = bind(VanillaSymbol,'\\partial ','∂', 'partial');
-
-LatexCmds.pounds = bind(VanillaSymbol,'\\pounds ','£');
-
-LatexCmds.alef = LatexCmds.alefsym = LatexCmds.aleph = LatexCmds.alephsym =
- bind(VanillaSymbol,'\\aleph ','ℵ', 'alef sym');
-
-LatexCmds.xist = //LOL
-LatexCmds.xists = LatexCmds.exist = LatexCmds.exists =
- bind(VanillaSymbol,'\\exists ','∃', 'there exists at least 1');
-
-LatexCmds.nexists = LatexCmds.nexist =
- bind(VanillaSymbol, '\\nexists ', '∄', 'there is no');
-
-LatexCmds.and = LatexCmds.land = LatexCmds.wedge =
- bind(BinaryOperator,'\\wedge ','∧', 'and');
-
-LatexCmds.or = LatexCmds.lor = LatexCmds.vee = bind(BinaryOperator,'\\vee ','∨', 'or');
-
-LatexCmds.o = LatexCmds.O =
-LatexCmds.empty = LatexCmds.emptyset =
-LatexCmds.oslash = LatexCmds.Oslash =
-LatexCmds.nothing = LatexCmds.varnothing =
- bind(BinaryOperator,'\\varnothing ','∅', 'nothing');
-
-LatexCmds.cup = LatexCmds.union = bind(BinaryOperator,'\\cup ','∪', 'union');
-
-LatexCmds.cap = LatexCmds.intersect = LatexCmds.intersection =
- bind(BinaryOperator,'\\cap ','∩', 'intersection');
-
-// FIXME: the correct LaTeX would be ^\circ but we can't parse that
-LatexCmds.deg = LatexCmds.degree = bind(VanillaSymbol,'\\degree ','°', 'degrees');
-
-LatexCmds.ang = LatexCmds.angle = bind(VanillaSymbol,'\\angle ','∠', 'angle');
-LatexCmds.measuredangle = bind(VanillaSymbol,'\\measuredangle ','∡', 'measured angle');
diff --git a/src/commands/math/advancedSymbols.ts b/src/commands/math/advancedSymbols.ts
new file mode 100644
index 000000000..3c430a21e
--- /dev/null
+++ b/src/commands/math/advancedSymbols.ts
@@ -0,0 +1,339 @@
+/************************************
+ * Symbols for Advanced Mathematics
+ ***********************************/
+
+LatexCmds.notin =
+LatexCmds.cong =
+LatexCmds.equiv =
+LatexCmds.oplus =
+LatexCmds.otimes = (latex:string) => new BinaryOperator('\\'+latex+' ', '&'+latex+';');
+
+LatexCmds['∗'] = LatexCmds.ast = LatexCmds.star = LatexCmds.loast = LatexCmds.lowast =
+ bindBinaryOperator('\\ast ','∗', 'low asterisk');
+LatexCmds.therefor = LatexCmds.therefore =
+ bindBinaryOperator('\\therefore ','∴', 'therefore');
+
+LatexCmds.cuz = // l33t
+LatexCmds.because = bindBinaryOperator('\\because ','∵', 'because');
+
+LatexCmds.prop = LatexCmds.propto = bindBinaryOperator('\\propto ','∝', 'proportional to');
+
+LatexCmds['≈'] = LatexCmds.asymp = LatexCmds.approx = bindBinaryOperator('\\approx ','≈', 'approximately equal to');
+
+LatexCmds.isin = LatexCmds['in'] = bindBinaryOperator('\\in ','∈', 'is in');
+
+LatexCmds.ni = LatexCmds.contains = bindBinaryOperator('\\ni ','∋', 'is not in');
+
+LatexCmds.notni = LatexCmds.niton = LatexCmds.notcontains = LatexCmds.doesnotcontain =
+ bindBinaryOperator('\\not\\ni ','∌', 'does not contain');
+
+LatexCmds.sub = LatexCmds.subset = bindBinaryOperator('\\subset ','⊂', 'subset');
+
+LatexCmds.sup = LatexCmds.supset = LatexCmds.superset =
+ bindBinaryOperator('\\supset ','⊃', 'superset');
+
+LatexCmds.nsub = LatexCmds.notsub =
+LatexCmds.nsubset = LatexCmds.notsubset =
+ bindBinaryOperator('\\not\\subset ','⊄', 'not a subset');
+
+LatexCmds.nsup = LatexCmds.notsup =
+LatexCmds.nsupset = LatexCmds.notsupset =
+LatexCmds.nsuperset = LatexCmds.notsuperset =
+ bindBinaryOperator('\\not\\supset ','⊅', 'not a superset');
+
+LatexCmds.sube = LatexCmds.subeq = LatexCmds.subsete = LatexCmds.subseteq =
+ bindBinaryOperator('\\subseteq ','⊆', 'subset or equal to');
+
+LatexCmds.supe = LatexCmds.supeq =
+LatexCmds.supsete = LatexCmds.supseteq =
+LatexCmds.supersete = LatexCmds.superseteq =
+ bindBinaryOperator('\\supseteq ','⊇', 'superset or equal to');
+
+LatexCmds.nsube = LatexCmds.nsubeq =
+LatexCmds.notsube = LatexCmds.notsubeq =
+LatexCmds.nsubsete = LatexCmds.nsubseteq =
+LatexCmds.notsubsete = LatexCmds.notsubseteq =
+ bindBinaryOperator('\\not\\subseteq ','⊈', 'not subset or equal to');
+
+LatexCmds.nsupe = LatexCmds.nsupeq =
+LatexCmds.notsupe = LatexCmds.notsupeq =
+LatexCmds.nsupsete = LatexCmds.nsupseteq =
+LatexCmds.notsupsete = LatexCmds.notsupseteq =
+LatexCmds.nsupersete = LatexCmds.nsuperseteq =
+LatexCmds.notsupersete = LatexCmds.notsuperseteq =
+ bindBinaryOperator('\\not\\supseteq ','⊉', 'not superset or equal to');
+
+//the canonical sets of numbers
+LatexCmds.mathbb = class extends MathCommand {
+ createLeftOf (_cursor:Cursor) {};
+ numBlocks () { return 1; };
+ parser () {
+ var string = Parser.string;
+ var regex = Parser.regex;
+ var optWhitespace = Parser.optWhitespace;
+ return optWhitespace.then(string('{'))
+ .then(optWhitespace)
+ .then(regex(/^[NPZQRCH]/))
+ .skip(optWhitespace)
+ .skip(string('}'))
+ .map(function(c) {
+ // instantiate the class for the matching char
+ var cmd = LatexCmds[c];
+ if (isMQNodeClass(cmd)) {
+ return new cmd();
+ } else {
+ return (cmd as MQNodeBuilderNoParam)();
+ }
+ });
+ };
+};
+
+LatexCmds.N = LatexCmds.naturals = LatexCmds.Naturals =
+ bindVanillaSymbol('\\mathbb{N}','ℕ', 'naturals');
+
+LatexCmds.P =
+LatexCmds.primes = LatexCmds.Primes =
+LatexCmds.projective = LatexCmds.Projective =
+LatexCmds.probability = LatexCmds.Probability =
+ bindVanillaSymbol('\\mathbb{P}','ℙ', 'P');
+
+LatexCmds.Z = LatexCmds.integers = LatexCmds.Integers =
+ bindVanillaSymbol('\\mathbb{Z}','ℤ', 'integers');
+
+LatexCmds.Q = LatexCmds.rationals = LatexCmds.Rationals =
+ bindVanillaSymbol('\\mathbb{Q}','ℚ', 'rationals');
+
+LatexCmds.R = LatexCmds.reals = LatexCmds.Reals =
+ bindVanillaSymbol('\\mathbb{R}','ℝ', 'reals');
+
+LatexCmds.C =
+LatexCmds.complex = LatexCmds.Complex =
+LatexCmds.complexes = LatexCmds.Complexes =
+LatexCmds.complexplane = LatexCmds.Complexplane = LatexCmds.ComplexPlane =
+ bindVanillaSymbol('\\mathbb{C}','ℂ', 'complexes');
+
+LatexCmds.H = LatexCmds.Hamiltonian = LatexCmds.quaternions = LatexCmds.Quaternions =
+ bindVanillaSymbol('\\mathbb{H}','ℍ', 'quaternions');
+
+//spacing
+LatexCmds.quad = LatexCmds.emsp = bindVanillaSymbol('\\quad ',' ', '4 spaces');
+LatexCmds.qquad = bindVanillaSymbol('\\qquad ',' ', '8 spaces');
+/* spacing special characters, gonna have to implement this in LatexCommandInput::onText somehow
+case ',':
+ return VanillaSymbol('\\, ',' ', 'comma');
+case ':':
+ return VanillaSymbol('\\: ',' ', 'colon');
+case ';':
+ return VanillaSymbol('\\; ',' ', 'semicolon');
+case '!':
+ return MQSymbol('\\! ','', 'exclamation point');
+*/
+
+//binary operators
+LatexCmds.diamond = bindVanillaSymbol('\\diamond ', '◇', 'diamond');
+LatexCmds.bigtriangleup = bindVanillaSymbol('\\bigtriangleup ', '△', 'triangle up');
+LatexCmds.ominus = bindVanillaSymbol('\\ominus ', '⊖', 'o minus');
+LatexCmds.uplus = bindVanillaSymbol('\\uplus ', '⊎', 'disjoint union');
+LatexCmds.bigtriangledown = bindVanillaSymbol('\\bigtriangledown ', '▽', 'triangle down');
+LatexCmds.sqcap = bindVanillaSymbol('\\sqcap ', '⊓', 'greatest lower bound');
+LatexCmds.triangleleft = bindVanillaSymbol('\\triangleleft ', '⊲', 'triangle left');
+LatexCmds.sqcup = bindVanillaSymbol('\\sqcup ', '⊔', 'least upper bound');
+LatexCmds.triangleright = bindVanillaSymbol('\\triangleright ', '⊳', 'triangle right');
+//circledot is not a not real LaTex command see https://github.com/mathquill/mathquill/pull/552 for more details
+LatexCmds.odot = LatexCmds.circledot = bindVanillaSymbol('\\odot ', '⊙', 'circle dot');
+LatexCmds.bigcirc = bindVanillaSymbol('\\bigcirc ', '◯', 'circle');
+LatexCmds.dagger = bindVanillaSymbol('\\dagger ', '', 'dagger');
+LatexCmds.ddagger = bindVanillaSymbol('\\ddagger ', '', 'big dagger');
+LatexCmds.wr = bindVanillaSymbol('\\wr ', '≀', 'wreath');
+LatexCmds.amalg = bindVanillaSymbol('\\amalg ', '∐', 'amalgam');
+
+//relationship symbols
+LatexCmds.models = bindVanillaSymbol('\\models ', '⊨', 'models');
+LatexCmds.prec = bindVanillaSymbol('\\prec ', '≺', 'precedes');
+LatexCmds.succ = bindVanillaSymbol('\\succ ', '≻', 'succeeds');
+LatexCmds.preceq = bindVanillaSymbol('\\preceq ', '≼', 'precedes or equals');
+LatexCmds.succeq = bindVanillaSymbol('\\succeq ', '≽', 'succeeds or equals');
+LatexCmds.simeq = bindVanillaSymbol('\\simeq ', '≃', 'similar or equal to');
+LatexCmds.mid = bindVanillaSymbol('\\mid ', '∣', 'divides');
+LatexCmds.ll = bindVanillaSymbol('\\ll ', '≪', 'll');
+LatexCmds.gg = bindVanillaSymbol('\\gg ', '≫', 'gg');
+LatexCmds.parallel = bindVanillaSymbol('\\parallel ', '∥', 'parallel with');
+LatexCmds.nparallel = bindVanillaSymbol('\\nparallel ', '∦', 'not parallel with');
+LatexCmds.bowtie = bindVanillaSymbol('\\bowtie ', '⋈', 'bowtie');
+LatexCmds.sqsubset = bindVanillaSymbol('\\sqsubset ', '⊏', 'square subset');
+LatexCmds.sqsupset = bindVanillaSymbol('\\sqsupset ', '⊐', 'square superset');
+LatexCmds.smile = bindVanillaSymbol('\\smile ', '⌣', 'smile');
+LatexCmds.sqsubseteq = bindVanillaSymbol('\\sqsubseteq ', '⊑', 'square subset or equal to');
+LatexCmds.sqsupseteq = bindVanillaSymbol('\\sqsupseteq ', '⊒', 'square superset or equal to');
+LatexCmds.doteq = bindVanillaSymbol('\\doteq ', '≐', 'dotted equals');
+LatexCmds.frown = bindVanillaSymbol('\\frown ', '⌢', 'frown');
+LatexCmds.vdash = bindVanillaSymbol('\\vdash ', '⊦', 'v dash');
+LatexCmds.dashv = bindVanillaSymbol('\\dashv ', '⊣', 'dash v');
+LatexCmds.nless = bindVanillaSymbol('\\nless ', '≮', 'not less than');
+LatexCmds.ngtr = bindVanillaSymbol('\\ngtr ', '≯', 'not greater than');
+
+//arrows
+LatexCmds.longleftarrow = bindVanillaSymbol('\\longleftarrow ', '←', 'left arrow');
+LatexCmds.longrightarrow = bindVanillaSymbol('\\longrightarrow ', '→', 'right arrow');
+LatexCmds.Longleftarrow = bindVanillaSymbol('\\Longleftarrow ', '⇐', 'left arrow');
+LatexCmds.Longrightarrow = bindVanillaSymbol('\\Longrightarrow ', '⇒', 'right arrow');
+LatexCmds.longleftrightarrow = bindVanillaSymbol('\\longleftrightarrow ', '↔', 'left and right arrow');
+LatexCmds.updownarrow = bindVanillaSymbol('\\updownarrow ', '↕', 'up and down arrow');
+LatexCmds.Longleftrightarrow = bindVanillaSymbol('\\Longleftrightarrow ', '⇔', 'left and right arrow');
+LatexCmds.Updownarrow = bindVanillaSymbol('\\Updownarrow ', '⇕', 'up and down arrow');
+LatexCmds.mapsto = bindVanillaSymbol('\\mapsto ', '↦', 'maps to');
+LatexCmds.nearrow = bindVanillaSymbol('\\nearrow ', '↗', 'northeast arrow');
+LatexCmds.hookleftarrow = bindVanillaSymbol('\\hookleftarrow ', '↩', 'hook left arrow');
+LatexCmds.hookrightarrow = bindVanillaSymbol('\\hookrightarrow ', '↪', 'hook right arrow');
+LatexCmds.searrow = bindVanillaSymbol('\\searrow ', '↘', 'southeast arrow');
+LatexCmds.leftharpoonup = bindVanillaSymbol('\\leftharpoonup ', '↼', 'left harpoon up');
+LatexCmds.rightharpoonup = bindVanillaSymbol('\\rightharpoonup ', '⇀', 'right harpoon up');
+LatexCmds.swarrow = bindVanillaSymbol('\\swarrow ', '↙', 'southwest arrow');
+LatexCmds.leftharpoondown = bindVanillaSymbol('\\leftharpoondown ', '↽', 'left harpoon down');
+LatexCmds.rightharpoondown = bindVanillaSymbol('\\rightharpoondown ', '⇁', 'right harpoon down');
+LatexCmds.nwarrow = bindVanillaSymbol('\\nwarrow ', '↖', 'northwest arrow');
+
+//Misc
+LatexCmds.ldots = bindVanillaSymbol('\\ldots ', '…', 'l dots');
+LatexCmds.cdots = bindVanillaSymbol('\\cdots ', '⋯', 'c dots');
+LatexCmds.vdots = bindVanillaSymbol('\\vdots ', '⋮', 'v dots');
+LatexCmds.ddots = bindVanillaSymbol('\\ddots ', '⋱', 'd dots');
+LatexCmds.surd = bindVanillaSymbol('\\surd ', '√', 'unresolved root');
+LatexCmds.triangle = bindVanillaSymbol('\\triangle ', '△', 'triangle');
+LatexCmds.ell = bindVanillaSymbol('\\ell ', 'ℓ', 'ell');
+LatexCmds.top = bindVanillaSymbol('\\top ', '⊤', 'top');
+LatexCmds.flat = bindVanillaSymbol('\\flat ', '♭', 'flat');
+LatexCmds.natural = bindVanillaSymbol('\\natural ', '♮', 'natural');
+LatexCmds.sharp = bindVanillaSymbol('\\sharp ', '♯', 'sharp');
+LatexCmds.wp = bindVanillaSymbol('\\wp ', '℘', 'wp');
+LatexCmds.bot = bindVanillaSymbol('\\bot ', '⊥', 'bot');
+LatexCmds.clubsuit = bindVanillaSymbol('\\clubsuit ', '♣', 'club suit');
+LatexCmds.diamondsuit = bindVanillaSymbol('\\diamondsuit ', '♢', 'diamond suit');
+LatexCmds.heartsuit = bindVanillaSymbol('\\heartsuit ', '♡', 'heart suit');
+LatexCmds.spadesuit = bindVanillaSymbol('\\spadesuit ', '♠', 'spade suit');
+//not real LaTex command see https://github.com/mathquill/mathquill/pull/552 for more details
+LatexCmds.parallelogram = bindVanillaSymbol('\\parallelogram ', '▱', 'parallelogram');
+LatexCmds.square = bindVanillaSymbol('\\square ', '⬜', 'square');
+
+//variable-sized
+LatexCmds.oint = bindVanillaSymbol('\\oint ', '∮', 'o int');
+LatexCmds.bigcap = bindVanillaSymbol('\\bigcap ', '∩', 'big cap');
+LatexCmds.bigcup = bindVanillaSymbol('\\bigcup ', '∪', 'big cup');
+LatexCmds.bigsqcup = bindVanillaSymbol('\\bigsqcup ', '⊔', 'big square cup');
+LatexCmds.bigvee = bindVanillaSymbol('\\bigvee ', '∨', 'big vee');
+LatexCmds.bigwedge = bindVanillaSymbol('\\bigwedge ', '∧', 'big wedge');
+LatexCmds.bigodot = bindVanillaSymbol('\\bigodot ', '⊙', 'big o dot');
+LatexCmds.bigotimes = bindVanillaSymbol('\\bigotimes ', '⊗', 'big o times');
+LatexCmds.bigoplus = bindVanillaSymbol('\\bigoplus ', '⊕', 'big o plus');
+LatexCmds.biguplus = bindVanillaSymbol('\\biguplus ', '⊎', 'big u plus');
+
+//delimiters
+LatexCmds.lfloor = bindVanillaSymbol('\\lfloor ', '⌊', 'left floor');
+LatexCmds.rfloor = bindVanillaSymbol('\\rfloor ', '⌋', 'right floor');
+LatexCmds.lceil = bindVanillaSymbol('\\lceil ', '⌈', 'left ceiling');
+LatexCmds.rceil = bindVanillaSymbol('\\rceil ', '⌉', 'right ceiling');
+LatexCmds.opencurlybrace = LatexCmds.lbrace = bindVanillaSymbol('\\lbrace ', '{', 'left brace');
+LatexCmds.closecurlybrace = LatexCmds.rbrace = bindVanillaSymbol('\\rbrace ', '}', 'right brace');
+LatexCmds.lbrack = bindVanillaSymbol('[', 'left bracket');
+LatexCmds.rbrack = bindVanillaSymbol(']', 'right bracket');
+
+//various symbols
+LatexCmds.slash = bindVanillaSymbol('/', 'slash');
+LatexCmds.vert = bindVanillaSymbol('|', 'vertical bar');
+LatexCmds.perp = LatexCmds.perpendicular = bindVanillaSymbol('\\perp ','⊥', 'perpendicular');
+LatexCmds.nabla = LatexCmds.del = bindVanillaSymbol('\\nabla ','∇');
+LatexCmds.hbar = bindVanillaSymbol('\\hbar ','ℏ', 'horizontal bar');
+
+LatexCmds.AA = LatexCmds.Angstrom = LatexCmds.angstrom =
+ bindVanillaSymbol('\\text\\AA ','Å', 'AA');
+
+LatexCmds.ring = LatexCmds.circ = LatexCmds.circle =
+ bindVanillaSymbol('\\circ ','∘', 'circle');
+
+LatexCmds.bull = LatexCmds.bullet = bindVanillaSymbol('\\bullet ','•', 'bullet');
+
+LatexCmds.setminus = LatexCmds.smallsetminus =
+ bindVanillaSymbol('\\setminus ','∖', 'set minus');
+
+LatexCmds.not = //bind(MQSymbol,'\\not ','/', 'not');
+LatexCmds['¬'] = LatexCmds.neg = bindVanillaSymbol('\\neg ','¬', 'not');
+
+LatexCmds['…'] = LatexCmds.dots = LatexCmds.ellip = LatexCmds.hellip =
+LatexCmds.ellipsis = LatexCmds.hellipsis =
+ bindVanillaSymbol('\\dots ','…', 'ellipsis');
+
+LatexCmds.converges =
+LatexCmds.darr = LatexCmds.dnarr = LatexCmds.dnarrow = LatexCmds.downarrow =
+ bindVanillaSymbol('\\downarrow ','↓', 'converges with');
+
+LatexCmds.dArr = LatexCmds.dnArr = LatexCmds.dnArrow = LatexCmds.Downarrow =
+ bindVanillaSymbol('\\Downarrow ','⇓', 'down arrow');
+
+LatexCmds.diverges = LatexCmds.uarr = LatexCmds.uparrow =
+ bindVanillaSymbol('\\uparrow ','↑', 'diverges from');
+
+LatexCmds.uArr = LatexCmds.Uparrow = bindVanillaSymbol('\\Uparrow ','⇑', 'up arrow');
+
+LatexCmds.rarr = LatexCmds.rightarrow = bindVanillaSymbol('\\rightarrow ','→', 'right arrow');
+
+LatexCmds.implies = bindBinaryOperator('\\Rightarrow ','⇒', 'implies');
+
+LatexCmds.rArr = LatexCmds.Rightarrow = bindVanillaSymbol('\\Rightarrow ','⇒', 'right arrow');
+
+LatexCmds.gets = bindBinaryOperator('\\gets ','←', 'gets');
+
+LatexCmds.larr = LatexCmds.leftarrow = bindVanillaSymbol('\\leftarrow ','←', 'left arrow');
+
+LatexCmds.impliedby = bindBinaryOperator('\\Leftarrow ','⇐', 'implied by');
+
+LatexCmds.lArr = LatexCmds.Leftarrow = bindVanillaSymbol('\\Leftarrow ','⇐', 'left arrow');
+
+LatexCmds.harr = LatexCmds.lrarr = LatexCmds.leftrightarrow =
+ bindVanillaSymbol('\\leftrightarrow ','↔', 'left and right arrow');
+
+LatexCmds.iff = bindBinaryOperator('\\Leftrightarrow ','⇔', 'if and only if');
+
+LatexCmds.hArr = LatexCmds.lrArr = LatexCmds.Leftrightarrow =
+ bindVanillaSymbol('\\Leftrightarrow ','⇔', 'left and right arrow');
+
+LatexCmds.Re = LatexCmds.Real = LatexCmds.real = bindVanillaSymbol('\\Re ','ℜ', 'real');
+
+LatexCmds.Im = LatexCmds.imag =
+LatexCmds.image = LatexCmds.imagin = LatexCmds.imaginary = LatexCmds.Imaginary =
+ bindVanillaSymbol('\\Im ','ℑ', 'imaginary');
+
+LatexCmds.part = LatexCmds.partial = bindVanillaSymbol('\\partial ','∂', 'partial');
+
+LatexCmds.pounds = bindVanillaSymbol('\\pounds ','£');
+
+LatexCmds.alef = LatexCmds.alefsym = LatexCmds.aleph = LatexCmds.alephsym =
+ bindVanillaSymbol('\\aleph ','ℵ', 'alef sym');
+
+LatexCmds.xist = //LOL
+LatexCmds.xists = LatexCmds.exist = LatexCmds.exists =
+ bindVanillaSymbol('\\exists ','∃', 'there exists at least 1');
+
+LatexCmds.nexists = LatexCmds.nexist =
+ bindVanillaSymbol('\\nexists ', '∄', 'there is no');
+
+LatexCmds.and = LatexCmds.land = LatexCmds.wedge =
+ bindBinaryOperator('\\wedge ','∧', 'and');
+
+LatexCmds.or = LatexCmds.lor = LatexCmds.vee = bindBinaryOperator('\\vee ','∨', 'or');
+
+LatexCmds.o = LatexCmds.O =
+LatexCmds.empty = LatexCmds.emptyset =
+LatexCmds.oslash = LatexCmds.Oslash =
+LatexCmds.nothing = LatexCmds.varnothing =
+ bindBinaryOperator('\\varnothing ','∅', 'nothing');
+
+LatexCmds.cup = LatexCmds.union = bindBinaryOperator('\\cup ','∪', 'union');
+
+LatexCmds.cap = LatexCmds.intersect = LatexCmds.intersection =
+ bindBinaryOperator('\\cap ','∩', 'intersection');
+
+// FIXME: the correct LaTeX would be ^\circ but we can't parse that
+LatexCmds.deg = LatexCmds.degree = bindVanillaSymbol('\\degree ','°', 'degrees');
+
+LatexCmds.ang = LatexCmds.angle = bindVanillaSymbol('\\angle ','∠', 'angle');
+LatexCmds.measuredangle = bindVanillaSymbol('\\measuredangle ','∡', 'measured angle');
diff --git a/src/commands/math/basicSymbols.js b/src/commands/math/basicSymbols.ts
similarity index 55%
rename from src/commands/math/basicSymbols.js
rename to src/commands/math/basicSymbols.ts
index 45b16c62d..80b90cb53 100644
--- a/src/commands/math/basicSymbols.js
+++ b/src/commands/math/basicSymbols.ts
@@ -1,19 +1,23 @@
/*********************************
* Symbols for Basic Mathematics
********************************/
-var DigitGroupingChar = P(Symbol, function(_, super_) {
- _.finalizeTree = _.siblingDeleted = _.siblingCreated = function(opts, dir) {
+class DigitGroupingChar extends MQSymbol {
+ finalizeTree (opts:CursorOptions, dir:Direction) { this.sharedSiblingMethod(opts, dir) };
+ siblingDeleted (opts:CursorOptions, dir:Direction) { this.sharedSiblingMethod(opts, dir) };
+ siblingCreated (opts:CursorOptions, dir:Direction) { this.sharedSiblingMethod(opts, dir) };
+
+ sharedSiblingMethod (opts:CursorOptions, dir:Direction) {
// don't try to fix digit grouping if the sibling to my right changed (dir === R or
// undefined) and it's now a DigitGroupingChar, it will try to fix grouping
if (dir !== L && this[R] instanceof DigitGroupingChar) return;
this.fixDigitGrouping(opts);
};
- _.fixDigitGrouping = function (opts) {
+ fixDigitGrouping (opts:CursorOptions) {
if (!opts.enableDigitGrouping) return;
- var left = this;
- var right = this;
+ var left:NodeRef = this;
+ var right:NodeRef = this;
var spacesFound = 0;
var dots = [];
@@ -22,9 +26,9 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
var DOT = '.';
// traverse left as far as possible (starting at this char)
- var node = left;
+ var node:NodeRef = left;
do {
- if (/^[0-9]$/.test(node.ctrlSeq)) {
+ if (/^[0-9]$/.test(node.ctrlSeq!)) {
left = node
} else if (node.ctrlSeq === SPACE) {
left = node
@@ -39,7 +43,7 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
// traverse right as far as possible (starting to right of this char)
while (node = right[R]) {
- if (/^[0-9]$/.test(node.ctrlSeq)) {
+ if (/^[0-9]$/.test(node.ctrlSeq!)) {
right = node
} else if (node.ctrlSeq === SPACE) {
right = node
@@ -53,19 +57,19 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
}
// trim the leading spaces
- while (right !== left && left.ctrlSeq === SPACE) {
+ while (right !== left && left && left.ctrlSeq === SPACE) {
left = left[R];
spacesFound -= 1;
}
// trim the trailing spaces
- while (right !== left && right.ctrlSeq === SPACE) {
+ while (right !== left && right && right.ctrlSeq === SPACE) {
right = right[L];
spacesFound -= 1;
}
// happens when you only have a space
- if (left === right && left.ctrlSeq === SPACE) return;
+ if (left === right && left && left.ctrlSeq === SPACE) return;
var disableFormatting = spacesFound > 0 || dots.length > 1;
if (disableFormatting) {
@@ -83,15 +87,17 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
}
};
- _.removeGroupingBetween = function (left, right) {
+ removeGroupingBetween (left:NodeRef, right:NodeRef) {
var node = left;
do {
- node.setGroupingClass(undefined);
- if (node === right) break;
+ if (node instanceof DigitGroupingChar) {
+ node.setGroupingClass(undefined);
+ }
+ if (!node || node === right) break;
} while (node = node[R]);
};
- _.addGroupingBetween = function (start, end) {
+ addGroupingBetween (start:NodeRef, end:NodeRef) {
var node = start;
var count = 0;
@@ -128,14 +134,17 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
}
}
- node.setGroupingClass(cls);
+ if (node instanceof DigitGroupingChar) {
+ node.setGroupingClass(cls);
+ }
if (node === end) break;
- node = node[L];
+ node = node[L] as DigitGroupingChar;
}
};
- _.setGroupingClass = function (cls) {
+ _groupingClass?:string;
+ setGroupingClass (cls:string | undefined) {
// nothing changed (either class is the same or it's still undefined)
if (this._groupingClass === cls) return;
@@ -148,48 +157,66 @@ var DigitGroupingChar = P(Symbol, function(_, super_) {
// cache the groupingClass
this._groupingClass = cls;
}
-});
+};
-var Digit = P(DigitGroupingChar, function(_, super_) {
- _.init = function(ch, html, mathspeak) {
- super_.init.call(this, ch, ''+(html || ch)+'', undefined, mathspeak);
+class Digit extends DigitGroupingChar {
+ constructor (ch:string, html?:string, mathspeak?:string) {
+ super(ch, ''+(html || ch)+'', undefined, mathspeak);
};
- _.createLeftOf = function(cursor) {
+ createLeftOf (cursor:Cursor) {
+ const cursorL = cursor[L];
+ const cursorLL = cursorL && cursorL[L];
+ const cursorParentParentSub = (
+ cursor.parent.parent instanceof SupSub ?
+ cursor.parent.parent.sub
+ : undefined
+ )
+
if (cursor.options.autoSubscriptNumerals
- && cursor.parent !== cursor.parent.parent.sub
- && ((cursor[L] instanceof Variable && cursor[L].isItalic !== false)
- || (cursor[L] instanceof SupSub
- && cursor[L][L] instanceof Variable
- && cursor[L][L].isItalic !== false))) {
- LatexCmds._().createLeftOf(cursor);
- super_.createLeftOf.call(this, cursor);
+ && cursor.parent !== cursorParentParentSub
+ && ((cursorL instanceof Variable && cursorL.isItalic !== false)
+ || (cursorL instanceof SupSub
+ && cursorLL instanceof Variable
+ && cursorLL.isItalic !== false))) {
+ new SubscriptCommand().createLeftOf(cursor);
+ super.createLeftOf(cursor);
cursor.insRightOf(cursor.parent.parent);
}
- else super_.createLeftOf.call(this, cursor);
+ else super.createLeftOf(cursor);
};
- _.mathspeak = function(opts) {
+ mathspeak (opts:MathspeakOptions) {
if (opts && opts.createdLeftOf) {
var cursor = opts.createdLeftOf;
+ var cursorL = cursor[L];
+ var cursorLL = cursorL && cursorL[L];
+ const cursorParentParentSub = (
+ cursor.parent.parent instanceof SupSub ?
+ cursor.parent.parent.sub
+ : undefined
+ )
+
if (cursor.options.autoSubscriptNumerals
- && cursor.parent !== cursor.parent.parent.sub
- && ((cursor[L] instanceof Variable && cursor[L].isItalic !== false)
+ && cursor.parent !== cursorParentParentSub
+ && ((cursorL instanceof Variable && cursorL.isItalic !== false)
|| (cursor[L] instanceof SupSub
- && cursor[L][L] instanceof Variable
- && cursor[L][L].isItalic !== false))) {
- return 'Subscript ' + super_.mathspeak.call(this) + ' Baseline';
+ && cursorLL instanceof Variable
+ && cursorLL.isItalic !== false))) {
+ return 'Subscript ' + super.mathspeak() + ' Baseline';
}
}
- return super_.mathspeak.apply(this, arguments);
+ return super.mathspeak();
};
-});
+}
+
+class Variable extends MQSymbol {
+ isItalic?:boolean;
-var Variable = P(Symbol, function(_, super_) {
- _.init = function(ch, html) {
- super_.init.call(this, ch, ''+(html || ch)+'');
+ constructor (ch:string, html?:string) {
+ super(ch, ''+(html || ch)+'');
};
- _.text = function() {
- var text = this.ctrlSeq;
+ text () {
+ var text = this.ctrlSeq || '';
if (this.isPartOfOperator) {
if (text[0] == '\\') {
text = text.slice(1, text.length);
@@ -200,7 +227,7 @@ var Variable = P(Symbol, function(_, super_) {
} else {
if (this[L] && !(this[L] instanceof Variable)
&& !(this[L] instanceof BinaryOperator)
- && this[L].ctrlSeq !== '\\ ')
+ && (this[L] as MQNode).ctrlSeq !== '\\ ')
text = '*' + text;
if (this[R] && !(this[R] instanceof BinaryOperator)
&& !(this[R] instanceof SupSub))
@@ -208,14 +235,14 @@ var Variable = P(Symbol, function(_, super_) {
}
return text;
};
- _.mathspeak = function() {
- var text = this.ctrlSeq;
+ mathspeak () {
+ var text = this.ctrlSeq || '';
if (
this.isPartOfOperator ||
text.length > 1 ||
(this.parent && this.parent.parent && this.parent.parent.isTextBlock())
) {
- return super_.mathspeak.call(this);
+ return super.mathspeak();
} else {
// Apple voices in VoiceOver (such as Alex, Bruce, and Victoria) do
// some strange pronunciation given certain expressions,
@@ -225,19 +252,27 @@ var Variable = P(Symbol, function(_, super_) {
return '"'+text+'"';
}
};
-});
+};
+function bindVariable (ch:string, html:string, _unusedMathspeak?:string) {
+ return () => new Variable(ch, html);
+}
+
-Options.p.autoCommands = { _maxLength: 0 };
-optionProcessors.autoCommands = function(cmds) {
+Options.prototype.autoCommands = { _maxLength: 0 };
+optionProcessors.autoCommands = function(cmds:string) {
if (!/^[a-z]+(?: [a-z]+)*$/i.test(cmds)) {
throw '"'+cmds+'" not a space-delimited list of only letters';
}
- var list = cmds.split(' '), dict = {}, maxLength = 0;
+ var list = cmds.split(' ');
+ var dict:AutoDict = {}
+ var maxLength = 0;
+
for (var i = 0; i < list.length; i += 1) {
var cmd = list[i];
if (cmd.length < 2) {
throw 'autocommand "'+cmd+'" not minimum length of 2';
}
+
if (LatexCmds[cmd] === OperatorName) {
throw '"' + cmd + '" is a built-in operator name';
}
@@ -248,12 +283,25 @@ optionProcessors.autoCommands = function(cmds) {
return dict;
};
-Options.p.autoParenthesizedFunctions = {_maxLength: 0};
+Options.prototype.quietEmptyDelimiters = {};
+optionProcessors.quietEmptyDelimiters = function(dlms:string) {
+ var list = dlms.split(' ');
+ var dict: { [id:string]:any; } = {};
+ for (var i = 0; i < list.length; i += 1) {
+ var dlm = list[i];
+ dict[dlm] = 1;
+ }
+ return dict;
+};
+
+Options.prototype.autoParenthesizedFunctions = {_maxLength: 0};
optionProcessors.autoParenthesizedFunctions = function (cmds) {
if (!/^[a-z]+(?: [a-z]+)*$/i.test(cmds)) {
throw '"'+cmds+'" not a space-delimited list of only letters';
}
- var list = cmds.split(' '), dict = {}, maxLength = 0;
+ var list = cmds.split(' ');
+ var dict:AutoDict = {}
+ var maxLength = 0;
for (var i = 0; i < list.length; i += 1) {
var cmd = list[i];
if (cmd.length < 2) {
@@ -266,48 +314,77 @@ optionProcessors.autoParenthesizedFunctions = function (cmds) {
return dict;
}
-var Letter = P(Variable, function(_, super_) {
- _.init = function(ch) { return super_.init.call(this, this.letter = ch); };
- _.checkAutoCmds = function (cursor) {
+class Letter extends Variable {
+ letter:string;
+
+ constructor (ch:string) {
+ super(ch);
+ this.letter = ch;
+ };
+ checkAutoCmds (cursor:Cursor) {
+ //exit early if in simple subscript and disableAutoSubstitutionInSubscripts is set.
+ if (this.shouldIgnoreSubstitutionInSimpleSubscript(cursor.options)) {
+ return;
+ }
+
//handle autoCommands
- var autoCmds = cursor.options.autoCommands, maxLength = autoCmds._maxLength;
+ var autoCmds = cursor.options.autoCommands;
+ var maxLength = autoCmds._maxLength || 0;
if (maxLength > 0) {
// want longest possible autocommand, so join together longest
// sequence of letters
- var str = '', l = this, i = 0;
+ var str = '';
+ var l:NodeRef = this;
+ var i = 0;
// FIXME: l.ctrlSeq === l.letter checks if first or last in an operator name
while (l instanceof Letter && l.ctrlSeq === l.letter && i < maxLength) {
- str = l.letter + str, l = l[L], i += 1;
+ str = l.letter + str;
+ l = l[L];
+ i += 1;
}
// check for an autocommand, going thru substrings longest to shortest
while (str.length) {
if (autoCmds.hasOwnProperty(str)) {
- for (var i = 1, l = this; i < str.length; i += 1, l = l[L]);
- Fragment(l, this).remove();
- cursor[L] = l[L];
- return LatexCmds[str](str).createLeftOf(cursor);
+ l = this;
+ for (i = 1; l && i < str.length; i += 1, l = l[L]);
+
+ new Fragment(l as MQNode, this).remove();
+ cursor[L] = (l as MQNode)[L];
+
+ var cmd = LatexCmds[str];
+ var node;
+ if (isMQNodeClass(cmd)) {
+ node = new (cmd as typeof TempSingleCharNode)(str); // TODO - How do we know that this class expects a single str input?
+ } else {
+ node = cmd(str);
+ }
+
+ return node.createLeftOf(cursor);
}
str = str.slice(1);
}
}
}
- _.autoParenthesize = function (cursor) {
+ autoParenthesize (cursor:Cursor) {
//exit early if already parenthesized
var right = cursor.parent.ends[R]
if (right && right instanceof Bracket && right.ctrlSeq === '\\left(') {
return
}
- //exit early if in simple subscript
- if (this.isParentSimpleSubscript()) {
+ //exit early if in simple subscript and disableAutoSubstitutionInSubscripts is set.
+ if (this.shouldIgnoreSubstitutionInSimpleSubscript(cursor.options)) {
return;
}
//handle autoParenthesized functions
- var str = '', l = this, i = 0;
+ var str = '';
+ var l:NodeRef = this
+ var i = 0;
- var autoParenthesizedFunctions = cursor.options.autoParenthesizedFunctions, maxLength = autoParenthesizedFunctions._maxLength;
+ var autoParenthesizedFunctions = cursor.options.autoParenthesizedFunctions;
+ var maxLength = autoParenthesizedFunctions._maxLength || 0;
var autoOperatorNames = cursor.options.autoOperatorNames
while (l instanceof Letter && i < maxLength) {
str = l.letter + str, l = l[L], i += 1;
@@ -322,30 +399,35 @@ var Letter = P(Variable, function(_, super_) {
}
}
- _.createLeftOf = function(cursor) {
- super_.createLeftOf.apply(this, arguments);
+ createLeftOf (cursor:Cursor) {
+ super.createLeftOf(cursor);
this.checkAutoCmds(cursor);
this.autoParenthesize(cursor);
};
- _.italicize = function(bool) {
+ italicize (bool:boolean) {
this.isItalic = bool;
this.isPartOfOperator = !bool;
this.jQ.toggleClass('mq-operator-name', !bool);
return this;
};
- _.finalizeTree = _.siblingDeleted = _.siblingCreated = function(opts, dir) {
+ finalizeTree (opts:CursorOptions, dir:Direction) {this.sharedSiblingMethod(opts, dir)};
+ siblingDeleted (opts:CursorOptions, dir:Direction) {this.sharedSiblingMethod(opts, dir)};
+ siblingCreated (opts:CursorOptions, dir:Direction) {this.sharedSiblingMethod(opts, dir)};
+
+ sharedSiblingMethod (opts:CursorOptions, dir:Direction) {
// don't auto-un-italicize if the sibling to my right changed (dir === R or
// undefined) and it's now a Letter, it will un-italicize everyone
if (dir !== L && this[R] instanceof Letter) return;
this.autoUnItalicize(opts);
};
- _.autoUnItalicize = function(opts) {
+
+ autoUnItalicize (opts:CursorOptions) {
var autoOps = opts.autoOperatorNames;
if (autoOps._maxLength === 0) return;
- //exit early if in simple subscript
- if (this.isParentSimpleSubscript()) {
+ //exit early if in simple subscript and disableAutoSubstitutionInSubscripts is set.
+ if (this.shouldIgnoreSubstitutionInSimpleSubscript(opts)) {
return;
}
@@ -357,30 +439,50 @@ var Letter = P(Variable, function(_, super_) {
// removeClass and delete flags from all letters before figuring out
// which, if any, are part of an operator name
- Fragment(l[R] || this.parent.ends[L], r[L] || this.parent.ends[R]).each(function(el) {
- el.italicize(true).jQ.removeClass('mq-first mq-last mq-followed-by-supsub');
- el.ctrlSeq = el.letter;
+ var lR = l && l[R];
+ var rL = r && r[L];
+
+ new Fragment(lR || this.parent.ends[L] as MQNode, rL || this.parent.ends[R] as MQNode).each(function(el) {
+ if (el instanceof Letter) {
+ el.italicize(true).jQ.removeClass('mq-first mq-last mq-followed-by-supsub');
+ el.ctrlSeq = el.letter;
+ }
+ return undefined;
});
+ let autoOpsLength = autoOps._maxLength || 0;
+
// check for operator names: at each position from left to right, check
// substrings from longest to shortest
- outer: for (var i = 0, first = l[R] || this.parent.ends[L]; i < str.length; i += 1, first = first[R]) {
- for (var len = min(autoOps._maxLength, str.length - i); len > 0; len -= 1) {
+ outer: for (var i = 0, first = (l as MQNode)[R] || this.parent.ends[L]; first && i < str.length; i += 1, first = (first as MQNode)[R]) {
+ for (var len = min(autoOpsLength, str.length - i); len > 0; len -= 1) {
var word = str.slice(i, i + len);
+ var last:MQNode = undefined!; // TODO - TS complaining that we use last before assigning to it
+
if (autoOps.hasOwnProperty(word)) {
- for (var j = 0, letter = first; j < len; j += 1, letter = letter[R]) {
- letter.italicize(false);
- var last = letter;
+ for (var j = 0, letter:NodeRef = first; j < len; j += 1, letter = (letter as MQNode)[R]) {
+ if (letter instanceof Letter) {
+ letter.italicize(false);
+ last = letter;
+ }
}
var isBuiltIn = BuiltInOpNames.hasOwnProperty(word);
first.ctrlSeq = (isBuiltIn ? '\\' : '\\operatorname{') + first.ctrlSeq;
last.ctrlSeq += (isBuiltIn ? ' ' : '}');
- if (TwoWordOpNames.hasOwnProperty(word)) last[L][L][L].jQ.addClass('mq-last');
- if (!shouldOmitPadding(first[L])) first.jQ.addClass('mq-first');
- if (!shouldOmitPadding(last[R])) {
+
+
+ if (TwoWordOpNames.hasOwnProperty(word)) {
+ const lastL = last[L];
+ const lastLL = lastL && lastL[L];
+ const lastLLL = (lastLL && lastLL[L]) as MQNode;
+ lastLLL.jQ.addClass('mq-last');
+ }
+
+ if (!this.shouldOmitPadding(first[L])) first.jQ.addClass('mq-first');
+ if (!this.shouldOmitPadding(last[R])) {
if (last[R] instanceof SupSub) {
- var supsub = last[R]; // XXX monkey-patching, but what's the right thing here?
+ var supsub = last[R] as MQNode; // XXX monkey-patching, but what's the right thing here?
// Have operatorname-specific code in SupSub? A CSS-like language to style the
// math tree, but which ignores cursor and selection (which CSS can't)?
var respace = supsub.siblingCreated = supsub.siblingDeleted = function() {
@@ -400,7 +502,7 @@ var Letter = P(Variable, function(_, super_) {
}
}
};
- function shouldOmitPadding(node) {
+ shouldOmitPadding(node:NodeRef) {
// omit padding if no node
if (!node) return true;
@@ -415,14 +517,14 @@ var Letter = P(Variable, function(_, super_) {
return false;
}
-});
-var BuiltInOpNames = {}; // the set of operator names like \sin, \cos, etc that
+};
+var BuiltInOpNames:AutoDict = {}; // the set of operator names like \sin, \cos, etc that
// are built-into LaTeX, see Section 3.17 of the Short Math Guide: http://tinyurl.com/jm9okjc
// MathQuill auto-unitalicizes some operator names not in that set, like 'hcf'
// and 'arsinh', which must be exported as \operatorname{hcf} and
// \operatorname{arsinh}. Note: over/under line/arrow \lim variants like
// \varlimsup are not supported
-var AutoOpNames = Options.p.autoOperatorNames = { _maxLength: 9 }; // the set
+var AutoOpNames:AutoDict = Options.prototype.autoOperatorNames = { _maxLength: 9 }; // the set
// of operator names that MathQuill auto-unitalicizes by default; overridable
var TwoWordOpNames = { limsup: 1, liminf: 1, projlim: 1, injlim: 1 };
(function() {
@@ -462,7 +564,9 @@ optionProcessors.autoOperatorNames = function(cmds) {
if (!/^[a-z\|\-]+(?: [a-z\|\-]+)*$/i.test(cmds)) {
throw '"'+cmds+'" not a space-delimited list of letters or "|"';
}
- var list = cmds.split(' '), dict = {}, maxLength = 0;
+ var list = cmds.split(' ');
+ var dict:AutoDict = {};
+ var maxLength = 0;
for (var i = 0; i < list.length; i += 1) {
var cmd = list[i];
if (cmd.length < 2) {
@@ -487,30 +591,33 @@ optionProcessors.autoOperatorNames = function(cmds) {
dict._maxLength = maxLength;
return dict;
};
-var OperatorName = P(Symbol, function(_, super_) {
- _.init = function(fn) { this.ctrlSeq = fn; };
- _.createLeftOf = function(cursor) {
+class OperatorName extends MQSymbol {
+ ctrlSeq:string;
+ constructor (fn?:string) {
+ super(fn || '');
+ };
+ createLeftOf (cursor:Cursor) {
var fn = this.ctrlSeq;
for (var i = 0; i < fn.length; i += 1) {
- Letter(fn.charAt(i)).createLeftOf(cursor);
+ new Letter(fn.charAt(i)).createLeftOf(cursor);
}
};
- _.parser = function() {
+ parser () {
var fn = this.ctrlSeq;
- var block = MathBlock();
+ var block = new MathBlock();
for (var i = 0; i < fn.length; i += 1) {
- Letter(fn.charAt(i)).adopt(block, block.ends[R], 0);
+ new Letter(fn.charAt(i)).adopt(block, block.ends[R], 0);
}
- return Parser.succeed(block.children());
+ return Parser.succeed(block.children()) as ParserAny;
};
-});
+};
for (var fn in AutoOpNames) if (AutoOpNames.hasOwnProperty(fn)) {
- LatexCmds[fn] = OperatorName;
+ (LatexCmds as LatexCmdsAny)[fn as string] = OperatorName;
}
-LatexCmds.operatorname = P(MathCommand, function(_) {
- _.createLeftOf = noop;
- _.numBlocks = function() { return 1; };
- _.parser = function() {
+LatexCmds.operatorname = class extends MathCommand {
+ createLeftOf () {};
+ numBlocks () { return 1; };
+ parser () {
return latexMathParser.block.map(function(b) {
// Check for the special case of \operatorname{ans}, which has
// a special html representation
@@ -523,62 +630,62 @@ LatexCmds.operatorname = P(MathCommand, function(_) {
} else {
isAllLetters = false;
}
+ return undefined;
});
- if (isAllLetters && str === 'ans') return LatexCmds[str](str);
+ if (isAllLetters && str === 'ans') {
+ return AnsBuilder();
+ }
// In cases other than `ans`, just return the children directly
return children;
- });
+ }) as ParserAny;
};
-});
+};
+
+LatexCmds.f = class extends Letter {
+ letter:string;
+ constructor() {
+ var letter = 'f';
+ super(letter);
-LatexCmds.f = P(Letter, function(_, super_) {
- _.init = function() {
- Symbol.p.init.call(this, this.letter = 'f', 'f');
+ this.letter = letter;
+ this.htmlTemplate = 'f';
};
- _.italicize = function(bool) {
+ italicize (bool:boolean) {
this.jQ.html('f').toggleClass('mq-f', bool);
- return super_.italicize.apply(this, arguments);
+ return super.italicize(bool);
};
-});
+};
// VanillaSymbol's
-LatexCmds[' '] = LatexCmds.space = P(DigitGroupingChar, function(_, super_) {
- _.init = function () {
- super_.init.call(this, '\\ ', ' ', ' ');
- };
-});
+LatexCmds[' '] = LatexCmds.space = () => new DigitGroupingChar('\\ ', ' ', ' ');
-LatexCmds['.'] = P(DigitGroupingChar, function(_, super_) {
- _.init = function () {
- super_.init.call(this, '.', '.', '.');
- };
-});
+LatexCmds['.'] = () => new DigitGroupingChar('.', '.', '.')
-LatexCmds["'"] = LatexCmds.prime = bind(VanillaSymbol, "'", '′', 'prime');
-LatexCmds['″'] = LatexCmds.dprime = bind(VanillaSymbol, '″', '″', 'double prime');
+LatexCmds["'"] = LatexCmds.prime = bindVanillaSymbol("'", '′', 'prime');
+LatexCmds['″'] = LatexCmds.dprime = bindVanillaSymbol('″', '″', 'double prime');
-LatexCmds.backslash = bind(VanillaSymbol,'\\backslash ','\\', 'backslash');
+LatexCmds.backslash = bindVanillaSymbol('\\backslash ','\\', 'backslash');
if (!CharCmds['\\']) CharCmds['\\'] = LatexCmds.backslash;
-LatexCmds.$ = bind(VanillaSymbol, '\\$', '$', 'dollar');
+LatexCmds.$ = bindVanillaSymbol('\\$', '$', 'dollar');
-LatexCmds.square = bind(VanillaSymbol, '\\square ', '\u25A1', 'square');
-LatexCmds.mid = bind(VanillaSymbol, '\\mid ', '\u2223', 'mid');
+LatexCmds.square = bindVanillaSymbol('\\square ', '\u25A1', 'square');
+LatexCmds.mid = bindVanillaSymbol('\\mid ', '\u2223', 'mid');
// does not use Symbola font
-var NonSymbolaSymbol = P(Symbol, function(_, super_) {
- _.init = function(ch, html) {
- super_.init.call(this, ch, ''+(html || ch)+'');
+class NonSymbolaSymbol extends MQSymbol {
+ constructor (ch:string, html?:string, _unusedMathspeak?:string) {
+ super(ch, ''+(html || ch)+'');
};
-});
+};
-LatexCmds['@'] = NonSymbolaSymbol;
-LatexCmds['&'] = bind(NonSymbolaSymbol, '\\&', '&', 'and');
-LatexCmds['%'] = P(NonSymbolaSymbol, function(_, super_) {
- _.init = function () {
- super_.init.call(this, '\\%', '%', 'percent');
+LatexCmds['@'] = () => new NonSymbolaSymbol('@');
+LatexCmds['&'] = () => new NonSymbolaSymbol('\\&', '&', 'and');
+LatexCmds['%'] = class extends NonSymbolaSymbol {
+ constructor() {
+ super('\\%', '%', 'percent');
};
- _.parser = function () {
+ parser () {
var optWhitespace = Parser.optWhitespace;
var string = Parser.string;
@@ -588,21 +695,21 @@ LatexCmds['%'] = P(NonSymbolaSymbol, function(_, super_) {
.then(
string('\\operatorname{of}')
.map(function () {
- return LatexCmds.percentof();
+ return PercentOfBuilder();
})
- ).or(super_.parser.call(this))
+ ).or(super.parser()) as ParserAny
;
}
-});
+};
LatexCmds['∥'] = LatexCmds.parallel =
- bind(VanillaSymbol, '\\parallel ', '∥', 'parallel');
+ bindVanillaSymbol('\\parallel ', '∥', 'parallel');
LatexCmds['∦'] = LatexCmds.nparallel =
- bind(VanillaSymbol, '\\nparallel ', '∦', 'not parallel');
+ bindVanillaSymbol('\\nparallel ', '∦', 'not parallel');
LatexCmds['⟂'] = LatexCmds.perp =
- bind(VanillaSymbol, '\\perp ', '⟂', 'perpendicular');
+ bindVanillaSymbol('\\perp ', '⟂', 'perpendicular');
//the following are all Greek to me, but this helped a lot: http://www.ams.org/STIX/ion/stixsig03.html
@@ -624,62 +731,58 @@ LatexCmds.sigma =
LatexCmds.tau =
LatexCmds.chi =
LatexCmds.psi =
-LatexCmds.omega = P(Variable, function(_, super_) {
- _.init = function(latex) {
- super_.init.call(this,'\\'+latex+' ','&'+latex+';');
- };
-});
+LatexCmds.omega = (latex) => new Variable('\\'+latex+' ','&'+latex+';');
//why can't anybody FUCKING agree on these
LatexCmds.phi = //W3C or Unicode?
- bind(Variable,'\\phi ','ϕ', 'phi');
+ bindVariable('\\phi ','ϕ', 'phi');
LatexCmds.phiv = //Elsevier and 9573-13
LatexCmds.varphi = //AMS and LaTeX
- bind(Variable,'\\varphi ','φ', 'phi');
+ bindVariable('\\varphi ','φ', 'phi');
LatexCmds.epsilon = //W3C or Unicode?
- bind(Variable,'\\epsilon ','ϵ', 'epsilon');
+ bindVariable('\\epsilon ','ϵ', 'epsilon');
LatexCmds.epsiv = //Elsevier and 9573-13
LatexCmds.varepsilon = //AMS and LaTeX
- bind(Variable,'\\varepsilon ','ε', 'epsilon');
+ bindVariable('\\varepsilon ','ε', 'epsilon');
LatexCmds.piv = //W3C/Unicode and Elsevier and 9573-13
LatexCmds.varpi = //AMS and LaTeX
- bind(Variable,'\\varpi ','ϖ', 'piv');
+ bindVariable('\\varpi ','ϖ', 'piv');
LatexCmds.sigmaf = //W3C/Unicode
LatexCmds.sigmav = //Elsevier
LatexCmds.varsigma = //LaTeX
- bind(Variable,'\\varsigma ','ς', 'sigma');
+ bindVariable('\\varsigma ','ς', 'sigma');
LatexCmds.thetav = //Elsevier and 9573-13
LatexCmds.vartheta = //AMS and LaTeX
LatexCmds.thetasym = //W3C/Unicode
- bind(Variable,'\\vartheta ','ϑ', 'theta');
+ bindVariable('\\vartheta ','ϑ', 'theta');
LatexCmds.upsilon = //AMS and LaTeX and W3C/Unicode
LatexCmds.upsi = //Elsevier and 9573-13
- bind(Variable,'\\upsilon ','υ', 'upsilon');
+ bindVariable('\\upsilon ','υ', 'upsilon');
//these aren't even mentioned in the HTML character entity references
LatexCmds.gammad = //Elsevier
LatexCmds.Gammad = //9573-13 -- WTF, right? I dunno if this was a typo in the reference (see above)
LatexCmds.digamma = //LaTeX
- bind(Variable,'\\digamma ','ϝ', 'gamma');
+ bindVariable('\\digamma ','ϝ', 'gamma');
LatexCmds.kappav = //Elsevier
LatexCmds.varkappa = //AMS and LaTeX
- bind(Variable,'\\varkappa ','ϰ', 'kappa');
+ bindVariable('\\varkappa ','ϰ', 'kappa');
LatexCmds.rhov = //Elsevier and 9573-13
LatexCmds.varrho = //AMS and LaTeX
- bind(Variable,'\\varrho ','ϱ', 'rho');
+ bindVariable('\\varrho ','ϱ', 'rho');
//Greek constants, look best in non-italicized Times New Roman
-LatexCmds.pi = LatexCmds['π'] = bind(NonSymbolaSymbol,'\\pi ','π', 'pi');
-LatexCmds.lambda = bind(NonSymbolaSymbol,'\\lambda ','λ', 'lambda');
+LatexCmds.pi = LatexCmds['π'] = () => new NonSymbolaSymbol('\\pi ','π', 'pi');
+LatexCmds.lambda = () => new NonSymbolaSymbol('\\lambda ','λ', 'lambda');
//uppercase greek letters
@@ -687,7 +790,7 @@ LatexCmds.Upsilon = //LaTeX
LatexCmds.Upsi = //Elsevier and 9573-13
LatexCmds.upsih = //W3C/Unicode "upsilon with hook"
LatexCmds.Upsih = //'cos it makes sense to me
- bind(Symbol,'\\Upsilon ','ϒ', 'capital upsilon'); //Symbola's 'upsilon with a hook' is a capital Y without hooks :(
+ () => new MQSymbol('\\Upsilon ','ϒ', 'capital upsilon'); //Symbola's 'upsilon with a hook' is a capital Y without hooks :(
//other symbols with the same LaTeX command and HTML character entity reference
LatexCmds.Gamma =
@@ -700,32 +803,38 @@ LatexCmds.Sigma =
LatexCmds.Phi =
LatexCmds.Psi =
LatexCmds.Omega =
-LatexCmds.forall = P(VanillaSymbol, function(_, super_) {
- _.init = function(latex) {
- super_.init.call(this,'\\'+latex+' ','&'+latex+';');
- };
-});
+LatexCmds.forall = (latex) => new VanillaSymbol('\\'+latex+' ','&'+latex+';')
// symbols that aren't a single MathCommand, but are instead a whole
// Fragment. Creates the Fragment from a LaTeX string
-var LatexFragment = P(MathCommand, function(_) {
- _.init = function(latex) { this.latex = latex; };
- _.createLeftOf = function(cursor) {
- var block = latexMathParser.parse(this.latex);
- block.children().adopt(cursor.parent, cursor[L], cursor[R]);
+class LatexFragment extends MathCommand {
+ latexStr:string;
+
+ constructor (latex:string) {
+ super();
+ this.latexStr = latex;
+ }
+
+ createLeftOf (cursor:Cursor) {
+ var block = latexMathParser.parse(this.latexStr);
+ block.children().adopt(cursor.parent, cursor[L] as MQNode, cursor[R] as MQNode);
cursor[L] = block.ends[R];
block.jQize().insertBefore(cursor.jQ);
block.finalizeInsert(cursor.options, cursor);
- if (block.ends[R][R].siblingCreated) block.ends[R][R].siblingCreated(cursor.options, L);
- if (block.ends[L][L].siblingCreated) block.ends[L][L].siblingCreated(cursor.options, R);
- cursor.parent.bubble(function (node) { node.reflow(); });
+ var blockEndsR = block.ends[R];
+ var blockEndsRR = blockEndsR && blockEndsR[R];
+ if (blockEndsRR) blockEndsRR.siblingCreated(cursor.options, L);
+ var blockEndsL = block.ends[L];
+ var blockEndsLL = blockEndsL && blockEndsL[L] ;
+ if (blockEndsLL) blockEndsLL.siblingCreated(cursor.options, R);
+ cursor.parent.bubble(function (node) { node.reflow(); return undefined; });
};
- _.mathspeak = function() { return latexMathParser.parse(this.latex).mathspeak(); };
- _.parser = function() {
- var frag = latexMathParser.parse(this.latex).children();
- return Parser.succeed(frag);
+ mathspeak () { return latexMathParser.parse(this.latexStr).mathspeak(); };
+ parser () {
+ var frag = latexMathParser.parse(this.latexStr).children();
+ return Parser.succeed(frag) as ParserAny;
};
-});
+};
// for what seems to me like [stupid reasons][1], Unicode provides
// subscripted and superscripted versions of all ten Arabic numerals,
@@ -751,20 +860,20 @@ var LatexFragment = P(MathCommand, function(_) {
// [2]: http://en.wikipedia.org/wiki/Number_Forms
// [3]: http://en.wikipedia.org/wiki/ISO/IEC_8859-1
// [4]: http://en.wikipedia.org/wiki/Windows-1252
-LatexCmds['⁰'] = bind(LatexFragment, '^0');
-LatexCmds['¹'] = bind(LatexFragment, '^1');
-LatexCmds['²'] = bind(LatexFragment, '^2');
-LatexCmds['³'] = bind(LatexFragment, '^3');
-LatexCmds['⁴'] = bind(LatexFragment, '^4');
-LatexCmds['⁵'] = bind(LatexFragment, '^5');
-LatexCmds['⁶'] = bind(LatexFragment, '^6');
-LatexCmds['⁷'] = bind(LatexFragment, '^7');
-LatexCmds['⁸'] = bind(LatexFragment, '^8');
-LatexCmds['⁹'] = bind(LatexFragment, '^9');
-
-LatexCmds['¼'] = bind(LatexFragment, '\\frac14');
-LatexCmds['½'] = bind(LatexFragment, '\\frac12');
-LatexCmds['¾'] = bind(LatexFragment, '\\frac34');
+LatexCmds['⁰'] = () => new LatexFragment('^0');
+LatexCmds['¹'] = () => new LatexFragment('^1');
+LatexCmds['²'] = () => new LatexFragment('^2');
+LatexCmds['³'] = () => new LatexFragment('^3');
+LatexCmds['⁴'] = () => new LatexFragment('^4');
+LatexCmds['⁵'] = () => new LatexFragment('^5');
+LatexCmds['⁶'] = () => new LatexFragment('^6');
+LatexCmds['⁷'] = () => new LatexFragment('^7');
+LatexCmds['⁸'] = () => new LatexFragment('^8');
+LatexCmds['⁹'] = () => new LatexFragment('^9');
+
+LatexCmds['¼'] = () => new LatexFragment('\\frac14');
+LatexCmds['½'] = () => new LatexFragment('\\frac12');
+LatexCmds['¾'] = () => new LatexFragment('\\frac34');
// this is a hack to make pasting the √ symbol
// actually insert a sqrt command. This isn't ideal,
@@ -788,16 +897,20 @@ LatexCmds['¾'] = bind(LatexFragment, '\\frac34');
// act more like simply typing the characters out. I'd be scared to try
// to make that change because I'm fairly confident I'd break something
// around handling valid latex as latex rather than treating it as keystrokes.
-LatexCmds['√'] = bind(LatexFragment, '\\sqrt{}');
+LatexCmds['√'] = () => new LatexFragment('\\sqrt{}');
// Binary operator determination is used in several contexts for PlusMinus nodes and their descendants.
// For instance, we set the item's class name based on this factor, and also assign different mathspeak values (plus vs positive, negative vs minus).
-function isBinaryOperator(node) {
- if (node[L]) {
+function isBinaryOperator(node:NodeRef):boolean {
+ if (!node) return false;
+
+ const nodeL = node[L];
+
+ if (nodeL) {
// If the left sibling is a binary operator or a separator (comma, semicolon, colon, space)
// or an open bracket (open parenthesis, open square bracket)
// consider the operator to be unary
- if (node[L] instanceof BinaryOperator || /^(\\ )|[,;:\(\[]$/.test(node[L].ctrlSeq)) {
+ if (nodeL instanceof BinaryOperator || /^(\\ )|[,;:\(\[]$/.test(nodeL.ctrlSeq!)) {
return false;
}
} else if (node.parent && node.parent.parent && node.parent.parent.isStyleBlock()) {
@@ -812,10 +925,16 @@ function isBinaryOperator(node) {
return true;
}
-var PlusMinus = P(BinaryOperator, function(_) {
+var PlusMinus = class extends BinaryOperator {
+ constructor (ch?:string, html?:string, mathspeak?:string) {
+ super(ch, html, undefined, mathspeak, true);
+ };
- _.init = VanillaSymbol.prototype.init;
- _.contactWeld = _.siblingCreated = _.siblingDeleted = function(opts, dir) {
+ contactWeld (cursor:Cursor, dir?:Direction) { this.sharedSiblingMethod(cursor.options, dir)}
+ siblingCreated (opts:CursorOptions, dir:Direction) { this.sharedSiblingMethod(opts, dir)}
+ siblingDeleted (opts:CursorOptions, dir:Direction) { this.sharedSiblingMethod(opts, dir)}
+
+ sharedSiblingMethod (_opts?:CursorOptions, dir?:Direction) {
if (dir === R) return; // ignore if sibling only changed on the right
this.jQ[0].className = isBinaryOperator(this)
? 'mq-binary-operator'
@@ -823,164 +942,172 @@ var PlusMinus = P(BinaryOperator, function(_) {
return this;
};
-});
+};
-LatexCmds['+'] = P(PlusMinus, function(_, super_) {
- _.init = function () {
- super_.init.call(this, '+', '+');
+LatexCmds['+'] = class extends PlusMinus {
+ constructor () {
+ super('+', '+');
};
- _.mathspeak = function() {
+ mathspeak ():string {
return isBinaryOperator(this) ? 'plus' : 'positive';
};
-});
+};
//yes, these are different dashes, en-dash, em-dash, unicode minus, actual dash
-LatexCmds['−'] = LatexCmds['—'] = LatexCmds['–'] = LatexCmds['-'] = P(PlusMinus, function(_, super_) {
- _.init = function () {
- super_.init.call(this, '-', '−');
+class MinusNode extends PlusMinus {
+ constructor () {
+ super('-', '−');
};
- _.mathspeak = function() {
+ mathspeak ():string {
return isBinaryOperator(this) ? 'minus' : 'negative';
};
-});
+};
+LatexCmds['−'] = LatexCmds['—'] = LatexCmds['–'] = LatexCmds['-'] = MinusNode;
LatexCmds['±'] = LatexCmds.pm = LatexCmds.plusmn = LatexCmds.plusminus =
- bind(PlusMinus,'\\pm ','±', 'plus-or-minus');
+ () => new PlusMinus('\\pm ','±', 'plus-or-minus');
LatexCmds.mp = LatexCmds.mnplus = LatexCmds.minusplus =
- bind(PlusMinus,'\\mp ','∓', 'minus-or-plus');
+ () => new PlusMinus('\\mp ','∓', 'minus-or-plus');
CharCmds['*'] = LatexCmds.sdot = LatexCmds.cdot =
- bind(BinaryOperator, '\\cdot ', '·', '*', 'times'); //semantically should be ⋅, but · looks better
+ bindBinaryOperator('\\cdot ', '·', '*', 'times'); //semantically should be ⋅, but · looks better
-var To = P(BinaryOperator, function(_, super_) {
- _.init = function() {
- super_.init.call(this, '\\to ','→', 'to');
+class To extends BinaryOperator {
+ constructor () {
+ super('\\to ','→', 'to');
}
- _.deleteTowards = function(dir, cursor) {
+ deleteTowards (dir:Direction, cursor:Cursor) {
if (dir === L) {
- var l = cursor[L];
- Fragment(l, this).remove();
+ var l = cursor[L] as MQNode;
+ new Fragment(l, this).remove();
cursor[L] = l[L];
- LatexCmds['−']().createLeftOf(cursor);
- cursor[L].bubble(function (node) { node.reflow(); });
+ new MinusNode().createLeftOf(cursor);
+ (cursor[L] as MQNode).bubble(function (node) { node.reflow(); return undefined });
return;
}
- super_.deleteTowards.apply(this, arguments);
+ super.deleteTowards(dir, cursor);
};
-})
+}
LatexCmds['→'] = LatexCmds.to = To;
-var Inequality = P(BinaryOperator, function(_, super_) {
- _.init = function(data, strict) {
+class Inequality extends BinaryOperator {
+ strict:boolean;
+ data:InequalityData;
+
+ constructor (data:InequalityData, strict:boolean) {
+ var strictness:''|'Strict' = (strict ? 'Strict' : '');
+ super(data[`ctrlSeq${strictness}`], data[`html${strictness}`],
+ data[`text${strictness}`], data[`mathspeak${strictness}`]);
+
this.data = data;
this.strict = strict;
- var strictness = (strict ? 'Strict' : '');
- super_.init.call(this, data['ctrlSeq'+strictness], data['html'+strictness],
- data['text'+strictness], data['mathspeak'+strictness]);
- };
- _.swap = function(strict) {
+ }
+
+ swap (strict:boolean) {
this.strict = strict;
- var strictness = (strict ? 'Strict' : '');
- this.ctrlSeq = this.data['ctrlSeq'+strictness];
- this.jQ.html(this.data['html'+strictness]);
- this.textTemplate = [ this.data['text'+strictness] ];
- this.mathspeakName = this.data['mathspeak'+strictness];
+ var strictness:''|'Strict' = (strict ? 'Strict' : '');
+ this.ctrlSeq = this.data[`ctrlSeq${strictness}`];
+ this.jQ.html(this.data[`html${strictness}`]);
+ this.textTemplate = [this.data[`text${strictness}`] ];
+ this.mathspeakName = this.data[`mathspeak${strictness}`];
};
- _.deleteTowards = function(dir, cursor) {
+ deleteTowards (dir:Direction, cursor:Cursor) {
if (dir === L && !this.strict) {
this.swap(true);
- this.bubble(function (node) { node.reflow(); });
+ this.bubble(function (node) { node.reflow(); return undefined });
return;
}
- super_.deleteTowards.apply(this, arguments);
+ super.deleteTowards(dir, cursor);
};
-});
+};
-var less = { ctrlSeq: '\\le ', html: '≤', text: '≤', mathspeak: 'less than or equal to',
+var less:InequalityData = { ctrlSeq: '\\le ', html: '≤', text: '≤', mathspeak: 'less than or equal to',
ctrlSeqStrict: '<', htmlStrict: '<', textStrict: '<', mathspeakStrict: 'less than'};
-var greater = { ctrlSeq: '\\ge ', html: '≥', text: '≥', mathspeak: 'greater than or equal to',
+var greater:InequalityData = { ctrlSeq: '\\ge ', html: '≥', text: '≥', mathspeak: 'greater than or equal to',
ctrlSeqStrict: '>', htmlStrict: '>', textStrict: '>', mathspeakStrict: 'greater than'};
-var Greater = P(Inequality, function(_, super_) {
- _.init = function() {
- super_.init.call(this, greater, true);
+class Greater extends Inequality {
+ constructor () {
+ super(greater, true);
};
- _.createLeftOf = function(cursor) {
- if (cursor[L] instanceof BinaryOperator && cursor[L].ctrlSeq === '-') {
- var l = cursor[L];
+ createLeftOf (cursor:Cursor) {
+ const cursorL = cursor[L];
+ if (cursorL instanceof BinaryOperator && cursorL.ctrlSeq === '-') {
+ var l = cursorL;
cursor[L] = l[L];
l.remove();
- To().createLeftOf(cursor);
- cursor[L].bubble(function (node) { node.reflow(); });
+ new To().createLeftOf(cursor);
+ (cursor[L] as MQNode).bubble(function (node) { node.reflow(); return undefined });
return;
}
- super_.createLeftOf.apply(this, arguments);
+ super.createLeftOf(cursor);
};
-})
+}
-LatexCmds['<'] = LatexCmds.lt = bind(Inequality, less, true);
+LatexCmds['<'] = LatexCmds.lt = () => new Inequality(less, true);
LatexCmds['>'] = LatexCmds.gt = Greater;
-LatexCmds['≤'] = LatexCmds.le = LatexCmds.leq = bind(Inequality, less, false);
-LatexCmds['≥'] = LatexCmds.ge = LatexCmds.geq = bind(Inequality, greater, false);
+LatexCmds['≤'] = LatexCmds.le = LatexCmds.leq = () => new Inequality(less, false);
+LatexCmds['≥'] = LatexCmds.ge = LatexCmds.geq = () => new Inequality(greater, false);
LatexCmds.infty = LatexCmds.infin = LatexCmds.infinity =
- bind(VanillaSymbol,'\\infty ','∞', 'infinity');
-LatexCmds['≠'] = LatexCmds.ne = LatexCmds.neq = bind(BinaryOperator,'\\ne ','≠', 'not equal');
+ bindVanillaSymbol('\\infty ','∞', 'infinity');
+LatexCmds['≠'] = LatexCmds.ne = LatexCmds.neq = bindBinaryOperator('\\ne ','≠', 'not equal');
-var Equality = P(BinaryOperator, function(_, super_) {
- _.init = function() {
- super_.init.call(this, '=', '=', '=', 'equals');
+class Equality extends BinaryOperator {
+ constructor () {
+ super('=', '=', '=', 'equals');
};
- _.createLeftOf = function(cursor) {
- if (cursor[L] instanceof Inequality && cursor[L].strict) {
- cursor[L].swap(false);
- cursor[L].bubble(function (node) { node.reflow(); });
+ createLeftOf (cursor:Cursor) {
+ var cursorL = cursor[L];
+ if (cursorL instanceof Inequality && cursorL.strict) {
+ cursorL.swap(false);
+ cursorL.bubble(function (node) { node.reflow(); return undefined});
return;
}
- super_.createLeftOf.apply(this, arguments);
+ super.createLeftOf(cursor);
};
-});
+};
LatexCmds['='] = Equality;
-LatexCmds['×'] = LatexCmds.times = bind(BinaryOperator, '\\times ', '×', '[x]', 'times');
+LatexCmds['×'] = LatexCmds.times = bindBinaryOperator('\\times ', '×', '[x]', 'times');
LatexCmds['÷'] = LatexCmds.div = LatexCmds.divide = LatexCmds.divides =
- bind(BinaryOperator,'\\div ','÷', '[/]', 'over');
+ bindBinaryOperator('\\div ','÷', '[/]', 'over');
-var Sim = P(BinaryOperator, function(_, super_) {
- _.init = function() {
- super_.init.call(this, '\\sim ', '~', '~', 'tilde');
+class Sim extends BinaryOperator {
+ constructor () {
+ super('\\sim ', '~', '~', 'tilde');
};
- _.createLeftOf = function(cursor) {
+ createLeftOf (cursor:Cursor) {
if (cursor[L] instanceof Sim) {
- var l = cursor[L];
+ var l = cursor[L] as MQNode;
cursor[L] = l[L];
l.remove();
- Approx().createLeftOf(cursor);
- cursor[L].bubble(function (node) { node.reflow(); });
+ new Approx().createLeftOf(cursor);
+ (cursor[L] as MQNode).bubble(function (node) { node.reflow(); return undefined });
return;
}
- super_.createLeftOf.apply(this, arguments);
+ super.createLeftOf(cursor);
};
-});
+};
-var Approx = P(BinaryOperator, function(_, super_) {
- _.init = function() {
- super_.init.call(this, '\\approx ', '≈', '≈', 'approximately equal');
+class Approx extends BinaryOperator {
+ constructor () {
+ super('\\approx ', '≈', '≈', 'approximately equal');
};
- _.deleteTowards = function(dir, cursor) {
+ deleteTowards (dir:Direction, cursor:Cursor) {
if (dir === L) {
- var l = cursor[L];
- Fragment(l, this).remove();
+ var l = cursor[L] as MQNode;
+ new Fragment(l, this).remove();
cursor[L] = l[L];
- Sim().createLeftOf(cursor);
- cursor[L].bubble(function (node) { node.reflow(); });
+ new Sim().createLeftOf(cursor);
+ (cursor[L] as MQNode).bubble(function (node) { node.reflow(); return undefined });
return;
}
- super_.deleteTowards.apply(this, arguments);
+ super.deleteTowards(dir, cursor);
};
-});
+};
CharCmds['~'] = LatexCmds.sim = Sim;
LatexCmds['≈'] = LatexCmds.approx = Approx;
diff --git a/src/commands/math/commands.js b/src/commands/math/commands.ts
similarity index 54%
rename from src/commands/math/commands.js
rename to src/commands/math/commands.ts
index 3a5214ac8..654045a23 100644
--- a/src/commands/math/commands.js
+++ b/src/commands/math/commands.ts
@@ -3,6 +3,7 @@
**************************/
var SVG_SYMBOLS = {
'sqrt': {
+ width: '',
html:
'