| | |
| | | (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;
|
| | | };
|
| | |
|
| | | /**
|
| | | * Table Grid class.
|
| | | */
|
| | |
| | | selectedCell = getCell(startPos.x, startPos.y);
|
| | | }
|
| | |
|
| | | function cloneNode(node, children) {
|
| | | node = node.cloneNode(children);
|
| | | node.removeAttribute('id');
|
| | |
|
| | | return node;
|
| | | }
|
| | |
|
| | | function buildGrid() {
|
| | | var startY = 0;
|
| | |
|
| | | 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
|
| | |
| | | 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() {
|
| | |
| | |
|
| | | if (node.nodeType == 3) {
|
| | | each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
|
| | | node = node.cloneNode(false);
|
| | | node = cloneNode(node, false);
|
| | |
|
| | | if (!formatNode)
|
| | | formatNode = curNode = node;
|
| | |
| | |
|
| | | // Add something to the inner node
|
| | | if (curNode)
|
| | | curNode.innerHTML = tinymce.isIE ? ' ' : '<br _mce_bogus="1" />';
|
| | | curNode.innerHTML = tinymce.isIE ? ' ' : '<br data-mce-bogus="1" />';
|
| | |
|
| | | return false;
|
| | | }
|
| | | }, 'childNodes');
|
| | |
|
| | | cell = cell.cloneNode(false);
|
| | | cell.rowSpan = cell.colSpan = 1;
|
| | | cell = cloneNode(cell, false);
|
| | | 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;
|
| | |
| | | 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++)
|
| | |
| | | };
|
| | |
|
| | | 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) {
|
| | |
| | |
|
| | | // 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);
|
| | | }
|
| | |
| | | };
|
| | |
|
| | | 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) {
|
| | |
| | | if (isCellSelected(cell)) {
|
| | | cell = cell.elm;
|
| | | rowElm = cell.parentNode;
|
| | | newRow = rowElm.cloneNode(false);
|
| | | newRow = cloneNode(rowElm, false);
|
| | | posY = y;
|
| | |
|
| | | if (before)
|
| | |
| | | });
|
| | |
|
| | | 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;
|
| | |
| | | });
|
| | |
|
| | | 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');
|
| | |
| | | fillLeftDown(posX, y, rowSpan - 1, colSpan);
|
| | | }
|
| | | } else
|
| | | cell.colSpan++;
|
| | | setSpanVal(cell, 'colSpan', cell.colSpan + 1);
|
| | |
|
| | | lastCell = cell;
|
| | | }
|
| | |
| | | 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);
|
| | | });
|
| | |
| | |
|
| | | // 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);
|
| | | }
|
| | |
| | | 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;
|
| | | }
|
| | |
| | | var rows = getSelectedRows();
|
| | |
|
| | | each(rows, function(row, i) {
|
| | | rows[i] = row.cloneNode(true);
|
| | | rows[i] = cloneNode(row, true);
|
| | | });
|
| | |
|
| | | return rows;
|
| | |
| | | // 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
|
| | |
| | |
|
| | | // 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');
|
| | | }
|
| | | }
|
| | | }
|
| | | };
|
| | |
| | | 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) {
|
| | |
| | | // 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();
|
| | | }
|
| | |
| | | ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
|
| | | var sm, se = ed.selection, el = se.getNode() || ed.getBody();
|
| | |
|
| | | if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th')) {
|
| | | if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th') || ed.dom.select('td.mceSelected,th.mceSelected').length) {
|
| | | m.removeAll();
|
| | |
|
| | | if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
|