| 1 | CKEDITOR.on 'instanceCreated', ({editor}) -> editor.on 'pluginsLoaded', -> |
|---|
| 2 | {TRISTATE_OFF: ENABLED, TRISTATE_DISABLED: DISABLED} = CKEDITOR |
|---|
| 3 | RedrawSubMenuName = null |
|---|
| 4 | OffsetsByLevel = [] |
|---|
| 5 | |
|---|
| 6 | row = editor.getMenuItem('tablerow') |
|---|
| 7 | rowItems = row.getItems() |
|---|
| 8 | row.getItems = -> |
|---|
| 9 | sel = editor.getSelection() |
|---|
| 10 | $.extend { |
|---|
| 11 | tablerow_moveBefore: if isFirstRow(sel) then DISABLED else ENABLED |
|---|
| 12 | tablerow_moveAfter: if isLastRow(sel) then DISABLED else ENABLED |
|---|
| 13 | }, rowItems |
|---|
| 14 | |
|---|
| 15 | col = editor.getMenuItem('tablecolumn') |
|---|
| 16 | colItems = col.getItems() |
|---|
| 17 | col.getItems = -> |
|---|
| 18 | sel = editor.getSelection() |
|---|
| 19 | $.extend { |
|---|
| 20 | tablecolumn_moveBefore: if isFirstColumn(sel) then DISABLED else ENABLED |
|---|
| 21 | tablecolumn_moveAfter: if isLastColumn(sel) then DISABLED else ENABLED |
|---|
| 22 | }, colItems |
|---|
| 23 | |
|---|
| 24 | editor.addMenuItems |
|---|
| 25 | tablerow_moveBefore: |
|---|
| 26 | label: 'Move Row Before' |
|---|
| 27 | group: 'tablerow' |
|---|
| 28 | command: 'rowMoveBefore' |
|---|
| 29 | order: 11 |
|---|
| 30 | tablerow_moveAfter: |
|---|
| 31 | label: 'Move Row After' |
|---|
| 32 | group: 'tablerow' |
|---|
| 33 | command: 'rowMoveAfter' |
|---|
| 34 | order: 12 |
|---|
| 35 | tablecolumn_moveBefore: |
|---|
| 36 | label: 'Move Column Before' |
|---|
| 37 | group: 'tablecolumn' |
|---|
| 38 | command: 'columnMoveBefore' |
|---|
| 39 | order: 11 |
|---|
| 40 | tablecolumn_moveAfter: |
|---|
| 41 | label: 'Move Column After' |
|---|
| 42 | group: 'tablecolumn' |
|---|
| 43 | command: 'columnMoveAfter' |
|---|
| 44 | order: 12 |
|---|
| 45 | |
|---|
| 46 | editor.addCommand "rowMoveBefore", |
|---|
| 47 | exec: (editor) -> moveRowBefore editor.getSelection() |
|---|
| 48 | editor.addCommand "rowMoveAfter", |
|---|
| 49 | exec: (editor) -> moveRowAfter editor.getSelection() |
|---|
| 50 | editor.addCommand "columnMoveBefore", |
|---|
| 51 | exec: (editor) -> moveColumnBefore editor.getSelection() |
|---|
| 52 | editor.addCommand "columnMoveAfter", |
|---|
| 53 | exec: (editor) -> moveColumnAfter editor.getSelection() |
|---|
| 54 | |
|---|
| 55 | for key in ['rowInsertBefore', 'rowInsertAfter', 'columnInsertBefore', 'columnInsertAfter'] |
|---|
| 56 | cmd = editor._.commands[key] |
|---|
| 57 | continue if cmd._origExec |
|---|
| 58 | subMenuName = "table#{ key.replace(/Insert.*/, '') }" |
|---|
| 59 | do (cmd, subMenuName) -> |
|---|
| 60 | cmd._origExec = cmd.exec |
|---|
| 61 | cmd.exec = (args...) -> |
|---|
| 62 | rv = @_origExec(args...) |
|---|
| 63 | redrawContextMenu subMenuName |
|---|
| 64 | return rv |
|---|
| 65 | |
|---|
| 66 | MenuElement = null |
|---|
| 67 | CKEDITOR.ui.on 'ready', ({data}) -> |
|---|
| 68 | MenuElement = data if data._?.panel |
|---|
| 69 | |
|---|
| 70 | editor.on 'menuShow', ({data}) -> |
|---|
| 71 | return unless RedrawSubMenuName |
|---|
| 72 | panel = data[0] |
|---|
| 73 | level = panel._.definition.level |
|---|
| 74 | setTimeout((-> |
|---|
| 75 | panel.element.setStyles OffsetsByLevel[level] |
|---|
| 76 | for item, idx in MenuElement.items |
|---|
| 77 | continue unless item.name == RedrawSubMenuName |
|---|
| 78 | do (MenuElement, idx) -> setTimeout((-> MenuElement._.showSubMenu idx), 100) |
|---|
| 79 | RedrawSubMenuName = null |
|---|
| 80 | return |
|---|
| 81 | ), 1) |
|---|
| 82 | |
|---|
| 83 | # Utility functions to detect edge of tables. |
|---|
| 84 | |
|---|
| 85 | isFirstRow = (selection) -> |
|---|
| 86 | cells = getSelectedCells( selection ) |
|---|
| 87 | firstCell = cells[0] |
|---|
| 88 | startRow = firstCell.getParent() |
|---|
| 89 | startRowIndex = startRow.$.rowIndex |
|---|
| 90 | return true if startRowIndex == 0 |
|---|
| 91 | table = firstCell.getAscendant( 'table' ) |
|---|
| 92 | rowCells = table.$.rows[0].cells |
|---|
| 93 | maxRowSpan = Math.max((mapCell.rowSpan for mapCell in rowCells)...) |
|---|
| 94 | return startRowIndex <= (maxRowSpan-1) |
|---|
| 95 | |
|---|
| 96 | isLastRow = (selection) -> |
|---|
| 97 | cells = getSelectedCells( selection ) |
|---|
| 98 | lastCell = cells[ cells.length - 1 ] |
|---|
| 99 | table = lastCell.getAscendant( 'table' ) |
|---|
| 100 | endRow = lastCell.getParent() |
|---|
| 101 | rowCells = endRow.$.cells |
|---|
| 102 | endRowIndex = endRow.$.rowIndex |
|---|
| 103 | maxRowSpan = Math.max((mapCell.rowSpan for mapCell in rowCells)...) |
|---|
| 104 | return ((endRowIndex + maxRowSpan) >= table.$.rows.length) |
|---|
| 105 | |
|---|
| 106 | isFirstColumn = (selection) -> |
|---|
| 107 | cells = getSelectedCells( selection ) |
|---|
| 108 | startColIndex = getColumnsIndices( cells, 1 ) |
|---|
| 109 | return (startColIndex == 0) |
|---|
| 110 | |
|---|
| 111 | isLastColumn = (selection) -> |
|---|
| 112 | cells = getSelectedCells( selection ) |
|---|
| 113 | endColIndex = getColumnsIndices( cells ) |
|---|
| 114 | lastRow = cells[ cells.length - 1 ].getParent() |
|---|
| 115 | rowCells = lastRow.$.cells |
|---|
| 116 | colIndex = -1 |
|---|
| 117 | for mapCell in rowCells |
|---|
| 118 | colIndex += mapCell.colSpan |
|---|
| 119 | return false if colIndex > endColIndex |
|---|
| 120 | return true |
|---|
| 121 | |
|---|
| 122 | $$ = (elm) -> new CKEDITOR.dom.element(elm) |
|---|
| 123 | |
|---|
| 124 | moveRowBefore = (selection) -> |
|---|
| 125 | cells = getSelectedCells( selection ) |
|---|
| 126 | endRow = cells[ cells.length - 1 ].getParent() |
|---|
| 127 | firstCell = cells[ 0 ] |
|---|
| 128 | startRowIndex = firstCell.getParent().$.rowIndex |
|---|
| 129 | table = firstCell.getAscendant( 'table' ) |
|---|
| 130 | prevRow = table.$.rows[startRowIndex - 1] |
|---|
| 131 | $$(prevRow).insertAfter endRow |
|---|
| 132 | redrawContextMenu 'tablerow' |
|---|
| 133 | |
|---|
| 134 | moveRowAfter = (selection) -> |
|---|
| 135 | cells = getSelectedCells( selection ) |
|---|
| 136 | startRow = cells[ 0 ].getParent() |
|---|
| 137 | lastCell = cells[ cells.length - 1 ] |
|---|
| 138 | endRowIndex = lastCell.getParent().$.rowIndex + lastCell.$.rowSpan - 1 |
|---|
| 139 | table = lastCell.getAscendant( 'table' ) |
|---|
| 140 | nextRow = table.$.rows[endRowIndex + 1] |
|---|
| 141 | $$(nextRow).insertBefore startRow |
|---|
| 142 | redrawContextMenu 'tablerow' |
|---|
| 143 | |
|---|
| 144 | moveColumn = (selection, isBefore) -> |
|---|
| 145 | cells = getSelectedCells( selection ) |
|---|
| 146 | table = cells[0].getAscendant( 'table' ) |
|---|
| 147 | startColIndex = getColumnsIndices( cells, 1 ) |
|---|
| 148 | endColIndex = getColumnsIndices( cells ) |
|---|
| 149 | for row in table.$.rows |
|---|
| 150 | rowCells = row.cells |
|---|
| 151 | if isBefore |
|---|
| 152 | $$(rowCells[startColIndex - 1]).insertAfter $$(rowCells[endColIndex]) |
|---|
| 153 | else |
|---|
| 154 | $$(rowCells[endColIndex + 1]).insertBefore $$(rowCells[startColIndex]) |
|---|
| 155 | redrawContextMenu 'tablecolumn' |
|---|
| 156 | |
|---|
| 157 | moveColumnBefore = (selection) -> |
|---|
| 158 | moveColumn selection, true |
|---|
| 159 | |
|---|
| 160 | moveColumnAfter = (selection) -> |
|---|
| 161 | moveColumn selection, false |
|---|
| 162 | |
|---|
| 163 | redrawContextMenu = (subMenuName) -> |
|---|
| 164 | RedrawSubMenuName = subMenuName |
|---|
| 165 | for elm, idx in $('.cke_contextmenu').get() |
|---|
| 166 | $parent = $(elm).parent() |
|---|
| 167 | OffsetsByLevel[idx] = |
|---|
| 168 | top: $parent.css('top') |
|---|
| 169 | left: $parent.css('left') |
|---|
| 170 | editor.execCommand 'contextMenu' |
|---|
| 171 | |
|---|
| 172 | # Everything below is copied from tabletools/plugin.js # |
|---|
| 173 | cellNodeRegex = /^(?:td|th)$/ |
|---|
| 174 | |
|---|
| 175 | getSelectedCells = (selection) -> |
|---|
| 176 | bookmarks = selection.createBookmarks() |
|---|
| 177 | ranges = selection.getRanges() |
|---|
| 178 | retval = [] |
|---|
| 179 | database = {} |
|---|
| 180 | i = 0 |
|---|
| 181 | |
|---|
| 182 | moveOutOfCellGuard = (node) -> |
|---|
| 183 | return if retval.length > 0 |
|---|
| 184 | if node.type is CKEDITOR.NODE_ELEMENT and cellNodeRegex.test(node.getName()) and not node.getCustomData("selected_cell") |
|---|
| 185 | CKEDITOR.dom.element.setMarker database, node, "selected_cell", true |
|---|
| 186 | retval.push node |
|---|
| 187 | return |
|---|
| 188 | |
|---|
| 189 | for range in ranges |
|---|
| 190 | if range.collapsed |
|---|
| 191 | startNode = range.getCommonAncestor() |
|---|
| 192 | nearestCell = startNode.getAscendant("td", true) or startNode.getAscendant("th", true) |
|---|
| 193 | retval.push nearestCell if nearestCell |
|---|
| 194 | else |
|---|
| 195 | walker = new CKEDITOR.dom.walker(range) |
|---|
| 196 | node = undefined |
|---|
| 197 | walker.guard = moveOutOfCellGuard |
|---|
| 198 | while (node = walker.next()) |
|---|
| 199 | parent = node.getAscendant("td") or node.getAscendant("th") |
|---|
| 200 | if parent and not parent.getCustomData("selected_cell") |
|---|
| 201 | CKEDITOR.dom.element.setMarker database, parent, "selected_cell", true |
|---|
| 202 | retval.push parent |
|---|
| 203 | |
|---|
| 204 | CKEDITOR.dom.element.clearAllMarkers database |
|---|
| 205 | selection.selectBookmarks bookmarks |
|---|
| 206 | return retval |
|---|
| 207 | |
|---|
| 208 | getColumnsIndices = (cells, isStart) -> |
|---|
| 209 | retval = (if isStart then Infinity else 0) |
|---|
| 210 | for cell in cells |
|---|
| 211 | colIndex = getCellColIndex(cell, isStart) |
|---|
| 212 | if isStart |
|---|
| 213 | retval = colIndex if colIndex < retval |
|---|
| 214 | else |
|---|
| 215 | retval = colIndex if colIndex > retval |
|---|
| 216 | return retval |
|---|
| 217 | |
|---|
| 218 | getCellColIndex = (cell, isStart) -> |
|---|
| 219 | row = cell.getParent() |
|---|
| 220 | rowCells = row.$.cells |
|---|
| 221 | colIndex = 0 |
|---|
| 222 | |
|---|
| 223 | for mapCell in rowCells |
|---|
| 224 | colIndex += (if isStart then 1 else mapCell.colSpan) |
|---|
| 225 | break if mapCell is cell.$ |
|---|
| 226 | return colIndex - 1 |
|---|