/** * ResizeBars.js * * Released under LGPL License. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /** * This class handles table column and row resizing by adding divs over the columns and rows of the table. * These divs are then manipulated using mouse events to resize the underlying table. * * @class tinymce.tableplugin.ResizeBars * @private */ define("tinymce/tableplugin/ResizeBars", [ "tinymce/util/Tools", "tinymce/util/VK" ], function(Tools, VK) { var hoverTable; return function(editor) { var RESIZE_BAR_CLASS = 'mce-resize-bar', RESIZE_BAR_ROW_CLASS = 'mce-resize-bar-row', RESIZE_BAR_ROW_CURSOR_STYLE = 'row-resize', RESIZE_BAR_ROW_DATA_ATTRIBUTE = 'data-row', RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE = 'data-initial-top', RESIZE_BAR_COL_CLASS = 'mce-resize-bar-col', RESIZE_BAR_COL_CURSOR_STYLE = 'col-resize', RESIZE_BAR_COL_DATA_ATTRIBUTE = 'data-col', RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE = 'data-initial-left', RESIZE_BAR_THICKNESS = 4, RESIZE_MINIMUM_WIDTH = 10, RESIZE_MINIMUM_HEIGHT = 10, RESIZE_BAR_DRAGGING_CLASS = 'mce-resize-bar-dragging'; var percentageBasedSizeRegex = new RegExp(/(\d+(\.\d+)?%)/), pixelBasedSizeRegex = new RegExp(/px|em/); var delayDrop, dragging, blockerElement, dragBar, lastX, lastY; // Get the absolute position's top edge. function getTopEdge(index, row) { return { index: index, y: editor.dom.getPos(row).y }; } // Get the absolute position's bottom edge. function getBottomEdge(index, row) { return { index: index, y: editor.dom.getPos(row).y + row.offsetHeight }; } // Get the absolute position's left edge. function getLeftEdge(index, cell) { return { index: index, x: editor.dom.getPos(cell).x }; } // Get the absolute position's right edge. function getRightEdge(index, cell) { return { index: index, x: editor.dom.getPos(cell).x + cell.offsetWidth }; } function isRtl() { var dir = editor.getBody().dir; return dir === 'rtl'; } function isInline() { return editor.inline; } function getBody() { return isInline ? editor.getBody().ownerDocument.body : editor.getBody(); } function getInnerEdge(index, cell) { return isRtl() ? getRightEdge(index, cell) : getLeftEdge(index, cell); } function getOuterEdge(index, cell) { return isRtl() ? getLeftEdge(index, cell) : getRightEdge(index, cell); } function getPercentageWidthFallback(element, table) { return getComputedStyleSize(element, 'width') / getComputedStyleSize(table, 'width') * 100; } function getComputedStyleSize(element, property) { var widthString = editor.dom.getStyle(element, property, true); var width = parseInt(widthString, 10); return width; } function getCurrentTablePercentWidth(table) { var tableWidth = getComputedStyleSize(table, 'width'); var tableParentWidth = getComputedStyleSize(table.parentElement, 'width'); return tableWidth / tableParentWidth * 100; } function getCellPercentDelta(table, delta) { var tableWidth = getComputedStyleSize(table, 'width'); return delta / tableWidth * 100; } function getTablePercentDelta(table, delta) { var tableParentWidth = getComputedStyleSize(table.parentElement, 'width'); return delta / tableParentWidth * 100; } // Find the left/right (ltr/rtl) or top side locations of the cells to measure. // This is the location of the borders we need to draw over. function findPositions(getInner, getOuter, thingsToMeasure) { var tablePositions = []; // Skip the first item in the array = no left (LTR), right (RTL) or top bars for (var i = 1; i < thingsToMeasure.length; i++) { // Get the element from the details var item = thingsToMeasure[i].element; // We need to zero index this again tablePositions.push(getInner(i - 1, item)); } var lastTableLineToMake = thingsToMeasure[thingsToMeasure.length - 1]; tablePositions.push(getOuter(thingsToMeasure.length - 1, lastTableLineToMake.element)); return tablePositions; } // Clear the bars. function clearBars() { var bars = editor.dom.select('.' + RESIZE_BAR_CLASS, getBody()); Tools.each(bars, function(bar) { editor.dom.remove(bar); }); } // Refresh the bars. function refreshBars(tableElement) { clearBars(); drawBars(tableElement); } // Generates a resize bar object for the editor to add. function generateBar(classToAdd, cursor, left, top, height, width, indexAttr, index) { var bar = { 'data-mce-bogus': 'all', 'class': RESIZE_BAR_CLASS + ' ' + classToAdd, 'unselectable': 'on', 'data-mce-resize': false, style: 'cursor: ' + cursor + '; ' + 'margin: 0; ' + 'padding: 0; ' + 'position: absolute; ' + 'left: ' + left + 'px; ' + 'top: ' + top + 'px; ' + 'height: ' + height + 'px; ' + 'width: ' + width + 'px; ' }; bar[indexAttr] = index; return bar; } // Draw the row bars over the row borders. function drawRows(rowPositions, tableWidth, tablePosition) { Tools.each(rowPositions, function(rowPosition) { var left = tablePosition.x, top = rowPosition.y - RESIZE_BAR_THICKNESS / 2, height = RESIZE_BAR_THICKNESS, width = tableWidth; editor.dom.add(getBody(), 'div', generateBar(RESIZE_BAR_ROW_CLASS, RESIZE_BAR_ROW_CURSOR_STYLE, left, top, height, width, RESIZE_BAR_ROW_DATA_ATTRIBUTE, rowPosition.index)); }); } // Draw the column bars over the column borders. function drawCols(cellPositions, tableHeight, tablePosition) { Tools.each(cellPositions, function(cellPosition) { var left = cellPosition.x - RESIZE_BAR_THICKNESS / 2, top = tablePosition.y, height = tableHeight, width = RESIZE_BAR_THICKNESS; editor.dom.add(getBody(), 'div', generateBar(RESIZE_BAR_COL_CLASS, RESIZE_BAR_COL_CURSOR_STYLE, left, top, height, width, RESIZE_BAR_COL_DATA_ATTRIBUTE, cellPosition.index)); }); } // Get a matrix of the cells in each row and the rows in the table. function getTableDetails(table) { return Tools.map(table.rows, function(row) { var cells = Tools.map(row.cells, function(cell) { var rowspan = cell.hasAttribute('rowspan') ? parseInt(cell.getAttribute('rowspan'), 10) : 1; var colspan = cell.hasAttribute('colspan') ? parseInt(cell.getAttribute('colspan'), 10) : 1; return { element: cell, rowspan: rowspan, colspan: colspan }; }); return { element: row, cells: cells }; }); } // Get a grid model of the table. function getTableGrid(tableDetails) { function key(rowIndex, colIndex) { return rowIndex + ',' + colIndex; } function getAt(rowIndex, colIndex) { return access[key(rowIndex, colIndex)]; } function getAllCells() { var allCells = []; Tools.each(rows, function(row) { allCells = allCells.concat(row.cells); }); return allCells; } function getAllRows() { return rows; } var access = {}; var rows = []; var maxRows = 0; var maxCols = 0; Tools.each(tableDetails, function(row, rowIndex) { var currentRow = []; Tools.each(row.cells, function(cell) { var start = 0; while (access[key(rowIndex, start)] !== undefined) { start++; } var current = { element: cell.element, colspan: cell.colspan, rowspan: cell.rowspan, rowIndex: rowIndex, colIndex: start }; for (var i = 0; i < cell.colspan; i++) { for (var j = 0; j < cell.rowspan; j++) { var cr = rowIndex + j; var cc = start + i; access[key(cr, cc)] = current; maxRows = Math.max(maxRows, cr + 1); maxCols = Math.max(maxCols, cc + 1); } } currentRow.push(current); }); rows.push({ element: row.element, cells: currentRow }); }); return { grid: { maxRows: maxRows, maxCols: maxCols }, getAt: getAt, getAllCells: getAllCells, getAllRows: getAllRows }; } function range(start, end) { var r = []; for (var i = start; i < end; i++) { r.push(i); } return r; } // Attempt to get a representative single block for this column. // If we can't find a single block, all blocks in this row/column are spanned // and we'll need to fallback to getting the first cell in the row/column. function decide(getBlock, isSingle, getFallback) { var inBlock = getBlock(); var singleInBlock; for (var i = 0; i < inBlock.length; i++) { if (isSingle(inBlock[i])) { singleInBlock = inBlock[i]; } } return singleInBlock ? singleInBlock : getFallback(); } // Attempt to get representative blocks for the width of each column. function getColumnBlocks(tableGrid) { var cols = range(0, tableGrid.grid.maxCols); var rows = range(0, tableGrid.grid.maxRows); return Tools.map(cols, function(col) { function getBlock() { var details = []; for (var i = 0; i < rows.length; i++) { var detail = tableGrid.getAt(i, col); if (detail && detail.colIndex === col) { details.push(detail); } } return details; } function isSingle(detail) { return detail.colspan === 1; } function getFallback() { var item; for (var i = 0; i < rows.length; i++) { item = tableGrid.getAt(i, col); if (item) { return item; } } return null; } return decide(getBlock, isSingle, getFallback); }); } // Attempt to get representative blocks for the height of each row. function getRowBlocks(tableGrid) { var cols = range(0, tableGrid.grid.maxCols); var rows = range(0, tableGrid.grid.maxRows); return Tools.map(rows, function(row) { function getBlock() { var details = []; for (var i = 0; i < cols.length; i++) { var detail = tableGrid.getAt(row, i); if (detail && detail.rowIndex === row) { details.push(detail); } } return details; } function isSingle(detail) { return detail.rowspan === 1; } function getFallback() { return tableGrid.getAt(row, 0); } return decide(getBlock, isSingle, getFallback); }); } // Draw resize bars over the left/right (ltr/rtl) or top side locations of the cells to measure. // This is the location of the borders we need to draw over. function drawBars(table) { var tableDetails = getTableDetails(table); var tableGrid = getTableGrid(tableDetails); var rows = getRowBlocks(tableGrid); var cols = getColumnBlocks(tableGrid); var tablePosition = editor.dom.getPos(table); var rowPositions = rows.length > 0 ? findPositions(getTopEdge, getBottomEdge, rows) : []; var colPositions = cols.length > 0 ? findPositions(getInnerEdge, getOuterEdge, cols) : []; drawRows(rowPositions, table.offsetWidth, tablePosition); drawCols(colPositions, table.offsetHeight, tablePosition); } // Attempt to deduce the width/height of a column/row that has more than one cell spanned. function deduceSize(deducables, index, isPercentageBased, table) { if (index < 0 || index >= deducables.length - 1) { return ""; } var current = deducables[index]; if (current) { current = { value: current, delta: 0 }; } else { var reversedUpToIndex = deducables.slice(0, index).reverse(); for (var i = 0; i < reversedUpToIndex.length; i++) { if (reversedUpToIndex[i]) { current = { value: reversedUpToIndex[i], delta: i + 1 }; } } } var next = deducables[index + 1]; if (next) { next = { value: next, delta: 1 }; } else { var rest = deducables.slice(index + 1); for (var j = 0; j < rest.length; j++) { if (rest[j]) { next = { value: rest[j], delta: j + 1 }; } } } var extras = next.delta - current.delta; var pixelWidth = Math.abs(next.value - current.value) / extras; return isPercentageBased ? pixelWidth / getComputedStyleSize(table, 'width') * 100 : pixelWidth; } function getStyleOrAttrib(element, property) { var sizeString = editor.dom.getStyle(element, property); if (!sizeString) { sizeString = editor.dom.getAttrib(element, property); } if (!sizeString) { sizeString = editor.dom.getStyle(element, property, true); } return sizeString; } function getWidth(element, isPercentageBased, table) { var widthString = getStyleOrAttrib(element, 'width'); var widthNumber = parseInt(widthString, 10); var getWidthFallback = isPercentageBased ? getPercentageWidthFallback(element, table) : getComputedStyleSize(element, 'width'); // If this is percentage based table, but this cell isn't percentage based. // Or if this is a pixel based table, but this cell isn't pixel based. if (isPercentageBased && !isPercentageBasedSize(widthString) || !isPercentageBased && !isPixelBasedSize(widthString)) { // set the widthnumber to 0 widthNumber = 0; } return !isNaN(widthNumber) && widthNumber > 0 ? widthNumber : getWidthFallback; } // Attempt to get the css width from column representative cells. function getWidths(tableGrid, isPercentageBased, table) { var cols = getColumnBlocks(tableGrid); var backups = Tools.map(cols, function(col) { return getInnerEdge(col.colIndex, col.element).x; }); var widths = []; for (var i = 0; i < cols.length; i++) { var span = cols[i].element.hasAttribute('colspan') ? parseInt(cols[i].element.getAttribute('colspan'), 10) : 1; // Deduce if the column has colspan of more than 1 var width = span > 1 ? deduceSize(backups, i) : getWidth(cols[i].element, isPercentageBased, table); // If everything's failed and we still don't have a width width = width ? width : RESIZE_MINIMUM_WIDTH; widths.push(width); } return widths; } // Attempt to get the pixel height from a cell. function getPixelHeight(element) { var heightString = getStyleOrAttrib(element, 'height'); var heightNumber = parseInt(heightString, 10); if (isPercentageBasedSize(heightString)) { heightNumber = 0; } return !isNaN(heightNumber) && heightNumber > 0 ? heightNumber : getComputedStyleSize(element, 'height'); } // Attempt to get the css height from row representative cells. function getPixelHeights(tableGrid) { var rows = getRowBlocks(tableGrid); var backups = Tools.map(rows, function(row) { return getTopEdge(row.rowIndex, row.element).y; }); var heights = []; for (var i = 0; i < rows.length; i++) { var span = rows[i].element.hasAttribute('rowspan') ? parseInt(rows[i].element.getAttribute('rowspan'), 10) : 1; var height = span > 1 ? deduceSize(backups, i) : getPixelHeight(rows[i].element); height = height ? height : RESIZE_MINIMUM_HEIGHT; heights.push(height); } return heights; } // Determine how much each column's css width will need to change. // Sizes = result = pixels widths OR percentage based widths function determineDeltas(sizes, column, step, min, isPercentageBased) { var result = sizes.slice(0); function generateZeros(array) { return Tools.map(array, function() { return 0; }); } function onOneColumn() { var deltas; if (isPercentageBased) { // If we have one column in a percent based table, that column should be 100% of the width of the table. deltas = [100 - result[0]]; } else { var newNext = Math.max(min, result[0] + step); deltas = [newNext - result[0]]; } return deltas; } function onLeftOrMiddle(index, next) { var startZeros = generateZeros(result.slice(0, index)); var endZeros = generateZeros(result.slice(next + 1)); var deltas; if (step >= 0) { var newNext = Math.max(min, result[next] - step); deltas = startZeros.concat([step, newNext - result[next]]).concat(endZeros); } else { var newThis = Math.max(min, result[index] + step); var diffx = result[index] - newThis; deltas = startZeros.concat([newThis - result[index], diffx]).concat(endZeros); } return deltas; } function onRight(previous, index) { var startZeros = generateZeros(result.slice(0, index)); var deltas; if (step >= 0) { deltas = startZeros.concat([step]); } else { var size = Math.max(min, result[index] + step); deltas = startZeros.concat([size - result[index]]); } return deltas; } var deltas; if (sizes.length === 0) { // No Columns deltas = []; } else if (sizes.length === 1) { // One Column deltas = onOneColumn(); } else if (column === 0) { // Left Column deltas = onLeftOrMiddle(0, 1); } else if (column > 0 && column < sizes.length - 1) { // Middle Column deltas = onLeftOrMiddle(column, column + 1); } else if (column === sizes.length - 1) { // Right Column deltas = onRight(column - 1, column); } else { deltas = []; } return deltas; } function total(start, end, measures) { var r = 0; for (var i = start; i < end; i++) { r += measures[i]; } return r; } // Combine cell's css widths to determine widths of colspan'd cells. function recalculateWidths(tableGrid, widths) { var allCells = tableGrid.getAllCells(); return Tools.map(allCells, function(cell) { var width = total(cell.colIndex, cell.colIndex + cell.colspan, widths); return { element: cell.element, width: width, colspan: cell.colspan }; }); } // Combine cell's css heights to determine heights of rowspan'd cells. function recalculateCellHeights(tableGrid, heights) { var allCells = tableGrid.getAllCells(); return Tools.map(allCells, function(cell) { var height = total(cell.rowIndex, cell.rowIndex + cell.rowspan, heights); return { element: cell.element, height: height, rowspan: cell.rowspan }; }); } // Calculate row heights. function recalculateRowHeights(tableGrid, heights) { var allRows = tableGrid.getAllRows(); return Tools.map(allRows, function(row, i) { return { element: row.element, height: heights[i] }; }); } function isPercentageBasedSize(size) { return percentageBasedSizeRegex.test(size); } function isPixelBasedSize(size) { return pixelBasedSizeRegex.test(size); } // Adjust the width of the column of table at index, with delta. function adjustWidth(table, delta, index) { var tableDetails = getTableDetails(table); var tableGrid = getTableGrid(tableDetails); function setSizes(newSizes, styleExtension) { Tools.each(newSizes, function(cell) { editor.dom.setStyle(cell.element, 'width', cell.width + styleExtension); editor.dom.setAttrib(cell.element, 'width', null); }); } function getNewTablePercentWidth() { return index < tableGrid.grid.maxCols - 1 ? getCurrentTablePercentWidth(table) : getCurrentTablePercentWidth(table) + getTablePercentDelta(table, delta); } function getNewTablePixelWidth() { return index < tableGrid.grid.maxCols - 1 ? getComputedStyleSize(table, 'width') : getComputedStyleSize(table, 'width') + delta; } function setTableSize(newTableWidth, styleExtension, isPercentBased) { if (index == tableGrid.grid.maxCols - 1 || !isPercentBased) { editor.dom.setStyle(table, 'width', newTableWidth + styleExtension); editor.dom.setAttrib(table, 'width', null); } } var percentageBased = isPercentageBasedSize(table.width) || isPercentageBasedSize(table.style.width); var widths = getWidths(tableGrid, percentageBased, table); var step = percentageBased ? getCellPercentDelta(table, delta) : delta; // TODO: change the min for percentage maybe? var deltas = determineDeltas(widths, index, step, RESIZE_MINIMUM_WIDTH, percentageBased, table); var newWidths = []; for (var i = 0; i < deltas.length; i++) { newWidths.push(deltas[i] + widths[i]); } var newSizes = recalculateWidths(tableGrid, newWidths); var styleExtension = percentageBased ? '%' : 'px'; var newTableWidth = percentageBased ? getNewTablePercentWidth() : getNewTablePixelWidth(); editor.undoManager.transact(function() { setSizes(newSizes, styleExtension); setTableSize(newTableWidth, styleExtension, percentageBased); }); } // Adjust the height of the row of table at index, with delta. function adjustHeight(table, delta, index) { var tableDetails = getTableDetails(table); var tableGrid = getTableGrid(tableDetails); var heights = getPixelHeights(tableGrid); var newHeights = [], newTotalHeight = 0; for (var i = 0; i < heights.length; i++) { newHeights.push(i === index ? delta + heights[i] : heights[i]); newTotalHeight += newTotalHeight[i]; } var newCellSizes = recalculateCellHeights(tableGrid, newHeights); var newRowSizes = recalculateRowHeights(tableGrid, newHeights); editor.undoManager.transact(function() { Tools.each(newRowSizes, function(row) { editor.dom.setStyle(row.element, 'height', row.height + 'px'); editor.dom.setAttrib(row.element, 'height', null); }); Tools.each(newCellSizes, function(cell) { editor.dom.setStyle(cell.element, 'height', cell.height + 'px'); editor.dom.setAttrib(cell.element, 'height', null); }); editor.dom.setStyle(table, 'height', newTotalHeight + 'px'); editor.dom.setAttrib(table, 'height', null); }); } function scheduleDelayedDropEvent() { delayDrop = setTimeout(function() { drop(); }, 200); } function cancelDelayedDropEvent() { clearTimeout(delayDrop); } function getBlockerElement() { var blocker = document.createElement('div'); blocker.setAttribute('style', 'margin: 0; ' + 'padding: 0; ' + 'position: fixed; ' + 'left: 0px; ' + 'top: 0px; ' + 'height: 100%; ' + 'width: 100%;'); blocker.setAttribute('data-mce-bogus', 'all'); return blocker; } function bindBlockerEvents(blocker, dragHandler) { editor.dom.bind(blocker, 'mouseup', function() { drop(); }); editor.dom.bind(blocker, 'mousemove', function(e) { cancelDelayedDropEvent(); if (dragging) { dragHandler(e); } }); editor.dom.bind(blocker, 'mouseout', function() { scheduleDelayedDropEvent(); }); } function drop() { editor.dom.remove(blockerElement); if (dragging) { editor.dom.removeClass(dragBar, RESIZE_BAR_DRAGGING_CLASS); dragging = false; var index, delta; if (isCol(dragBar)) { var initialLeft = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE), 10); var newLeft = editor.dom.getPos(dragBar).x; index = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_COL_DATA_ATTRIBUTE), 10); delta = isRtl() ? initialLeft - newLeft : newLeft - initialLeft; adjustWidth(hoverTable, delta, index); } else if (isRow(dragBar)) { var initialTop = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE), 10); var newTop = editor.dom.getPos(dragBar).y; index = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_ROW_DATA_ATTRIBUTE), 10); delta = newTop - initialTop; adjustHeight(hoverTable, delta, index); } refreshBars(hoverTable); editor.nodeChanged(); } } function setupBaseDrag(bar, dragHandler) { blockerElement = blockerElement ? blockerElement : getBlockerElement(); dragging = true; editor.dom.addClass(bar, RESIZE_BAR_DRAGGING_CLASS); dragBar = bar; bindBlockerEvents(blockerElement, dragHandler); editor.dom.add(getBody(), blockerElement); } function isCol(target) { return editor.dom.hasClass(target, RESIZE_BAR_COL_CLASS); } function isRow(target) { return editor.dom.hasClass(target, RESIZE_BAR_ROW_CLASS); } function colDragHandler(event) { lastX = lastX !== undefined ? lastX : event.clientX; // we need a firstX var deltaX = event.clientX - lastX; lastX = event.clientX; var oldLeft = editor.dom.getPos(dragBar).x; editor.dom.setStyle(dragBar, 'left', oldLeft + deltaX + 'px'); } function rowDragHandler(event) { lastY = lastY !== undefined ? lastY : event.clientY; var deltaY = event.clientY - lastY; lastY = event.clientY; var oldTop = editor.dom.getPos(dragBar).y; editor.dom.setStyle(dragBar, 'top', oldTop + deltaY + 'px'); } function setupColDrag(bar) { lastX = undefined; setupBaseDrag(bar, colDragHandler); } function setupRowDrag(bar) { lastY = undefined; setupBaseDrag(bar, rowDragHandler); } function mouseDownHandler(e) { var target = e.target, body = editor.getBody(); // Since this code is working on global events we need to work on a global hoverTable state // and make sure that the state is correct according to the events fired if (!editor.$.contains(body, hoverTable) && hoverTable !== body) { return; } if (isCol(target)) { e.preventDefault(); var initialLeft = editor.dom.getPos(target).x; editor.dom.setAttrib(target, RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE, initialLeft); setupColDrag(target); } else if (isRow(target)) { e.preventDefault(); var initialTop = editor.dom.getPos(target).y; editor.dom.setAttrib(target, RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE, initialTop); setupRowDrag(target); } else { clearBars(); } } editor.on('init', function() { // Needs to be like this for inline mode, editor.on does not bind to elements in the document body otherwise editor.dom.bind(getBody(), 'mousedown', mouseDownHandler); }); // If we're updating the table width via the old mechanic, we need to update the constituent cells' widths/heights too. editor.on('ObjectResized', function(e) { var table = e.target; if (table.nodeName === 'TABLE') { var newCellSizes = []; Tools.each(table.rows, function(row) { Tools.each(row.cells, function(cell) { var width = editor.dom.getStyle(cell, 'width', true); newCellSizes.push({ cell: cell, width: width }); }); }); Tools.each(newCellSizes, function(newCellSize) { editor.dom.setStyle(newCellSize.cell, 'width', newCellSize.width); editor.dom.setAttrib(newCellSize.cell, 'width', null); }); } }); editor.on('mouseover', function(e) { if (!dragging) { var tableElement = editor.dom.getParent(e.target, 'table'); if (e.target.nodeName === 'TABLE' || tableElement) { hoverTable = tableElement; refreshBars(tableElement); } } }); // Prevents the user from moving the caret inside the resize bars on Chrome // Only does it on arrow keys since clearBars might be an epxensive operation // since it's querying the DOM editor.on('keydown', function(e) { switch (e.keyCode) { case VK.LEFT: case VK.RIGHT: case VK.UP: case VK.DOWN: clearBars(); break; } }); editor.on('remove', function() { clearBars(); editor.dom.unbind(getBody(), 'mousedown', mouseDownHandler); }); return { adjustWidth: adjustWidth, adjustHeight: adjustHeight, clearBars: clearBars, drawBars: drawBars, determineDeltas: determineDeltas, getTableGrid: getTableGrid, getTableDetails: getTableDetails, getWidths: getWidths, getPixelHeights: getPixelHeights, isPercentageBasedSize: isPercentageBasedSize, isPixelBasedSize: isPixelBasedSize, recalculateWidths: recalculateWidths, recalculateCellHeights: recalculateCellHeights, recalculateRowHeights: recalculateRowHeights }; }; });