Thomas Bruederli
2012-07-12 688d16e88296e2ae96ab9b7fc379eaef31d77933
program/js/tiny_mce/plugins/table/editor_plugin_src.js
@@ -11,6 +11,24 @@
(function(tinymce) {
   var each = tinymce.each;
   // Checks if the selection/caret is at the start of the specified block element
   function isAtStart(rng, par) {
      var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
      rng2.setStartBefore(par);
      rng2.setEnd(rng.endContainer, rng.endOffset);
      elm = doc.createElement('body');
      elm.appendChild(rng2.cloneContents());
      // Check for text characters of other elements that should be treated as content
      return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length == 0;
   };
   function getSpanVal(td, name) {
      return parseInt(td.getAttribute(name) || 1);
   }
   /**
    * Table Grid class.
    */
@@ -38,12 +56,12 @@
         grid = [];
         each(['thead', 'tbody', 'tfoot'], function(part) {
            var rows = dom.select(part + ' tr', table);
            var rows = dom.select('> ' + part + ' tr', table);
            each(rows, function(tr, y) {
               y += startY;
               each(dom.select('td,th', tr), function(td, x) {
               each(dom.select('> td, > th', tr), function(td, x) {
                  var x2, y2, rowspan, colspan;
                  // Skip over existing cells produced by rowspan
@@ -86,12 +104,19 @@
            return row[x];
      };
      function getSpanVal(td, name) {
         return parseInt(td.getAttribute(name) || 1);
      };
      function setSpanVal(td, name, val) {
         if (td) {
            val = parseInt(val);
            if (val === 1)
               td.removeAttribute(name, 1);
            else
               td.setAttribute(name, val, 1);
         }
      }
      function isCellSelected(cell) {
         return dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell;
         return cell && (dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell);
      };
      function getSelectedRows() {
@@ -141,20 +166,21 @@
               // Add something to the inner node
               if (curNode)
                  curNode.innerHTML = tinymce.isIE ? '&nbsp;' : '<br _mce_bogus="1" />';
                  curNode.innerHTML = tinymce.isIE ? '&nbsp;' : '<br data-mce-bogus="1" />';
               return false;
            }
         }, 'childNodes');
         cell = cloneNode(cell, false);
         cell.rowSpan = cell.colSpan = 1;
         setSpanVal(cell, 'rowSpan', 1);
         setSpanVal(cell, 'colSpan', 1);
         if (formatNode) {
            cell.appendChild(formatNode);
         } else {
            if (!tinymce.isIE)
               cell.innerHTML = '<br _mce_bogus="1" />';
               cell.innerHTML = '<br data-mce-bogus="1" />';
         }
         return cell;
@@ -236,7 +262,8 @@
                  rowSpan = getSpanVal(cell, 'rowspan');
                  if (colSpan > 1 || rowSpan > 1) {
                     cell.colSpan = cell.rowSpan = 1;
                     setSpanVal(cell, 'rowSpan', 1);
                     setSpanVal(cell, 'colSpan', 1);
                     // Insert cells right
                     for (i = 0; i < colSpan - 1; i++)
@@ -250,7 +277,7 @@
      };
      function merge(cell, cols, rows) {
         var startX, startY, endX, endY, x, y, startCell, endCell, cell, children;
         var startX, startY, endX, endY, x, y, startCell, endCell, cell, children, count;
         // Use specified cell and cols/rows
         if (cell) {
@@ -260,6 +287,21 @@
            endX = startX + (cols - 1);
            endY = startY + (rows - 1);
         } else {
            startPos = endPos = null;
            // Calculate start/end pos by checking for selected cells in grid works better with context menu
            each(grid, function(row, y) {
               each(row, function(cell, x) {
                  if (isCellSelected(cell)) {
                     if (!startPos) {
                        startPos = {x: x, y: y};
                     }
                     endPos = {x: x, y: y};
                  }
               });
            });
            // Use selection
            startX = startPos.x;
            startY = startPos.y;
@@ -279,23 +321,34 @@
            // Set row/col span to start cell
            startCell = getCell(startX, startY).elm;
            startCell.colSpan = (endX - startX) + 1;
            startCell.rowSpan = (endY - startY) + 1;
            setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
            setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
            // Remove other cells and add it's contents to the start cell
            for (y = startY; y <= endY; y++) {
               for (x = startX; x <= endX; x++) {
                  if (!grid[y] || !grid[y][x])
                     continue;
                  cell = grid[y][x].elm;
                  if (cell != startCell) {
                     // Move children to startCell
                     children = tinymce.grep(cell.childNodes);
                     each(children, function(node, i) {
                        // Jump over last BR element
                        if (node.nodeName != 'BR' || i != children.length - 1)
                           startCell.appendChild(node);
                     each(children, function(node) {
                        startCell.appendChild(node);
                     });
                     // Remove bogus nodes if there is children in the target cell
                     if (children.length) {
                        children = tinymce.grep(startCell.childNodes);
                        count = 0;
                        each(children, function(node) {
                           if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1)
                              startCell.removeChild(node);
                        });
                     }
                     // Remove cell
                     dom.remove(cell);
                  }
@@ -308,7 +361,7 @@
      };
      function insertRow(before) {
         var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell;
         var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
         // Find first/last row
         each(grid, function(row, y) {
@@ -329,30 +382,35 @@
         });
         for (x = 0; x < grid[0].length; x++) {
            // Cell not found could be because of an invalid table structure
            if (!grid[posY][x])
               continue;
            cell = grid[posY][x].elm;
            if (cell != lastCell) {
               if (!before) {
                  rowSpan = getSpanVal(cell, 'rowspan');
                  if (rowSpan > 1) {
                     cell.rowSpan = rowSpan + 1;
                     setSpanVal(cell, 'rowSpan', rowSpan + 1);
                     continue;
                  }
               } else {
                  // Check if cell above can be expanded
                  if (posY > 0 && grid[posY - 1][x]) {
                     otherCell = grid[posY - 1][x].elm;
                     rowSpan = getSpanVal(otherCell, 'rowspan');
                     rowSpan = getSpanVal(otherCell, 'rowSpan');
                     if (rowSpan > 1) {
                        otherCell.rowSpan = rowSpan + 1;
                        setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
                        continue;
                     }
                  }
               }
               // Insert new cell into new row
               newCell = cloneCell(cell)
               newCell.colSpan = cell.colSpan;
               newCell = cloneCell(cell);
               setSpanVal(newCell, 'colSpan', cell.colSpan);
               newRow.appendChild(newCell);
               lastCell = cell;
@@ -386,8 +444,12 @@
         });
         each(grid, function(row, y) {
            var cell = row[posX].elm, rowSpan, colSpan;
            var cell, rowSpan, colSpan;
            if (!row[posX])
               return;
            cell = row[posX].elm;
            if (cell != lastCell) {
               colSpan = getSpanVal(cell, 'colspan');
               rowSpan = getSpanVal(cell, 'rowspan');
@@ -401,7 +463,7 @@
                     fillLeftDown(posX, y, rowSpan - 1, colSpan);
                  }
               } else
                  cell.colSpan++;
                  setSpanVal(cell, 'colSpan', cell.colSpan + 1);
               lastCell = cell;
            }
@@ -418,10 +480,10 @@
                  each(grid, function(row) {
                     var cell = row[x].elm, colSpan;
                     colSpan = getSpanVal(cell, 'colspan');
                     colSpan = getSpanVal(cell, 'colSpan');
                     if (colSpan > 1)
                        cell.colSpan = colSpan - 1;
                        setSpanVal(cell, 'colSpan', colSpan - 1);
                     else
                        dom.remove(cell);
                  });
@@ -444,10 +506,10 @@
            // Move down row spanned cells
            each(tr.cells, function(cell) {
               var rowSpan = getSpanVal(cell, 'rowspan');
               var rowSpan = getSpanVal(cell, 'rowSpan');
               if (rowSpan > 1) {
                  cell.rowSpan = rowSpan - 1;
                  setSpanVal(cell, 'rowSpan', rowSpan - 1);
                  pos = getPos(cell);
                  fillLeftDown(pos.x, pos.y, 1, 1);
               }
@@ -461,12 +523,12 @@
               cell = cell.elm;
               if (cell != lastCell) {
                  rowSpan = getSpanVal(cell, 'rowspan');
                  rowSpan = getSpanVal(cell, 'rowSpan');
                  if (rowSpan <= 1)
                     dom.remove(cell);
                  else
                     cell.rowSpan = rowSpan - 1;
                     setSpanVal(cell, 'rowSpan', rowSpan - 1);
                  lastCell = cell;
               }
@@ -534,7 +596,8 @@
            // Remove col/rowspans
            for (i = 0; i < cellCount; i++) {
               cell = row.cells[i];
               cell.colSpan = cell.rowSpan = 1;
               setSpanVal(cell, 'colSpan', 1);
               setSpanVal(cell, 'rowSpan', 1);
            }
            // Needs more cells
@@ -551,6 +614,9 @@
            else
               dom.insertAfter(row, targetRow);
         });
         // Remove current selection
         dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
      };
      function getPos(target) {
@@ -676,8 +742,10 @@
            // Add new selection
            for (y = startY; y <= maxY; y++) {
               for (x = startX; x <= maxX; x++)
                  dom.addClass(grid[y][x].elm, 'mceSelected');
               for (x = startX; x <= maxX; x++) {
                  if (grid[y][x])
                     dom.addClass(grid[y][x].elm, 'mceSelected');
               }
            }
         }
      };
@@ -702,7 +770,7 @@
   tinymce.create('tinymce.plugins.TablePlugin', {
      init : function(ed, url) {
         var winMan, clipboardRows;
         var winMan, clipboardRows, hasCellSelection = true; // Might be selected cells on reload
         function createTableGrid(node) {
            var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table');
@@ -714,7 +782,11 @@
         function cleanup() {
            // Restore selection possibilities
            ed.getBody().style.webkitUserSelect = '';
            ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
            if (hasCellSelection) {
               ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
               hasCellSelection = false;
            }
         };
         // Register buttons
@@ -740,10 +812,33 @@
            ed.onClick.add(function(ed, e) {
               e = e.target;
               if (e.nodeName === 'TABLE')
               if (e.nodeName === 'TABLE') {
                  ed.selection.select(e);
                  ed.nodeChanged();
               }
            });
         }
         ed.onPreProcess.add(function(ed, args) {
            var nodes, i, node, dom = ed.dom, value;
            nodes = dom.select('table', args.node);
            i = nodes.length;
            while (i--) {
               node = nodes[i];
               dom.setAttrib(node, 'data-mce-style', '');
               if ((value = dom.getAttrib(node, 'width'))) {
                  dom.setStyle(node, 'width', value);
                  dom.setAttrib(node, 'width', '');
               }
               if ((value = dom.getAttrib(node, 'height'))) {
                  dom.setStyle(node, 'height', value);
                  dom.setAttrib(node, 'height', '');
               }
            }
         });
         // Handle node change updates
         ed.onNodeChange.add(function(ed, cm, n) {
@@ -800,15 +895,20 @@
                     }
                     tableGrid.setEndCell(target);
                     hasCellSelection = true;
                  }
                  // Remove current selection
                  sel = ed.selection.getSel();
                  if (sel.removeAllRanges)
                     sel.removeAllRanges();
                  else
                     sel.empty();
                  try {
                     if (sel.removeAllRanges)
                        sel.removeAllRanges();
                     else
                        sel.empty();
                  } catch (ex) {
                     // IE9 might throw errors here
                  }
                  e.preventDefault();
               }
@@ -846,7 +946,7 @@
                           return;
                        }
                     } while (node = (start ? walker.next() : walker.prev()));
                  };
                  }
                  // Try to expand text selection as much as we can only Gecko supports cell selection
                  selectedCells = dom.select('td.mceSelected,th.mceSelected');
@@ -854,6 +954,8 @@
                     rng = dom.createRng();
                     node = selectedCells[0];
                     endNode = selectedCells[selectedCells.length - 1];
                     rng.setStartBefore(node);
                     rng.setEndAfter(node);
                     setPoint(node, 1);
                     walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
@@ -880,6 +982,60 @@
            ed.onKeyUp.add(function(ed, e) {
               cleanup();
            });
            ed.onKeyDown.add(function (ed, e) {
               fixTableCellSelection(ed);
            });
            ed.onMouseDown.add(function (ed, e) {
               if (e.button != 2) {
                  fixTableCellSelection(ed);
               }
            });
            function tableCellSelected(ed, rng, n, currentCell) {
               // The decision of when a table cell is selected is somewhat involved.  The fact that this code is
               // required is actually a pointer to the root cause of this bug. A cell is selected when the start
               // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
               // or the parent of the table (in the case of the selection containing the last cell of a table).
               var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE'),
               tableParent, allOfCellSelected, tableCellSelection;
               if (table)
               tableParent = table.parentNode;
               allOfCellSelected =rng.startContainer.nodeType == TEXT_NODE &&
                  rng.startOffset == 0 &&
                  rng.endOffset == 0 &&
                  currentCell &&
                  (n.nodeName=="TR" || n==tableParent);
               tableCellSelection = (n.nodeName=="TD"||n.nodeName=="TH")&& !currentCell;
               return  allOfCellSelected || tableCellSelection;
               // return false;
            }
            // this nasty hack is here to work around some WebKit selection bugs.
            function fixTableCellSelection(ed) {
               if (!tinymce.isWebKit)
                  return;
               var rng = ed.selection.getRng();
               var n = ed.selection.getNode();
               var currentCell = ed.dom.getParent(rng.startContainer, 'TD,TH');
               if (!tableCellSelected(ed, rng, n, currentCell))
                  return;
                  if (!currentCell) {
                     currentCell=n;
                  }
               // Get the very last node inside the table cell
               var end = currentCell.lastChild;
               while (end.lastChild)
                  end = end.lastChild;
               // Select the entire table cell. Nothing outside of the table cell should be selected.
               rng.setEnd(end, end.nodeValue.length);
               ed.selection.setRng(rng);
            }
            ed.plugins.table.fixTableCellSelection=fixTableCellSelection;
            // Add context menu
            if (ed && ed.plugins.contextmenu) {
@@ -933,60 +1089,228 @@
               });
            }
            // Fixes an issue on Gecko where it's impossible to place the caret behind a table
            // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
            if (!tinymce.isIE) {
               function fixTableCaretPos() {
                  var last;
            // Fix to allow navigating up and down in a table in WebKit browsers.
            if (tinymce.isWebKit) {
               function moveSelection(ed, e) {
                  var VK = tinymce.VK;
                  var key = e.keyCode;
                  // Skip empty text nodes form the end
                  for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
                  function handle(upBool, sourceNode, event) {
                     var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
                     var currentRow = ed.dom.getParent(sourceNode, 'tr');
                     var siblingRow = currentRow[siblingDirection];
                  if (last && last.nodeName == 'TABLE')
                     ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />');
               };
               // Fixes an bug where it's impossible to place the caret before a table in Gecko
               // this fix solves it by detecting when the caret is at the beginning of such a table
               // and then manually moves the caret infront of the table
               if (tinymce.isGecko) {
                  ed.onKeyDown.add(function(ed, e) {
                     var rng, table, dom = ed.dom;
                     // On gecko it's not possible to place the caret before a table
                     if (e.keyCode == 37 || e.keyCode == 38) {
                        rng = ed.selection.getRng();
                        table = dom.getParent(rng.startContainer, 'table');
                        if (table && ed.getBody().firstChild == table) {
                           if (isAtStart(rng, table)) {
                              rng = dom.createRng();
                              rng.setStartBefore(table);
                              rng.setEndBefore(table);
                              ed.selection.setRng(rng);
                              e.preventDefault();
                     if (siblingRow) {
                        moveCursorToRow(ed, sourceNode, siblingRow, upBool);
                        tinymce.dom.Event.cancel(event);
                        return true;
                     } else {
                        var tableNode = ed.dom.getParent(currentRow, 'table');
                        var middleNode = currentRow.parentNode;
                        var parentNodeName = middleNode.nodeName.toLowerCase();
                        if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
                           var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
                           if (targetParent !== null) {
                              return moveToRowInTarget(upBool, targetParent, sourceNode, event);
                           }
                        }
                        return escapeTable(upBool, currentRow, siblingDirection, tableNode, event);
                     }
                  });
                  }
                  function getTargetParent(upBool, topNode, secondNode, nodeName) {
                     var tbodies = ed.dom.select('>' + nodeName, topNode);
                     var position = tbodies.indexOf(secondNode);
                     if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
                        return getFirstHeadOrFoot(upBool, topNode);
                     } else if (position === -1) {
                        var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
                        return tbodies[topOrBottom];
                     } else {
                        return tbodies[position + (upBool ? -1 : 1)];
                     }
                  }
                  function getFirstHeadOrFoot(upBool, parent) {
                     var tagName = upBool ? 'thead' : 'tfoot';
                     var headOrFoot = ed.dom.select('>' + tagName, parent);
                     return headOrFoot.length !== 0 ? headOrFoot[0] : null;
                  }
                  function moveToRowInTarget(upBool, targetParent, sourceNode, event) {
                     var targetRow = getChildForDirection(targetParent, upBool);
                     targetRow && moveCursorToRow(ed, sourceNode, targetRow, upBool);
                     tinymce.dom.Event.cancel(event);
                     return true;
                  }
                  function escapeTable(upBool, currentRow, siblingDirection, table, event) {
                     var tableSibling = table[siblingDirection];
                     if (tableSibling) {
                        moveCursorToStartOfElement(tableSibling);
                        return true;
                     } else {
                        var parentCell = ed.dom.getParent(table, 'td,th');
                        if (parentCell) {
                           return handle(upBool, parentCell, event);
                        } else {
                           var backUpSibling = getChildForDirection(currentRow, !upBool);
                           moveCursorToStartOfElement(backUpSibling);
                           return tinymce.dom.Event.cancel(event);
                        }
                     }
                  }
                  function getChildForDirection(parent, up) {
                     var child =  parent && parent[up ? 'lastChild' : 'firstChild'];
                     // BR is not a valid table child to return in this case we return the table cell
                     return child && child.nodeName === 'BR' ? ed.dom.getParent(child, 'td,th') : child;
                  }
                  function moveCursorToStartOfElement(n) {
                     ed.selection.setCursorLocation(n, 0);
                  }
                  function isVerticalMovement() {
                     return key == VK.UP || key == VK.DOWN;
                  }
                  function isInTable(ed) {
                     var node = ed.selection.getNode();
                     var currentRow = ed.dom.getParent(node, 'tr');
                     return currentRow !== null;
                  }
                  function columnIndex(column) {
                     var colIndex = 0;
                     var c = column;
                     while (c.previousSibling) {
                        c = c.previousSibling;
                        colIndex = colIndex + getSpanVal(c, "colspan");
                     }
                     return colIndex;
                  }
                  function findColumn(rowElement, columnIndex) {
                     var c = 0;
                     var r = 0;
                     each(rowElement.children, function(cell, i) {
                        c = c + getSpanVal(cell, "colspan");
                        r = i;
                        if (c > columnIndex)
                           return false;
                     });
                     return r;
                  }
                  function moveCursorToRow(ed, node, row, upBool) {
                     var srcColumnIndex = columnIndex(ed.dom.getParent(node, 'td,th'));
                     var tgtColumnIndex = findColumn(row, srcColumnIndex);
                     var tgtNode = row.childNodes[tgtColumnIndex];
                     var rowCellTarget = getChildForDirection(tgtNode, upBool);
                     moveCursorToStartOfElement(rowCellTarget || tgtNode);
                  }
                  function shouldFixCaret(preBrowserNode) {
                     var newNode = ed.selection.getNode();
                     var newParent = ed.dom.getParent(newNode, 'td,th');
                     var oldParent = ed.dom.getParent(preBrowserNode, 'td,th');
                     return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent)
                  }
                  function checkSameParentTable(nodeOne, NodeTwo) {
                     return ed.dom.getParent(nodeOne, 'TABLE') === ed.dom.getParent(NodeTwo, 'TABLE');
                  }
                  if (isVerticalMovement() && isInTable(ed)) {
                     var preBrowserNode = ed.selection.getNode();
                     setTimeout(function() {
                        if (shouldFixCaret(preBrowserNode)) {
                           handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
                        }
                     }, 0);
                  }
               }
               ed.onKeyUp.add(fixTableCaretPos);
               ed.onSetContent.add(fixTableCaretPos);
               ed.onVisualAid.add(fixTableCaretPos);
               ed.onPreProcess.add(function(ed, o) {
                  var last = o.node.lastChild;
                  if (last && last.childNodes.length == 1 && last.firstChild.nodeName == 'BR')
                     ed.dom.remove(last);
               });
               fixTableCaretPos();
               ed.onKeyDown.add(moveSelection);
            }
            // Fixes an issue on Gecko where it's impossible to place the caret behind a table
            // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
            function fixTableCaretPos() {
               var last;
               // Skip empty text nodes form the end
               for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
               if (last && last.nodeName == 'TABLE') {
                  if (ed.settings.forced_root_block)
                     ed.dom.add(ed.getBody(), ed.settings.forced_root_block, null, tinymce.isIE ? '&nbsp;' : '<br data-mce-bogus="1" />');
                  else
                     ed.dom.add(ed.getBody(), 'br', {'data-mce-bogus': '1'});
               }
            };
            // Fixes an bug where it's impossible to place the caret before a table in Gecko
            // this fix solves it by detecting when the caret is at the beginning of such a table
            // and then manually moves the caret infront of the table
            if (tinymce.isGecko) {
               ed.onKeyDown.add(function(ed, e) {
                  var rng, table, dom = ed.dom;
                  // On gecko it's not possible to place the caret before a table
                  if (e.keyCode == 37 || e.keyCode == 38) {
                     rng = ed.selection.getRng();
                     table = dom.getParent(rng.startContainer, 'table');
                     if (table && ed.getBody().firstChild == table) {
                        if (isAtStart(rng, table)) {
                           rng = dom.createRng();
                           rng.setStartBefore(table);
                           rng.setEndBefore(table);
                           ed.selection.setRng(rng);
                           e.preventDefault();
                        }
                     }
                  }
               });
            }
            ed.onKeyUp.add(fixTableCaretPos);
            ed.onSetContent.add(fixTableCaretPos);
            ed.onVisualAid.add(fixTableCaretPos);
            ed.onPreProcess.add(function(ed, o) {
               var last = o.node.lastChild;
               if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 && (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) && last.previousSibling && last.previousSibling.nodeName == "TABLE") {
                  ed.dom.remove(last);
               }
            });
            /**
             * Fixes bug in Gecko where shift-enter in table cell does not place caret on new line
             */
            if (tinymce.isGecko) {
               ed.onKeyDown.add(function(ed, e) {
                  if (e.keyCode === tinymce.VK.ENTER && e.shiftKey) {
                     var node = ed.selection.getRng().startContainer;
                     var tableCell = dom.getParent(node, 'td,th');
                     if (tableCell) {
                        var zeroSizedNbsp = ed.getDoc().createTextNode("\uFEFF");
                        dom.insertAfter(zeroSizedNbsp, node);
                     }
                  }
               });
            }
            fixTableCaretPos();
            ed.startContent = ed.getContent({format : 'raw'});
         });
         // Register action commands
@@ -1122,4 +1446,4 @@
   // Register plugin
   tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);
})(tinymce);
})(tinymce);