(function(tinymce) {\r
var each = tinymce.each;\r
\r
+ // Checks if the selection/caret is at the start of the specified block element\r
+ function isAtStart(rng, par) {\r
+ var doc = par.ownerDocument, rng2 = doc.createRange(), elm;\r
+\r
+ rng2.setStartBefore(par);\r
+ rng2.setEnd(rng.endContainer, rng.endOffset);\r
+\r
+ elm = doc.createElement('body');\r
+ elm.appendChild(rng2.cloneContents());\r
+\r
+ // Check for text characters of other elements that should be treated as content\r
+ return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length == 0;\r
+ };\r
+\r
/**\r
* Table Grid class.\r
*/\r
grid = [];\r
\r
each(['thead', 'tbody', 'tfoot'], function(part) {\r
- var rows = dom.select(part + ' tr', table);\r
+ var rows = dom.select('> ' + part + ' tr', table);\r
\r
each(rows, function(tr, y) {\r
y += startY;\r
\r
- each(dom.select('td,th', tr), function(td, x) {\r
+ each(dom.select('> td, > th', tr), function(td, x) {\r
var x2, y2, rowspan, colspan;\r
\r
// Skip over existing cells produced by rowspan\r
return parseInt(td.getAttribute(name) || 1);\r
};\r
\r
+ function setSpanVal(td, name, val) {\r
+ if (td) {\r
+ val = parseInt(val);\r
+\r
+ if (val === 1)\r
+ td.removeAttribute(name, 1);\r
+ else\r
+ td.setAttribute(name, val, 1);\r
+ }\r
+ }\r
+\r
function isCellSelected(cell) {\r
- return dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell;\r
+ return cell && (dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell);\r
};\r
\r
function getSelectedRows() {\r
\r
// Add something to the inner node\r
if (curNode)\r
- curNode.innerHTML = tinymce.isIE ? ' ' : '<br _mce_bogus="1" />';\r
+ curNode.innerHTML = tinymce.isIE ? ' ' : '<br data-mce-bogus="1" />';\r
\r
return false;\r
}\r
}, 'childNodes');\r
\r
cell = cloneNode(cell, false);\r
- cell.rowSpan = cell.colSpan = 1;\r
+ setSpanVal(cell, 'rowSpan', 1);\r
+ setSpanVal(cell, 'colSpan', 1);\r
\r
if (formatNode) {\r
cell.appendChild(formatNode);\r
} else {\r
if (!tinymce.isIE)\r
- cell.innerHTML = '<br _mce_bogus="1" />';\r
+ cell.innerHTML = '<br data-mce-bogus="1" />';\r
}\r
\r
return cell;\r
rowSpan = getSpanVal(cell, 'rowspan');\r
\r
if (colSpan > 1 || rowSpan > 1) {\r
- cell.colSpan = cell.rowSpan = 1;\r
+ setSpanVal(cell, 'rowSpan', 1);\r
+ setSpanVal(cell, 'colSpan', 1);\r
\r
// Insert cells right\r
for (i = 0; i < colSpan - 1; i++)\r
};\r
\r
function merge(cell, cols, rows) {\r
- var startX, startY, endX, endY, x, y, startCell, endCell, cell, children;\r
+ var startX, startY, endX, endY, x, y, startCell, endCell, cell, children, count;\r
\r
// Use specified cell and cols/rows\r
if (cell) {\r
\r
// Set row/col span to start cell\r
startCell = getCell(startX, startY).elm;\r
- startCell.colSpan = (endX - startX) + 1;\r
- startCell.rowSpan = (endY - startY) + 1;\r
+ setSpanVal(startCell, 'colSpan', (endX - startX) + 1);\r
+ setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);\r
\r
// Remove other cells and add it's contents to the start cell\r
for (y = startY; y <= endY; y++) {\r
for (x = startX; x <= endX; x++) {\r
+ if (!grid[y] || !grid[y][x])\r
+ continue;\r
+\r
cell = grid[y][x].elm;\r
\r
if (cell != startCell) {\r
// Move children to startCell\r
children = tinymce.grep(cell.childNodes);\r
- each(children, function(node, i) {\r
- // Jump over last BR element\r
- if (node.nodeName != 'BR' || i != children.length - 1)\r
- startCell.appendChild(node);\r
+ each(children, function(node) {\r
+ startCell.appendChild(node);\r
});\r
\r
+ // Remove bogus nodes if there is children in the target cell\r
+ if (children.length) {\r
+ children = tinymce.grep(startCell.childNodes);\r
+ count = 0;\r
+ each(children, function(node) {\r
+ if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1)\r
+ startCell.removeChild(node);\r
+ });\r
+ }\r
+ \r
// Remove cell\r
dom.remove(cell);\r
}\r
};\r
\r
function insertRow(before) {\r
- var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell;\r
+ var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;\r
\r
// Find first/last row\r
each(grid, function(row, y) {\r
});\r
\r
for (x = 0; x < grid[0].length; x++) {\r
+ // Cell not found could be because of an invalid table structure\r
+ if (!grid[posY][x])\r
+ continue;\r
+\r
cell = grid[posY][x].elm;\r
\r
if (cell != lastCell) {\r
if (!before) {\r
rowSpan = getSpanVal(cell, 'rowspan');\r
if (rowSpan > 1) {\r
- cell.rowSpan = rowSpan + 1;\r
+ setSpanVal(cell, 'rowSpan', rowSpan + 1);\r
continue;\r
}\r
} else {\r
// Check if cell above can be expanded\r
if (posY > 0 && grid[posY - 1][x]) {\r
otherCell = grid[posY - 1][x].elm;\r
- rowSpan = getSpanVal(otherCell, 'rowspan');\r
+ rowSpan = getSpanVal(otherCell, 'rowSpan');\r
if (rowSpan > 1) {\r
- otherCell.rowSpan = rowSpan + 1;\r
+ setSpanVal(otherCell, 'rowSpan', rowSpan + 1);\r
continue;\r
}\r
}\r
}\r
\r
// Insert new cell into new row\r
- newCell = cloneCell(cell)\r
- newCell.colSpan = cell.colSpan;\r
+ newCell = cloneCell(cell);\r
+ setSpanVal(newCell, 'colSpan', cell.colSpan);\r
+\r
newRow.appendChild(newCell);\r
\r
lastCell = cell;\r
});\r
\r
each(grid, function(row, y) {\r
- var cell = row[posX].elm, rowSpan, colSpan;\r
+ var cell, rowSpan, colSpan;\r
\r
+ if (!row[posX])\r
+ return;\r
+\r
+ cell = row[posX].elm;\r
if (cell != lastCell) {\r
colSpan = getSpanVal(cell, 'colspan');\r
rowSpan = getSpanVal(cell, 'rowspan');\r
fillLeftDown(posX, y, rowSpan - 1, colSpan);\r
}\r
} else\r
- cell.colSpan++;\r
+ setSpanVal(cell, 'colSpan', cell.colSpan + 1);\r
\r
lastCell = cell;\r
}\r
each(grid, function(row) {\r
var cell = row[x].elm, colSpan;\r
\r
- colSpan = getSpanVal(cell, 'colspan');\r
+ colSpan = getSpanVal(cell, 'colSpan');\r
\r
if (colSpan > 1)\r
- cell.colSpan = colSpan - 1;\r
+ setSpanVal(cell, 'colSpan', colSpan - 1);\r
else\r
dom.remove(cell);\r
});\r
\r
// Move down row spanned cells\r
each(tr.cells, function(cell) {\r
- var rowSpan = getSpanVal(cell, 'rowspan');\r
+ var rowSpan = getSpanVal(cell, 'rowSpan');\r
\r
if (rowSpan > 1) {\r
- cell.rowSpan = rowSpan - 1;\r
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);\r
pos = getPos(cell);\r
fillLeftDown(pos.x, pos.y, 1, 1);\r
}\r
cell = cell.elm;\r
\r
if (cell != lastCell) {\r
- rowSpan = getSpanVal(cell, 'rowspan');\r
+ rowSpan = getSpanVal(cell, 'rowSpan');\r
\r
if (rowSpan <= 1)\r
dom.remove(cell);\r
else\r
- cell.rowSpan = rowSpan - 1;\r
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);\r
\r
lastCell = cell;\r
}\r
// Remove col/rowspans\r
for (i = 0; i < cellCount; i++) {\r
cell = row.cells[i];\r
- cell.colSpan = cell.rowSpan = 1;\r
+ setSpanVal(cell, 'colSpan', 1);\r
+ setSpanVal(cell, 'rowSpan', 1);\r
}\r
\r
// Needs more cells\r
\r
// Add new selection\r
for (y = startY; y <= maxY; y++) {\r
- for (x = startX; x <= maxX; x++)\r
- dom.addClass(grid[y][x].elm, 'mceSelected');\r
+ for (x = startX; x <= maxX; x++) {\r
+ if (grid[y][x])\r
+ dom.addClass(grid[y][x].elm, 'mceSelected');\r
+ }\r
}\r
}\r
};\r
\r
tinymce.create('tinymce.plugins.TablePlugin', {\r
init : function(ed, url) {\r
- var winMan, clipboardRows;\r
+ var winMan, clipboardRows, hasCellSelection = true; // Might be selected cells on reload\r
\r
function createTableGrid(node) {\r
var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table');\r
function cleanup() {\r
// Restore selection possibilities\r
ed.getBody().style.webkitUserSelect = '';\r
- ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');\r
+\r
+ if (hasCellSelection) {\r
+ ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');\r
+ hasCellSelection = false;\r
+ }\r
};\r
\r
// Register buttons\r
ed.onClick.add(function(ed, e) {\r
e = e.target;\r
\r
- if (e.nodeName === 'TABLE')\r
+ if (e.nodeName === 'TABLE') {\r
ed.selection.select(e);\r
+ ed.nodeChanged();\r
+ }\r
});\r
}\r
\r
+ ed.onPreProcess.add(function(ed, args) {\r
+ var nodes, i, node, dom = ed.dom, value;\r
+\r
+ nodes = dom.select('table', args.node);\r
+ i = nodes.length;\r
+ while (i--) {\r
+ node = nodes[i];\r
+ dom.setAttrib(node, 'data-mce-style', '');\r
+\r
+ if ((value = dom.getAttrib(node, 'width'))) {\r
+ dom.setStyle(node, 'width', value);\r
+ dom.setAttrib(node, 'width', '');\r
+ }\r
+\r
+ if ((value = dom.getAttrib(node, 'height'))) {\r
+ dom.setStyle(node, 'height', value);\r
+ dom.setAttrib(node, 'height', '');\r
+ }\r
+ }\r
+ });\r
+\r
// Handle node change updates\r
ed.onNodeChange.add(function(ed, cm, n) {\r
var p;\r
}\r
\r
tableGrid.setEndCell(target);\r
+ hasCellSelection = true;\r
}\r
\r
// Remove current selection\r
sel = ed.selection.getSel();\r
\r
- if (sel.removeAllRanges)\r
- sel.removeAllRanges();\r
- else\r
- sel.empty();\r
+ try {\r
+ if (sel.removeAllRanges)\r
+ sel.removeAllRanges();\r
+ else\r
+ sel.empty();\r
+ } catch (ex) {\r
+ // IE9 might throw errors here\r
+ }\r
\r
e.preventDefault();\r
}\r
rng = dom.createRng();\r
node = selectedCells[0];\r
endNode = selectedCells[selectedCells.length - 1];\r
+ rng.setStart(node);
+ rng.setEnd(node);
\r
setPoint(node, 1);\r
walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table'));\r