From 0e9eea9dea5c2327a71659dae0f3768d6fdae120 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Fri, 7 Jul 2017 12:11:26 +1000 Subject: [PATCH 01/13] Add direction-sensitive implementation of leftRightIntoCmdGoes for matrix. This lets the user cross over the top or bottom of the matrix naturally without getting caught inside it, using the same config option as for fractions. --- src/commands/math.js | 6 ++++ src/commands/math/commands.js | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/commands/math.js b/src/commands/math.js index a8d8d6fa6..13c048c20 100644 --- a/src/commands/math.js +++ b/src/commands/math.js @@ -105,6 +105,9 @@ var MathCommand = P(MathElement, function(_, super_) { // the cursor _.moveTowards = function(dir, cursor, updown) { var updownInto = updown && this[updown+'Into']; + if (typeof updownInto === 'function') { + updownInto = updownInto.call(this, dir, cursor); + } cursor.insAtDirEnd(-dir, updownInto || this.ends[-dir]); }; _.deleteTowards = function(dir, cursor) { @@ -377,6 +380,9 @@ var MathBlock = P(MathElement, function(_, super_) { // the cursor _.moveOutOf = function(dir, cursor, updown) { var updownInto = updown && this.parent[updown+'Into']; + if (typeof updownInto === 'function') { + updownInto = updownInto.call(this.parent, dir, cursor); + } if (!updownInto && this[dir]) cursor.insAtDirEnd(-dir, this[dir]); else cursor.insDirOf(dir, this.parent); }; diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index 3b347deac..66460176c 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -1007,6 +1007,58 @@ Environments.matrix = P(Environment, function(_, super_) { table.toggleClass('mq-rows-1', table.find('tr').length === 1); this.relink(); }; + _.upInto = function(dir, cursor) { + var rowSize = this.getRowSize(); + if (cursor.parent.parent === this) { + // We're inside the matrix. + var i = this.blocks.indexOf(cursor.parent); + if (dir === L) { + // If we're on the left edge and moving left, we should exit. + return i % rowSize === 0; + } else { + // If we're on the right edge and moving right, we should exit. + return (i + 1) % rowSize == 0; + } + } + // Otherwise, we must be about to enter the matrix. + if (dir === L) { + return this.blocks[rowSize - 1]; + } else { + return this.blocks[0]; + } + } + _.downInto = function(dir, cursor) { + var rowSize = this.getRowSize(); + if (cursor.parent.parent === this) { + // We're inside the matrix. + var i = this.blocks.indexOf(cursor.parent); + if (dir === L) { + // If we're on the left edge and moving left, we should exit. + return i % rowSize === 0; + } else { + // If we're on the right edge and moving right, we should exit. + return (i + 1) % rowSize == 0; + } + } + // Otherwise, we must be about to enter the matrix. + if (dir === L) { + return this.blocks[this.blocks.length - 1]; + } else { + return this.blocks[this.blocks.length - rowSize]; + } + } + + _.getRowSize = function() { + var cell; + for (var i=0; i Date: Thu, 27 Jul 2017 16:37:04 +1000 Subject: [PATCH 02/13] Implement as getEntryPoint and atExitPoint instead of updowninto. By overriding moveTowards and moveOutOf on matrix and MatrixCell respectively, we can avoid changing {up,down}Into into functions and so keep these changes restricted to affecting the matrix environment. --- src/commands/math/commands.js | 57 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index 66460176c..ac4b569d7 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -1007,30 +1007,30 @@ Environments.matrix = P(Environment, function(_, super_) { table.toggleClass('mq-rows-1', table.find('tr').length === 1); this.relink(); }; - _.upInto = function(dir, cursor) { + // Enter the matrix at the top or bottom row if updown is configured. + _.getEntryPoint = function(dir, cursor, updown) { var rowSize = this.getRowSize(); - if (cursor.parent.parent === this) { - // We're inside the matrix. - var i = this.blocks.indexOf(cursor.parent); + if (updown === 'up') { if (dir === L) { - // If we're on the left edge and moving left, we should exit. - return i % rowSize === 0; + return this.blocks[rowSize - 1]; } else { - // If we're on the right edge and moving right, we should exit. - return (i + 1) % rowSize == 0; + return this.blocks[0]; + } + } else if (updown === 'down') { + // Otherwise, we must be about to enter the matrix. + if (dir === L) { + return this.blocks[this.blocks.length - 1]; + } else { + return this.blocks[this.blocks.length - rowSize]; } - } - // Otherwise, we must be about to enter the matrix. - if (dir === L) { - return this.blocks[rowSize - 1]; } else { - return this.blocks[0]; + pray("Invalid value for updown '" + updown + "'", false); } - } - _.downInto = function(dir, cursor) { - var rowSize = this.getRowSize(); - if (cursor.parent.parent === this) { - // We're inside the matrix. + }; + // Exit the matrix at the first and last columns if updown is configured. + _.atExitPoint = function(dir, cursor) { + var rowSize = this.getRowSize(); + // Which block are we in? var i = this.blocks.indexOf(cursor.parent); if (dir === L) { // If we're on the left edge and moving left, we should exit. @@ -1039,14 +1039,11 @@ Environments.matrix = P(Environment, function(_, super_) { // If we're on the right edge and moving right, we should exit. return (i + 1) % rowSize == 0; } - } - // Otherwise, we must be about to enter the matrix. - if (dir === L) { - return this.blocks[this.blocks.length - 1]; - } else { - return this.blocks[this.blocks.length - rowSize]; - } - } + }; + _.moveTowards = function(dir, cursor, updown) { + var entryPoint = updown && this.getEntryPoint(dir, cursor, updown); + cursor.insAtDirEnd(-dir, entryPoint || this.ends[-dir]); + }; _.getRowSize = function() { var cell; @@ -1348,5 +1345,11 @@ var MatrixCell = P(MathBlock, function(_, super_) { // called when last cell gets deleted return super_.deleteOutOf.apply(self, args); }); - } + }; + _.moveOutOf = function(dir, cursor, updown) { + var atExitPoint = updown && this.parent.atExitPoint(dir, cursor); + // Step out of the matrix if we've moved past an edge column + if (!atExitPoint && this[dir]) cursor.insAtDirEnd(-dir, this[dir]); + else cursor.insDirOf(dir, this.parent); + }; }); From ba9e675df0b191fcfe69a7547b5d51e1b6ffb1a3 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Thu, 27 Jul 2017 16:47:51 +1000 Subject: [PATCH 03/13] Calculate rowSize as we relink instead of doing extra work. --- src/commands/math/commands.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index ac4b569d7..095b7c081 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -1009,10 +1009,9 @@ Environments.matrix = P(Environment, function(_, super_) { }; // Enter the matrix at the top or bottom row if updown is configured. _.getEntryPoint = function(dir, cursor, updown) { - var rowSize = this.getRowSize(); if (updown === 'up') { if (dir === L) { - return this.blocks[rowSize - 1]; + return this.blocks[this.rowSize - 1]; } else { return this.blocks[0]; } @@ -1021,7 +1020,7 @@ Environments.matrix = P(Environment, function(_, super_) { if (dir === L) { return this.blocks[this.blocks.length - 1]; } else { - return this.blocks[this.blocks.length - rowSize]; + return this.blocks[this.blocks.length - this.rowSize]; } } else { pray("Invalid value for updown '" + updown + "'", false); @@ -1029,15 +1028,14 @@ Environments.matrix = P(Environment, function(_, super_) { }; // Exit the matrix at the first and last columns if updown is configured. _.atExitPoint = function(dir, cursor) { - var rowSize = this.getRowSize(); // Which block are we in? var i = this.blocks.indexOf(cursor.parent); if (dir === L) { // If we're on the left edge and moving left, we should exit. - return i % rowSize === 0; + return i % this.rowSize === 0; } else { // If we're on the right edge and moving right, we should exit. - return (i + 1) % rowSize == 0; + return (i + 1) % this.rowSize == 0; } }; _.moveTowards = function(dir, cursor, updown) { @@ -1045,29 +1043,26 @@ Environments.matrix = P(Environment, function(_, super_) { cursor.insAtDirEnd(-dir, entryPoint || this.ends[-dir]); }; - _.getRowSize = function() { - var cell; - for (var i=0; i Date: Thu, 27 Jul 2017 17:19:09 +1000 Subject: [PATCH 04/13] Add unit tests for leftRightIntoCmdGoes with matrices. --- src/commands/math/commands.js | 2 +- test/unit/typing.test.js | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index 095b7c081..fbd6770d5 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -1035,7 +1035,7 @@ Environments.matrix = P(Environment, function(_, super_) { return i % this.rowSize === 0; } else { // If we're on the right edge and moving right, we should exit. - return (i + 1) % this.rowSize == 0; + return (i + 1) % this.rowSize === 0; } }; _.moveTowards = function(dir, cursor, updown) { diff --git a/test/unit/typing.test.js b/test/unit/typing.test.js index 33d4163e8..7e8a40035 100644 --- a/test/unit/typing.test.js +++ b/test/unit/typing.test.js @@ -1119,6 +1119,62 @@ suite('typing with auto-replaces', function() { assertLatex('\\begin{matrix}&&\\\\b&c&\\\\a&d&\\end{matrix}'); }); + test('passes over matrices when leftRightIntoCmdGoes is set to up', function() { + mq.config({ leftRightIntoCmdGoes: 'up' }); + + // 1 2 3 + // 4 5 6 + // 7 8 9 + mq.write('\\begin{matrix}1&2&3\\\\4&5&6\\\\7&8&9\\end{matrix}'); + + mq.keystroke('Left Left Left Left Left Left Left').typedText('a') + .keystroke('Right Right Right Right Right Right Right').typedText('b') + .keystroke('Left Left Left Left').typedText('c'); + + // It should've entered the top of the matrix and exited at either end, leading to + // 1 2c 3 + // a 4 5 6 b + // 7 8 9 + assertLatex('a\\begin{matrix}1&2c&3\\\\4&5&6\\\\7&8&9\\end{matrix}b'); + }); + + test('passes under matrices when leftRightIntoCmdGoes is set to down', function() { + mq.config({ leftRightIntoCmdGoes: 'down' }); + + // 1 2 3 + // 4 5 6 + // 7 8 9 + mq.write('\\begin{matrix}1&2&3\\\\4&5&6\\\\7&8&9\\end{matrix}'); + + mq.keystroke('Left Left Left Left Left Left Left').typedText('a') + .keystroke('Right Right Right Right Right Right Right').typedText('b') + .keystroke('Left Left Left Left').typedText('c'); + + // It should've entered the bottom of the matrix and exited at either end, leading to + // 1 2 3 + // a 4 5 6 b + // 7 8c 9 + assertLatex('a\\begin{matrix}1&2&3\\\\4&5&6\\\\7&8c&9\\end{matrix}b'); + }); + + test('exits out of matrices on their edges when leftRightIntoCmdGoes is set', function() { + mq.config({ leftRightIntoCmdGoes: 'up' }); + + // 1 2 3 + // 4 5 6 + // 7 8 9 + mq.write('\\begin{matrix}1&2&3\\\\4&5&6\\\\7&8&9\\end{matrix}'); + + mq.keystroke('Left Left Left Down').typedText('a') + .keystroke('Right Right Right').typedText('b') + + // It should've entered the top of the matrix and exited out the side, leading to + // 1 2 3 + // 4 5a 6 b + // 7 8 9 + assertLatex('\\begin{matrix}1&2&3\\\\4&5a&6\\\\7&8&9\\end{matrix}b'); + }); + test('delete key removes empty matrix row/column', function() { mq.write('\\begin{matrix}a&&b\\\\&c&d\\\\&e&f\\end{matrix}'); From 7cd5de415e17cda64a6c227c5bdfdc6b6515b9d8 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Thu, 27 Jul 2017 17:21:13 +1000 Subject: [PATCH 05/13] Remove code for interpreting upDownInto as a function. --- src/commands/math.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/commands/math.js b/src/commands/math.js index 13c048c20..a8d8d6fa6 100644 --- a/src/commands/math.js +++ b/src/commands/math.js @@ -105,9 +105,6 @@ var MathCommand = P(MathElement, function(_, super_) { // the cursor _.moveTowards = function(dir, cursor, updown) { var updownInto = updown && this[updown+'Into']; - if (typeof updownInto === 'function') { - updownInto = updownInto.call(this, dir, cursor); - } cursor.insAtDirEnd(-dir, updownInto || this.ends[-dir]); }; _.deleteTowards = function(dir, cursor) { @@ -380,9 +377,6 @@ var MathBlock = P(MathElement, function(_, super_) { // the cursor _.moveOutOf = function(dir, cursor, updown) { var updownInto = updown && this.parent[updown+'Into']; - if (typeof updownInto === 'function') { - updownInto = updownInto.call(this.parent, dir, cursor); - } if (!updownInto && this[dir]) cursor.insAtDirEnd(-dir, this[dir]); else cursor.insDirOf(dir, this.parent); }; From b480298badf30d39562358a7d6791de5e589bd97 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Fri, 7 Jul 2017 17:44:42 +1000 Subject: [PATCH 06/13] align* environment for rendering multi-line equations using a custom delimiter ('='). --- src/commands/math/commands.js | 74 +++++++++++++++++++++++++++-------- src/css/math.less | 16 +++++++- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index fbd6770d5..738fd5123 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -863,7 +863,7 @@ LatexCmds.begin = P(MathCommand, function(_, super_) { var string = Parser.string; var regex = Parser.regex; return string('{') - .then(regex(/^[a-z]+/i)) + .then(regex(/^[a-z*]+/i)) .skip(string('}')) .then(function (env) { return (Environments[env] ? @@ -888,7 +888,7 @@ var Environment = P(MathCommand, function(_, super_) { var Matrix = Environments.matrix = P(Environment, function(_, super_) { - var delimiters = { + _.delimiters = { column: '&', row: '\\\\' }; @@ -898,6 +898,9 @@ Environments.matrix = P(Environment, function(_, super_) { }; _.environment = 'matrix'; + _.removeEmptyColumns = true; // Default to removing empty columns on deleting cell. + _.removeEmptyRows = true; // Default to removing empty rows on deleting the last cell. + _.reflow = function() { var blockjQ = this.jQ.children('table'); @@ -915,12 +918,12 @@ Environments.matrix = P(Environment, function(_, super_) { this.eachChild(function (cell) { if (typeof row !== 'undefined') { latex += (row !== cell.row) ? - delimiters.row : - delimiters.column; + this.delimiters.row : + this.delimiters.column; } row = cell.row; latex += cell.latex(); - }); + }.bind(this)); return this.wrappers().join(latex); }; @@ -936,18 +939,24 @@ Environments.matrix = P(Environment, function(_, super_) { // Build .. structure from cells this.eachChild(function (cell) { - if (row !== cell.row) { + var isFirstColumn = row !== cell.row; + if (isFirstColumn) { row = cell.row; trs += '$tds'; cells[row] = []; } + if (this.parent.htmlColumnSeparator && !isFirstColumn) { + cells[row].push(this.parent.htmlColumnSeparator); + } cells[row].push('&'+(i++)+''); }); + var tableClasses = this.extraTableClasses ? 'mq-non-leaf ' + this.extraTableClasses : 'mq-non-leaf'; + this.htmlTemplate = '' + parenHtml(this.parentheses.left) - + '' + + '
' + trs.replace(/\$tds/g, function () { return cells.shift().join(''); }) @@ -973,8 +982,8 @@ Environments.matrix = P(Environment, function(_, super_) { var string = Parser.string; return optWhitespace - .then(string(delimiters.column) - .or(string(delimiters.row)) + .then(string(this.delimiters.column) + .or(string(this.delimiters.row)) .or(latexMathParser.block)) .many() .skip(optWhitespace) @@ -993,7 +1002,7 @@ Environments.matrix = P(Environment, function(_, super_) { blocks.push(items[i]); } else { addCell(); - if (items[i] === delimiters.row) row+=1; + if (items[i] === this.delimiters.row) row+=1; } } addCell(); @@ -1100,6 +1109,10 @@ Environments.matrix = P(Environment, function(_, super_) { } maxLength = Math.max.apply(null, lengths); + + // Record the row size as it's useful for determining which edge of the matrix we're on. + this.rowSize = maxLength; + if (maxLength !== Math.min.apply(null, lengths)) { // Pad shorter rows to correct length for (i=0; i 1) { + if (_.removeEmptyRows && isEmpty(myRow) && myColumn.length > 1) { row = rows.indexOf(myRow); // Decrease all following row numbers this.eachChild(function (cell) { @@ -1165,7 +1178,7 @@ Environments.matrix = P(Environment, function(_, super_) { remove(myRow); this.jQ.find('tr').eq(row).remove(); } - if (isEmpty(myColumn) && myRow.length > 1) { + if (_.removeEmptyColumns && isEmpty(myColumn) && myRow.length > 1) { remove(myColumn); } this.finalizeTree(); @@ -1193,14 +1206,17 @@ Environments.matrix = P(Environment, function(_, super_) { }); // Add new cells, one for each column + var isFirstColumn = true; for (var i=0; is and add to new row block.jQ = $(''; + _.delimiters = { + column: '&=', + row: '\\\\', + }; + + // Don't delete empty columns, the align environment is for equations and should always have two columns. + _.removeEmptyColumns = false; + + // For the same reason, don't allow adding columns. + _.addColumn = function() {}; + +}); + // Replacement for mathblocks inside matrix cells // Adds matrix-specific keyboard commands var MatrixCell = P(MathBlock, function(_, super_) { diff --git a/src/css/math.less b/src/css/math.less index b27a3ab43..fead1acdb 100644 --- a/src/css/math.less +++ b/src/css/math.less @@ -50,7 +50,7 @@ .mq-text-mode { display: inline-block; - white-space: pre; + white-space: pre; } .mq-text-mode.mq-hasCursor { @@ -311,7 +311,7 @@ display: block; text-align: center; } - + .mq-hat-prefix { display: block; text-align: center; @@ -388,6 +388,18 @@ vertical-align: middle; margin-bottom: 1px; } + + &.rcl { + td:nth-child(1) { + text-align: right; + } + td:nth-child(2) { + text-align: center; + } + td:nth-child(3) { + text-align: left; + } + } } td { From 409a180538f1d1106afa88f11df2515caec5a694 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Tue, 11 Jul 2017 11:46:08 +1000 Subject: [PATCH 07/13] Note in comment why '*' was added to the regex. --- src/commands/math/commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index 738fd5123..c98418e96 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -863,7 +863,7 @@ LatexCmds.begin = P(MathCommand, function(_, super_) { var string = Parser.string; var regex = Parser.regex; return string('{') - .then(regex(/^[a-z*]+/i)) + .then(regex(/^[a-z*]+/i)) // LaTex uses * as a convention for alternative forms of a command, usually 'without automatic numbering' .skip(string('}')) .then(function (env) { return (Environments[env] ? From d149b5cf8f756d4e63d400ceadde6dcd30527375 Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Fri, 28 Jul 2017 16:43:22 +1000 Subject: [PATCH 08/13] Add tests for align* environment. --- src/commands/math/commands.js | 2 +- test/unit/latex.test.js | 3 +++ test/unit/typing.test.js | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js index c98418e96..261e064db 100644 --- a/src/commands/math/commands.js +++ b/src/commands/math/commands.js @@ -1002,7 +1002,7 @@ Environments.matrix = P(Environment, function(_, super_) { blocks.push(items[i]); } else { addCell(); - if (items[i] === this.delimiters.row) row+=1; + if (items[i] === self.delimiters.row) row+=1; } } addCell(); diff --git a/test/unit/latex.test.js b/test/unit/latex.test.js index afa3a32a5..c6b2a5dcb 100644 --- a/test/unit/latex.test.js +++ b/test/unit/latex.test.js @@ -147,6 +147,9 @@ suite('latex', function() { // Adds missing cells assertParsesLatex('\\begin{Vmatrix}x&y\\\\1\\end{Vmatrix}', '\\begin{Vmatrix}x&y\\\\1&\\end{Vmatrix}'); assertParsesLatex('\\begin{Vmatrix}x\\\\x&y\\\\x\\end{Vmatrix}', '\\begin{Vmatrix}x&\\\\x&y\\\\x&\\end{Vmatrix}'); + + // align* + assertParsesLatex('\\begin{align*}y&=ax^2+bx+c\\\\y&=mx+c\\end{align*}'); }); suite('public API', function() { diff --git a/test/unit/typing.test.js b/test/unit/typing.test.js index 7e8a40035..0eb9424d8 100644 --- a/test/unit/typing.test.js +++ b/test/unit/typing.test.js @@ -1175,6 +1175,26 @@ suite('typing with auto-replaces', function() { assertLatex('\\begin{matrix}1&2&3\\\\4&5a&6\\\\7&8&9\\end{matrix}b'); }); + test('renders align* environment', function() { + // y = mx + c + mq.write('\\begin{align*}&=\\end{align*}'); + mq.keystroke('Left Left').typedText('y') + .keystroke('Right').typedText('mx+c'); + assertLatex('\\begin{align*}y&=mx+c\\end{align*}'); + + // Shift-Enter adds a new line. + // y = mx + c + // y - c = mx + // y - c / m = x + mq.keystroke('Shift-Enter Left') + .typedText('y-c').keystroke('Right').typedText('mx') + .keystroke('Shift-Enter Left') + .typedText('/y-c').keystroke('Down').typedText('m') + .keystroke('Right Right').typedText('x'); + + assertLatex('\\begin{align*}y&=mx+c\\\\y-c&=mx\\\\\\frac{y-c}{m}&=x\\end{align*}') + }); + test('delete key removes empty matrix row/column', function() { mq.write('\\begin{matrix}a&&b\\\\&c&d\\\\&e&f\\end{matrix}'); From 74793e23da978bc14327cfb51f712423356c565d Mon Sep 17 00:00:00 2001 From: Alex Finkel Date: Wed, 25 Oct 2017 16:22:25 +1100 Subject: [PATCH 09/13] Add visual test for align*. --- test/visual.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/visual.html b/test/visual.html index 42a968452..2e8bda05c 100644 --- a/test/visual.html +++ b/test/visual.html @@ -289,6 +289,25 @@

Text mode

Mutiple consecutive spaces in the middle of a text mode block should not collapse into one space: \text{three spaces}

+

Matrix align* environments

+

align* environments should render with each row's = sign aligned, the LHS aligned right, and the RHS aligned left + + \begin{align*} + y &= mx+c \\ + y-c &= mx \\ + \frac{y-c}{m} &= x + \end{align*} + +

align* environments can be edited. New row: shift+enter.

+ + \begin{align*} + y &= mx+c \\ + y-c &= mx \\ + \frac{y-c}{m} &= x + \end{align*} + +

+
') - .attr(mqBlockId, block.id) .appendTo(newRow); } @@ -1227,8 +1243,7 @@ Environments.matrix = P(Environment, function(_, super_) { newCells.push(block); rows[i].splice(column, 0, block); - block.jQ = $('') - .attr(mqBlockId, block.id); + block.jQ = $(''); } // Add cell elements in correct positions @@ -1307,6 +1322,33 @@ Environments.Vmatrix = P(Matrix, function(_, super_) { }; }); +// An environment for aligning equations that translates well enough to amsmath align*. +// This is inheriting from Matrix for implementation purposes only, it is a more restrictive design +// allowing only three columns, the middle of which is just the '=' sign, and is represented slightly +// differently in latex. Nevertheless, we want it to render as a table so it's convenient to extend Matrix. +Environments['align*'] = P(Matrix, function (_, super_) { + _.environment = 'align*'; + _.extraTableClasses = 'rcl'; + _.createBlocks = function() { + this.blocks = [ + MatrixCell(0, this), + MatrixCell(0, this), + ]; + } + _.htmlColumnSeparator = '=