wysihtml.commands.addTableCells = { exec: function(composer, command, value) { if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) { // switches start and end if start is bigger than end (reverse selection) var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end); if (value == 'before' || value == 'above') { wysihtml.dom.table.addCells(tableSelect.start, value); } else if (value == 'after' || value == 'below') { wysihtml.dom.table.addCells(tableSelect.end, value); } setTimeout(function() { composer.tableSelection.select(tableSelect.start, tableSelect.end); },0); } }, state: function(composer, command) { return false; } }; wysihtml.commands.createTable = { exec: function(composer, command, value) { var col, row, html; if (value && value.cols && value.rows && parseInt(value.cols, 10) > 0 && parseInt(value.rows, 10) > 0) { if (value.tableStyle) { html = ''; } else { html = '
'; } html += ''; for (row = 0; row < value.rows; row ++) { html += ''; for (col = 0; col < value.cols; col ++) { html += ''; } html += ''; } html += '

'; composer.commands.exec('insertHTML', html); } }, state: function(composer, command) { return false; } }; wysihtml.commands.deleteTableCells = { exec: function(composer, command, value) { if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) { var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end), idx = wysihtml.dom.table.indexOf(tableSelect.start), selCell, table = composer.tableSelection.table; wysihtml.dom.table.removeCells(tableSelect.start, value); setTimeout(function() { // move selection to next or previous if not present selCell = wysihtml.dom.table.findCell(table, idx); if (!selCell) { if (value == 'row') { selCell = wysihtml.dom.table.findCell(table, { 'row': idx.row - 1, 'col': idx.col }); } if (value == 'column') { selCell = wysihtml.dom.table.findCell(table, { 'row': idx.row, 'col': idx.col - 1 }); } } if (selCell) { composer.tableSelection.select(selCell, selCell); } }, 0); } }, state: function(composer, command) { return false; } }; wysihtml.commands.mergeTableCells = { exec: function(composer, command) { if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) { if (this.state(composer, command)) { wysihtml.dom.table.unmergeCell(composer.tableSelection.start); } else { wysihtml.dom.table.mergeCellsBetween(composer.tableSelection.start, composer.tableSelection.end); } } }, state: function(composer, command) { if (composer.tableSelection) { var start = composer.tableSelection.start, end = composer.tableSelection.end; if (start && end && start == end && (( wysihtml.dom.getAttribute(start, 'colspan') && parseInt(wysihtml.dom.getAttribute(start, 'colspan'), 10) > 1 ) || ( wysihtml.dom.getAttribute(start, 'rowspan') && parseInt(wysihtml.dom.getAttribute(start, 'rowspan'), 10) > 1 )) ) { return [start]; } } return false; } }; (function() { var api = wysihtml.dom; var MapCell = function(cell) { this.el = cell; this.isColspan= false; this.isRowspan= false; this.firstCol= true; this.lastCol= true; this.firstRow= true; this.lastRow= true; this.isReal= true; this.spanCollection= []; this.modified = false; }; var TableModifyerByCell = function (cell, table) { if (cell) { this.cell = cell; this.table = api.getParentElement(cell, { query: "table" }); } else if (table) { this.table = table; this.cell = this.table.querySelectorAll('th, td')[0]; } }; function queryInList(list, query) { var ret = [], q; for (var e = 0, len = list.length; e < len; e++) { q = list[e].querySelectorAll(query); if (q) { for(var i = q.length; i--; ret.unshift(q[i])); } } return ret; } function removeElement(el) { el.parentNode.removeChild(el); } function insertAfter(referenceNode, newNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } function nextNode(node, tag) { var element = node.nextSibling; while (element.nodeType !=1) { element = element.nextSibling; if (!tag || tag == element.tagName.toLowerCase()) { return element; } } return null; } TableModifyerByCell.prototype = { addSpannedCellToMap: function(cell, map, r, c, cspan, rspan) { var spanCollect = [], rmax = r + ((rspan) ? parseInt(rspan, 10) - 1 : 0), cmax = c + ((cspan) ? parseInt(cspan, 10) - 1 : 0); for (var rr = r; rr <= rmax; rr++) { if (typeof map[rr] == "undefined") { map[rr] = []; } for (var cc = c; cc <= cmax; cc++) { map[rr][cc] = new MapCell(cell); map[rr][cc].isColspan = (cspan && parseInt(cspan, 10) > 1); map[rr][cc].isRowspan = (rspan && parseInt(rspan, 10) > 1); map[rr][cc].firstCol = cc == c; map[rr][cc].lastCol = cc == cmax; map[rr][cc].firstRow = rr == r; map[rr][cc].lastRow = rr == rmax; map[rr][cc].isReal = cc == c && rr == r; map[rr][cc].spanCollection = spanCollect; spanCollect.push(map[rr][cc]); } } }, setCellAsModified: function(cell) { cell.modified = true; if (cell.spanCollection.length > 0) { for (var s = 0, smax = cell.spanCollection.length; s < smax; s++) { cell.spanCollection[s].modified = true; } } }, setTableMap: function() { var map = []; var tableRows = this.getTableRows(), ridx, row, cells, cidx, cell, c, cspan, rspan; for (ridx = 0; ridx < tableRows.length; ridx++) { row = tableRows[ridx]; cells = this.getRowCells(row); c = 0; if (typeof map[ridx] == "undefined") { map[ridx] = []; } for (cidx = 0; cidx < cells.length; cidx++) { cell = cells[cidx]; // If cell allready set means it is set by col or rowspan, // so increase cols index until free col is found while (typeof map[ridx][c] != "undefined") { c++; } cspan = api.getAttribute(cell, 'colspan'); rspan = api.getAttribute(cell, 'rowspan'); if (cspan || rspan) { this.addSpannedCellToMap(cell, map, ridx, c, cspan, rspan); c = c + ((cspan) ? parseInt(cspan, 10) : 1); } else { map[ridx][c] = new MapCell(cell); c++; } } } this.map = map; return map; }, getRowCells: function(row) { var inlineTables = this.table.querySelectorAll('table'), inlineCells = (inlineTables) ? queryInList(inlineTables, 'th, td') : [], allCells = row.querySelectorAll('th, td'), tableCells = (inlineCells.length > 0) ? wysihtml.lang.array(allCells).without(inlineCells) : allCells; return tableCells; }, getTableRows: function() { var inlineTables = this.table.querySelectorAll('table'), inlineRows = (inlineTables) ? queryInList(inlineTables, 'tr') : [], allRows = this.table.querySelectorAll('tr'), tableRows = (inlineRows.length > 0) ? wysihtml.lang.array(allRows).without(inlineRows) : allRows; return tableRows; }, getMapIndex: function(cell) { var r_length = this.map.length, c_length = (this.map && this.map[0]) ? this.map[0].length : 0; for (var r_idx = 0;r_idx < r_length; r_idx++) { for (var c_idx = 0;c_idx < c_length; c_idx++) { if (this.map[r_idx][c_idx].el === cell) { return {'row': r_idx, 'col': c_idx}; } } } return false; }, getElementAtIndex: function(idx) { this.setTableMap(); if (this.map[idx.row] && this.map[idx.row][idx.col] && this.map[idx.row][idx.col].el) { return this.map[idx.row][idx.col].el; } return null; }, getMapElsTo: function(to_cell) { var els = []; this.setTableMap(); this.idx_start = this.getMapIndex(this.cell); this.idx_end = this.getMapIndex(to_cell); // switch indexes if start is bigger than end if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) { var temp_idx = this.idx_start; this.idx_start = this.idx_end; this.idx_end = temp_idx; } if (this.idx_start.col > this.idx_end.col) { var temp_cidx = this.idx_start.col; this.idx_start.col = this.idx_end.col; this.idx_end.col = temp_cidx; } if (this.idx_start != null && this.idx_end != null) { for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) { for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) { els.push(this.map[row][col].el); } } } return els; }, orderSelectionEnds: function(secondcell) { this.setTableMap(); this.idx_start = this.getMapIndex(this.cell); this.idx_end = this.getMapIndex(secondcell); // switch indexes if start is bigger than end if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) { var temp_idx = this.idx_start; this.idx_start = this.idx_end; this.idx_end = temp_idx; } if (this.idx_start.col > this.idx_end.col) { var temp_cidx = this.idx_start.col; this.idx_start.col = this.idx_end.col; this.idx_end.col = temp_cidx; } return { "start": this.map[this.idx_start.row][this.idx_start.col].el, "end": this.map[this.idx_end.row][this.idx_end.col].el }; }, createCells: function(tag, nr, attrs) { var doc = this.table.ownerDocument, frag = doc.createDocumentFragment(), cell; for (var i = 0; i < nr; i++) { cell = doc.createElement(tag); if (attrs) { for (var attr in attrs) { if (attrs.hasOwnProperty(attr)) { cell.setAttribute(attr, attrs[attr]); } } } // add non breaking space cell.appendChild(document.createTextNode("\u00a0")); frag.appendChild(cell); } return frag; }, // Returns next real cell (not part of spanned cell unless first) on row if selected index is not real. I no real cells -1 will be returned correctColIndexForUnreals: function(col, row) { var r = this.map[row], corrIdx = -1; for (var i = 0, max = col; i < col; i++) { if (r[i].isReal){ corrIdx++; } } return corrIdx; }, getLastNewCellOnRow: function(row, rowLimit) { var cells = this.getRowCells(row), cell, idx; for (var cidx = 0, cmax = cells.length; cidx < cmax; cidx++) { cell = cells[cidx]; idx = this.getMapIndex(cell); if (idx === false || (typeof rowLimit != "undefined" && idx.row != rowLimit)) { return cell; } } return null; }, removeEmptyTable: function() { var cells = this.table.querySelectorAll('td, th'); if (!cells || cells.length == 0) { removeElement(this.table); return true; } else { return false; } }, // Splits merged cell on row to unique cells splitRowToCells: function(cell) { if (cell.isColspan) { var colspan = parseInt(api.getAttribute(cell.el, 'colspan') || 1, 10), cType = cell.el.tagName.toLowerCase(); if (colspan > 1) { var newCells = this.createCells(cType, colspan -1); insertAfter(cell.el, newCells); } cell.el.removeAttribute('colspan'); } }, getRealRowEl: function(force, idx) { var r = null, c = null; idx = idx || this.idx; for (var cidx = 0, cmax = this.map[idx.row].length; cidx < cmax; cidx++) { c = this.map[idx.row][cidx]; if (c.isReal) { r = api.getParentElement(c.el, { query: "tr" }); if (r) { return r; } } } if (r === null && force) { r = api.getParentElement(this.map[idx.row][idx.col].el, { query: "tr" }) || null; } return r; }, injectRowAt: function(row, col, colspan, cType, c) { var r = this.getRealRowEl(false, {'row': row, 'col': col}), new_cells = this.createCells(cType, colspan); if (r) { var n_cidx = this.correctColIndexForUnreals(col, row); if (n_cidx >= 0) { insertAfter(this.getRowCells(r)[n_cidx], new_cells); } else { r.insertBefore(new_cells, r.firstChild); } } else { var rr = this.table.ownerDocument.createElement('tr'); rr.appendChild(new_cells); insertAfter(api.getParentElement(c.el, { query: "tr" }), rr); } }, canMerge: function(to) { this.to = to; this.setTableMap(); this.idx_start = this.getMapIndex(this.cell); this.idx_end = this.getMapIndex(this.to); // switch indexes if start is bigger than end if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) { var temp_idx = this.idx_start; this.idx_start = this.idx_end; this.idx_end = temp_idx; } if (this.idx_start.col > this.idx_end.col) { var temp_cidx = this.idx_start.col; this.idx_start.col = this.idx_end.col; this.idx_end.col = temp_cidx; } for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) { for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) { if (this.map[row][col].isColspan || this.map[row][col].isRowspan) { return false; } } } return true; }, decreaseCellSpan: function(cell, span) { var nr = parseInt(api.getAttribute(cell.el, span), 10) - 1; if (nr >= 1) { cell.el.setAttribute(span, nr); } else { cell.el.removeAttribute(span); if (span == 'colspan') { cell.isColspan = false; } if (span == 'rowspan') { cell.isRowspan = false; } cell.firstCol = true; cell.lastCol = true; cell.firstRow = true; cell.lastRow = true; cell.isReal = true; } }, removeSurplusLines: function() { var row, cell, ridx, rmax, cidx, cmax, allRowspan; this.setTableMap(); if (this.map) { ridx = 0; rmax = this.map.length; for (;ridx < rmax; ridx++) { row = this.map[ridx]; allRowspan = true; cidx = 0; cmax = row.length; for (; cidx < cmax; cidx++) { cell = row[cidx]; if (!(api.getAttribute(cell.el, "rowspan") && parseInt(api.getAttribute(cell.el, "rowspan"), 10) > 1 && cell.firstRow !== true)) { allRowspan = false; break; } } if (allRowspan) { cidx = 0; for (; cidx < cmax; cidx++) { this.decreaseCellSpan(row[cidx], 'rowspan'); } } } // remove rows without cells var tableRows = this.getTableRows(); ridx = 0; rmax = tableRows.length; for (;ridx < rmax; ridx++) { row = tableRows[ridx]; if (row.childNodes.length == 0 && (/^\s*$/.test(row.textContent || row.innerText))) { removeElement(row); } } } }, fillMissingCells: function() { var r_max = 0, c_max = 0, prevcell = null; this.setTableMap(); if (this.map) { // find maximal dimensions of broken table r_max = this.map.length; for (var ridx = 0; ridx < r_max; ridx++) { if (this.map[ridx].length > c_max) { c_max = this.map[ridx].length; } } for (var row = 0; row < r_max; row++) { for (var col = 0; col < c_max; col++) { if (this.map[row] && !this.map[row][col]) { if (col > 0) { this.map[row][col] = new MapCell(this.createCells('td', 1)); prevcell = this.map[row][col-1]; if (prevcell && prevcell.el && prevcell.el.parent) { // if parent does not exist element is removed from dom insertAfter(this.map[row][col-1].el, this.map[row][col].el); } } } } } } }, rectify: function() { if (!this.removeEmptyTable()) { this.removeSurplusLines(); this.fillMissingCells(); return true; } else { return false; } }, unmerge: function() { if (this.rectify()) { this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (this.idx) { var thisCell = this.map[this.idx.row][this.idx.col], colspan = (api.getAttribute(thisCell.el, "colspan")) ? parseInt(api.getAttribute(thisCell.el, "colspan"), 10) : 1, cType = thisCell.el.tagName.toLowerCase(); if (thisCell.isRowspan) { var rowspan = parseInt(api.getAttribute(thisCell.el, "rowspan"), 10); if (rowspan > 1) { for (var nr = 1, maxr = rowspan - 1; nr <= maxr; nr++){ this.injectRowAt(this.idx.row + nr, this.idx.col, colspan, cType, thisCell); } } thisCell.el.removeAttribute('rowspan'); } this.splitRowToCells(thisCell); } } }, // merges cells from start cell (defined in creating obj) to "to" cell merge: function(to) { if (this.rectify()) { if (this.canMerge(to)) { var rowspan = this.idx_end.row - this.idx_start.row + 1, colspan = this.idx_end.col - this.idx_start.col + 1; for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) { for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) { if (row == this.idx_start.row && col == this.idx_start.col) { if (rowspan > 1) { this.map[row][col].el.setAttribute('rowspan', rowspan); } if (colspan > 1) { this.map[row][col].el.setAttribute('colspan', colspan); } } else { // transfer content if (!(/^\s*\s*$/.test(this.map[row][col].el.innerHTML.toLowerCase()))) { this.map[this.idx_start.row][this.idx_start.col].el.innerHTML += ' ' + this.map[row][col].el.innerHTML; } removeElement(this.map[row][col].el); } } } this.rectify(); } else { if (window.console) { console.log('Do not know how to merge allready merged cells.'); } } } }, // Decreases rowspan of a cell if it is done on first cell of rowspan row (real cell) // Cell is moved to next row (if it is real) collapseCellToNextRow: function(cell) { var cellIdx = this.getMapIndex(cell.el), newRowIdx = cellIdx.row + 1, newIdx = {'row': newRowIdx, 'col': cellIdx.col}; if (newRowIdx < this.map.length) { var row = this.getRealRowEl(false, newIdx); if (row !== null) { var n_cidx = this.correctColIndexForUnreals(newIdx.col, newIdx.row); if (n_cidx >= 0) { insertAfter(this.getRowCells(row)[n_cidx], cell.el); } else { var lastCell = this.getLastNewCellOnRow(row, newRowIdx); if (lastCell !== null) { insertAfter(lastCell, cell.el); } else { row.insertBefore(cell.el, row.firstChild); } } if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) { cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1); } else { cell.el.removeAttribute('rowspan'); } } } }, // Removes a cell when removing a row // If is rowspan cell then decreases the rowspan // and moves cell to next row if needed (is first cell of rowspan) removeRowCell: function(cell) { if (cell.isReal) { if (cell.isRowspan) { this.collapseCellToNextRow(cell); } else { removeElement(cell.el); } } else { if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) { cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1); } else { cell.el.removeAttribute('rowspan'); } } }, getRowElementsByCell: function() { var cells = []; this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (this.idx !== false) { var modRow = this.map[this.idx.row]; for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) { if (modRow[cidx].isReal) { cells.push(modRow[cidx].el); } } } return cells; }, getColumnElementsByCell: function() { var cells = []; this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (this.idx !== false) { for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) { if (this.map[ridx][this.idx.col] && this.map[ridx][this.idx.col].isReal) { cells.push(this.map[ridx][this.idx.col].el); } } } return cells; }, // Removes the row of selected cell removeRow: function() { var oldRow = api.getParentElement(this.cell, { query: "tr" }); if (oldRow) { this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (this.idx !== false) { var modRow = this.map[this.idx.row]; for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) { if (!modRow[cidx].modified) { this.setCellAsModified(modRow[cidx]); this.removeRowCell(modRow[cidx]); } } } removeElement(oldRow); } }, removeColCell: function(cell) { if (cell.isColspan) { if (parseInt(api.getAttribute(cell.el, 'colspan'), 10) > 2) { cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) - 1); } else { cell.el.removeAttribute('colspan'); } } else if (cell.isReal) { removeElement(cell.el); } }, removeColumn: function() { this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (this.idx !== false) { for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) { if (!this.map[ridx][this.idx.col].modified) { this.setCellAsModified(this.map[ridx][this.idx.col]); this.removeColCell(this.map[ridx][this.idx.col]); } } } }, // removes row or column by selected cell element remove: function(what) { if (this.rectify()) { switch (what) { case 'row': this.removeRow(); break; case 'column': this.removeColumn(); break; } this.rectify(); } }, addRow: function(where) { var doc = this.table.ownerDocument; this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (where == "below" && api.getAttribute(this.cell, 'rowspan')) { this.idx.row = this.idx.row + parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1; } if (this.idx !== false) { var modRow = this.map[this.idx.row], newRow = doc.createElement('tr'); for (var ridx = 0, rmax = modRow.length; ridx < rmax; ridx++) { if (!modRow[ridx].modified) { this.setCellAsModified(modRow[ridx]); this.addRowCell(modRow[ridx], newRow, where); } } switch (where) { case 'below': insertAfter(this.getRealRowEl(true), newRow); break; case 'above': var cr = api.getParentElement(this.map[this.idx.row][this.idx.col].el, { query: "tr" }); if (cr) { cr.parentNode.insertBefore(newRow, cr); } break; } } }, addRowCell: function(cell, row, where) { var colSpanAttr = (cell.isColspan) ? {"colspan" : api.getAttribute(cell.el, 'colspan')} : null; if (cell.isReal) { if (where != 'above' && cell.isRowspan) { cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el,'rowspan'), 10) + 1); } else { row.appendChild(this.createCells('td', 1, colSpanAttr)); } } else { if (where != 'above' && cell.isRowspan && cell.lastRow) { row.appendChild(this.createCells('td', 1, colSpanAttr)); } else if (c.isRowspan) { cell.el.attr('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) + 1); } } }, add: function(where) { if (this.rectify()) { if (where == 'below' || where == 'above') { this.addRow(where); } if (where == 'before' || where == 'after') { this.addColumn(where); } } }, addColCell: function (cell, ridx, where) { var doAdd, cType = cell.el.tagName.toLowerCase(); // defines add cell vs expand cell conditions // true means add switch (where) { case "before": doAdd = (!cell.isColspan || cell.firstCol); break; case "after": doAdd = (!cell.isColspan || cell.lastCol || (cell.isColspan && cell.el == this.cell)); break; } if (doAdd){ // adds a cell before or after current cell element switch (where) { case "before": cell.el.parentNode.insertBefore(this.createCells(cType, 1), cell.el); break; case "after": insertAfter(cell.el, this.createCells(cType, 1)); break; } // handles if cell has rowspan if (cell.isRowspan) { this.handleCellAddWithRowspan(cell, ridx+1, where); } } else { // expands cell cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) + 1); } }, addColumn: function(where) { var row, modCell; this.setTableMap(); this.idx = this.getMapIndex(this.cell); if (where == "after" && api.getAttribute(this.cell, 'colspan')) { this.idx.col = this.idx.col + parseInt(api.getAttribute(this.cell, 'colspan'), 10) - 1; } if (this.idx !== false) { for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++ ) { row = this.map[ridx]; if (row[this.idx.col]) { modCell = row[this.idx.col]; if (!modCell.modified) { this.setCellAsModified(modCell); this.addColCell(modCell, ridx , where); } } } } }, handleCellAddWithRowspan: function (cell, ridx, where) { var addRowsNr = parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1, crow = api.getParentElement(cell.el, { query: "tr" }), cType = cell.el.tagName.toLowerCase(), cidx, temp_r_cells, doc = this.table.ownerDocument, nrow; for (var i = 0; i < addRowsNr; i++) { cidx = this.correctColIndexForUnreals(this.idx.col, (ridx + i)); crow = nextNode(crow, 'tr'); if (crow) { if (cidx > 0) { switch (where) { case "before": temp_r_cells = this.getRowCells(crow); if (cidx > 0 && this.map[ridx + i][this.idx.col].el != temp_r_cells[cidx] && cidx == temp_r_cells.length - 1) { insertAfter(temp_r_cells[cidx], this.createCells(cType, 1)); } else { temp_r_cells[cidx].parentNode.insertBefore(this.createCells(cType, 1), temp_r_cells[cidx]); } break; case "after": insertAfter(this.getRowCells(crow)[cidx], this.createCells(cType, 1)); break; } } else { crow.insertBefore(this.createCells(cType, 1), crow.firstChild); } } else { nrow = doc.createElement('tr'); nrow.appendChild(this.createCells(cType, 1)); this.table.appendChild(nrow); } } } }; api.table = { getCellsBetween: function(cell1, cell2) { var c1 = new TableModifyerByCell(cell1); return c1.getMapElsTo(cell2); }, addCells: function(cell, where) { var c = new TableModifyerByCell(cell); c.add(where); }, removeCells: function(cell, what) { var c = new TableModifyerByCell(cell); c.remove(what); }, mergeCellsBetween: function(cell1, cell2) { var c1 = new TableModifyerByCell(cell1); c1.merge(cell2); }, unmergeCell: function(cell) { var c = new TableModifyerByCell(cell); c.unmerge(); }, orderSelectionEnds: function(cell, cell2) { var c = new TableModifyerByCell(cell); return c.orderSelectionEnds(cell2); }, indexOf: function(cell) { var c = new TableModifyerByCell(cell); c.setTableMap(); return c.getMapIndex(cell); }, findCell: function(table, idx) { var c = new TableModifyerByCell(null, table); return c.getElementAtIndex(idx); }, findRowByCell: function(cell) { var c = new TableModifyerByCell(cell); return c.getRowElementsByCell(); }, findColumnByCell: function(cell) { var c = new TableModifyerByCell(cell); return c.getColumnElementsByCell(); }, canMerge: function(cell1, cell2) { var c = new TableModifyerByCell(cell1); return c.canMerge(cell2); } }; })(); (function() { // Keep the old composer.observe function. var oldObserverFunction = wysihtml.views.Composer.prototype.observe; var extendedObserverFunction = function() { oldObserverFunction.call(this); // Bind the table user interaction tracking if (this.config.handleTables) { // If handleTables option is true, table handling functions are bound initTableHandling.call(this); } }; // Table management. // If present enableObjectResizing and enableInlineTableEditing command // should be called with false to prevent native table handlers. var initTableHandling = function() { var hideHandlers = function() { this.win.removeEventListener('load', hideHandlers); this.doc.execCommand('enableObjectResizing', false, 'false'); this.doc.execCommand('enableInlineTableEditing', false, 'false'); }.bind(this), iframeInitiator = (function() { hideHandlers.call(this); this.actions.removeListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator); }).bind(this); if ( this.doc.execCommand && wysihtml.browser.supportsCommand(this.doc, 'enableObjectResizing') && wysihtml.browser.supportsCommand(this.doc, 'enableInlineTableEditing') ) { if (this.sandbox.getIframe) { this.actions.addListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator); } else { this.win.addEventListener('load', hideHandlers); } } this.tableSelection = wysihtml.quirks.tableCellsSelection(this.element, this.parent); }; // Cell selections handling var tableCellsSelection = function(editable, editor) { var init = function() { editable.addEventListener('mousedown', handleMouseDown); return select; }; var handleMouseDown = function(event) { var target = wysihtml.dom.getParentElement(event.target, {query: 'td, th'}, false, editable); if (target) { handleSelectionMousedown(target); } }; var handleSelectionMousedown = function(target) { select.start = target; select.end = target; select.cells = [target]; select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable); if (select.table) { removeCellSelections(); dom.addClass(target, selectionClass); editable.addEventListener('mousemove', handleMouseMove); editable.addEventListener('mouseup', handleMouseUp); editor.fire('tableselectstart').fire('tableselectstart:composer'); } }; // remove all selection classes var removeCellSelections = function() { if (editable) { var selectedCells = editable.querySelectorAll('.' + selectionClass); if (selectedCells.length > 0) { for (var i = 0; i < selectedCells.length; i++) { dom.removeClass(selectedCells[i], selectionClass); } } } }; var addSelections = function(cells) { for (var i = 0; i < cells.length; i++) { dom.addClass(cells[i], selectionClass); } }; var handleMouseMove = function(event) { var curTable = null, cell = dom.getParentElement(event.target, {query: 'td, th'}, false, editable), oldEnd; if (cell && select.table && select.start) { curTable = dom.getParentElement(cell, {query: 'table'}, false, editable); if (curTable && curTable === select.table) { removeCellSelections(); oldEnd = select.end; select.end = cell; select.cells = dom.table.getCellsBetween(select.start, cell); if (select.cells.length > 1) { editor.composer.selection.deselect(); } addSelections(select.cells); if (select.end !== oldEnd) { editor.fire('tableselectchange').fire('tableselectchange:composer'); } } } }; var handleMouseUp = function(event) { editable.removeEventListener('mousemove', handleMouseMove); editable.removeEventListener('mouseup', handleMouseUp); editor.fire('tableselect').fire('tableselect:composer'); setTimeout(function() { bindSideclick(); }, 0); }; var sideClickHandler = function(event) { editable.ownerDocument.removeEventListener('click', sideClickHandler); if (dom.getParentElement(event.target, {query: 'table'}, false, editable) != select.table) { removeCellSelections(); select.table = null; select.start = null; select.end = null; editor.fire('tableunselect').fire('tableunselect:composer'); } }; var bindSideclick = function() { editable.ownerDocument.addEventListener('click', sideClickHandler); }; var selectCells = function(start, end) { select.start = start; select.end = end; select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable); selectedCells = dom.table.getCellsBetween(select.start, select.end); addSelections(selectedCells); bindSideclick(); editor.fire('tableselect').fire('tableselect:composer'); }; var dom = wysihtml.dom, select = { table: null, start: null, end: null, cells: null, select: selectCells }, selectionClass = 'wysiwyg-tmp-selected-cell'; return init(); }; // Bind to wysihtml wysihtml.Editor.prototype.defaults.handleTables = true; wysihtml.quirks.tableCellsSelection = tableCellsSelection; wysihtml.views.Composer.prototype.observe = extendedObserverFunction; })();