Merge pull request #591 from italoc-84/patch-1
Add \nexists symbol
diff --git a/README.md b/README.md
index 7b93213..956bee5 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,7 @@
Additionally, descendants of `MQ.EditableField` (currently only `MQ.MathField`)
expose:
+* `.focus()`, `.blur()` focuses or defocuses the editable field
* `.write(' - 1')` will write some LaTeX at the current cursor position
* `.cmd('\\sqrt')` will enter a LaTeX command at the current cursor position or
with the current selection
diff --git a/src/commands/math/advancedSymbols.js b/src/commands/math/advancedSymbols.js
index 085da1c..f5aa5a5 100644
--- a/src/commands/math/advancedSymbols.js
+++ b/src/commands/math/advancedSymbols.js
@@ -222,10 +222,6 @@
LatexCmds.rbrack = bind(VanillaSymbol, ']');
//various symbols
-LatexCmds['∫'] =
-LatexCmds['int'] =
-LatexCmds.integral = bind(Symbol,'\\int ','<big>∫</big>');
-
LatexCmds.slash = bind(VanillaSymbol, '/');
LatexCmds.vert = bind(VanillaSymbol,'|');
LatexCmds.perp = LatexCmds.perpendicular = bind(VanillaSymbol,'\\perp ','⊥');
diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js
index 37c00bf..414edaa 100644
--- a/src/commands/math/commands.js
+++ b/src/commands/math/commands.js
@@ -380,6 +380,10 @@
LatexCmds.coprod =
LatexCmds.coproduct = bind(SummationNotation,'\\coprod ','∐');
+LatexCmds['∫'] =
+LatexCmds['int'] =
+LatexCmds.integral = bind(Symbol,'\\int ','<big>∫</big>');
+
var Fraction =
LatexCmds.frac =
LatexCmds.dfrac =
diff --git a/src/commands/text.js b/src/commands/text.js
index 42f85d4..d4352bd 100644
--- a/src/commands/text.js
+++ b/src/commands/text.js
@@ -48,10 +48,8 @@
return optWhitespace
.then(string('{')).then(regex(/^[^}]*/)).skip(string('}'))
.map(function(text) {
- // TODO: is this the correct behavior when parsing
- // the latex \text{} ? This violates the requirement that
- // the text contents are always nonempty. Should we just
- // disown the parent node instead?
+ if (text.length === 0) return Fragment();
+
TextPiece(text).adopt(textBlock, 0, 0);
return textBlock;
})
@@ -64,7 +62,11 @@
});
};
_.text = function() { return '"' + this.textContents() + '"'; };
- _.latex = function() { return '\\text{' + this.textContents() + '}'; };
+ _.latex = function() {
+ var contents = this.textContents();
+ if (contents.length === 0) return '';
+ return '\\text{' + contents + '}';
+ };
_.html = function() {
return (
'<span class="mq-text-mode" mathquill-command-id='+this.id+'>'
@@ -107,7 +109,7 @@
else { // split apart
var leftBlock = TextBlock();
var leftPc = this.ends[L];
- leftPc.disown();
+ leftPc.disown().jQ.detach();
leftPc.adopt(leftBlock, 0, 0);
cursor.insLeftOf(this);
@@ -162,15 +164,22 @@
}
};
- _.blur = function() {
+ _.blur = function(cursor) {
MathBlock.prototype.blur.call(this);
- fuseChildren(this);
+ if (!cursor) return;
+ if (this.textContents() === '') {
+ this.remove();
+ if (cursor[L] === this) cursor[L] = this[L];
+ else if (cursor[R] === this) cursor[R] = this[R];
+ }
+ else fuseChildren(this);
};
function fuseChildren(self) {
self.jQ[0].normalize();
var textPcDom = self.jQ[0].firstChild;
+ if (!textPcDom) return;
pray('only node in TextBlock span is Text node', textPcDom.nodeType === 3);
// nodeType === 3 has meant a Text node since ancient times:
// http://reference.sitepoint.com/javascript/Node/nodeType
diff --git a/src/css/math.less b/src/css/math.less
index 6115ee8..fcaa23d 100644
--- a/src/css/math.less
+++ b/src/css/math.less
@@ -49,7 +49,14 @@
.mq-text-mode {
- font-size: 87%;
+ display: inline-block;
+ }
+ .mq-text-mode.mq-hasCursor {
+ box-shadow: inset darkgray 0 .1em .2em;
+ padding: 0 .1em;
+ margin: 0 -.1em;
+
+ min-width: 1ex;
}
.mq-font {
diff --git a/src/cursor.js b/src/cursor.js
index b08b2f6..622a023 100644
--- a/src/cursor.js
+++ b/src/cursor.js
@@ -56,7 +56,8 @@
this[-dir] = oppDir;
// by contract, .blur() is called after all has been said and done
// and the cursor has actually been moved
- if (oldParent !== parent && oldParent.blur) oldParent.blur();
+ // FIXME pass cursor to .blur() so text can fix cursor pointers when removing itself
+ if (oldParent !== parent && oldParent.blur) oldParent.blur(this);
};
_.insDirOf = function(dir, el) {
prayDirection(dir);
diff --git a/src/services/latex.js b/src/services/latex.js
index 025f1a8..75369e8 100644
--- a/src/services/latex.js
+++ b/src/services/latex.js
@@ -1,6 +1,6 @@
-// Parser MathCommand
+// Parser MathBlock
var latexMathParser = (function() {
- function commandToBlock(cmd) {
+ function commandToBlock(cmd) { // can also take in a Fragment
var block = MathBlock();
cmd.adopt(block, 0, 0);
return block;
diff --git a/test/unit/backspace.test.js b/test/unit/backspace.test.js
index 51a34ca..688d1ba 100644
--- a/test/unit/backspace.test.js
+++ b/test/unit/backspace.test.js
@@ -197,7 +197,7 @@
assert.equal(mq.latex(),'n=1');
});
- test('backspace into text block', function() {
+ test('backspace through text block', function() {
mq.latex('\\text{x}');
mq.keystroke('Backspace');
@@ -206,6 +206,18 @@
assert.equal(cursor.parent, textBlock, 'cursor is in text block');
assert.equal(cursor[R], 0, 'cursor is at the end of text block');
assert.equal(cursor[L].text, 'x', 'cursor is rightward of the x');
+ assert.equal(mq.latex(), '\\text{x}', 'the x has been deleted');
+
+ mq.keystroke('Backspace');
+ assert.equal(cursor.parent, textBlock, 'cursor is still in text block');
+ assert.equal(cursor[R], 0, 'cursor is at the right end of the text block');
+ assert.equal(cursor[L], 0, 'cursor is at the left end of the text block');
+ assert.equal(mq.latex(), '', 'the x has been deleted');
+
+ mq.keystroke('Backspace');
+ assert.equal(cursor[R], 0, 'cursor is at the right end of the root block');
+ assert.equal(cursor[L], 0, 'cursor is at the left end of the root block');
+ assert.equal(mq.latex(), '');
});
suite('empties', function() {
diff --git a/test/unit/latex.test.js b/test/unit/latex.test.js
index 7cfa094..4cf59fa 100644
--- a/test/unit/latex.test.js
+++ b/test/unit/latex.test.js
@@ -100,6 +100,7 @@
assertParsesLatex('\\text { lol! } ', '\\text{ lol! }');
assertParsesLatex('\\text{apples} \\ne \\text{oranges}',
'\\text{apples}\\ne \\text{oranges}');
+ assertParsesLatex('\\text{}', '');
});
test('not real LaTex commands, but valid symbols', function() {
diff --git a/test/unit/text.test.js b/test/unit/text.test.js
index 6f2ecd3..982a5ac 100644
--- a/test/unit/text.test.js
+++ b/test/unit/text.test.js
@@ -65,4 +65,43 @@
assert.equal(block.latex(), '\\text{x}');
});
+
+ test('stepping out of an empty block deletes it', function() {
+ var mq = MathQuill.MathField($('<span></span>').appendTo('#mock')[0]);
+ var controller = mq.__controller;
+ var cursor = controller.cursor;
+
+ try {
+ mq.latex('\\text{x}');
+
+ mq.keystroke('Left');
+ assertSplit(cursor.jQ, 'x');
+
+ mq.keystroke('Backspace');
+ assertSplit(cursor.jQ);
+
+ mq.keystroke('Right');
+ assertSplit(cursor.jQ);
+ assert.equal(cursor[L], 0);
+ } finally {
+ $(mq.el()).remove();
+ }
+ });
+
+ test('typing $ in a textblock splits it', function() {
+ var mq = MathQuill.MathField($('<span></span>').appendTo('#mock')[0]);
+ var controller = mq.__controller;
+ var cursor = controller.cursor;
+
+ try {
+ mq.latex('\\text{asdf}');
+ mq.keystroke('Left Left Left');
+ assertSplit(cursor.jQ, 'as', 'df');
+
+ mq.typedText('$');
+ assert.equal(mq.latex(), '\\text{as}\\text{df}');
+ } finally {
+ $(mq.el()).remove();
+ }
+ });
});