/static/scripts/tiny_mce/plugins/table/editor_plugin_src.js

http://n23.googlecode.com/ · JavaScript · 1115 lines · 811 code · 236 blank · 68 comment · 283 complexity · d35b6a97b20268acd13920ab93006a3c MD5 · raw file

  1. /**
  2. * $Id: editor_plugin_src.js 824 2008-04-28 15:12:06Z spocke $
  3. *
  4. * @author Moxiecode
  5. * @copyright Copyright Š 2004-2008, Moxiecode Systems AB, All rights reserved.
  6. */
  7. (function() {
  8. var each = tinymce.each;
  9. tinymce.create('tinymce.plugins.TablePlugin', {
  10. init : function(ed, url) {
  11. var t = this;
  12. t.editor = ed;
  13. t.url = url;
  14. // Register buttons
  15. each([
  16. ['table', 'table.desc', 'mceInsertTable', true],
  17. ['delete_table', 'table.del', 'mceTableDelete'],
  18. ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'],
  19. ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'],
  20. ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'],
  21. ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'],
  22. ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'],
  23. ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'],
  24. ['row_props', 'table.row_desc', 'mceTableRowProps', true],
  25. ['cell_props', 'table.cell_desc', 'mceTableCellProps', true],
  26. ['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true],
  27. ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true]
  28. ], function(c) {
  29. ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]});
  30. });
  31. ed.onInit.add(function() {
  32. if (ed && ed.plugins.contextmenu) {
  33. ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
  34. var sm, se = ed.selection, el = se.getNode() || ed.getBody();
  35. if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th')) {
  36. m.removeAll();
  37. if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
  38. m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true});
  39. m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});
  40. m.addSeparator();
  41. }
  42. if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) {
  43. m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});
  44. m.addSeparator();
  45. }
  46. m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', ui : true, value : {action : 'insert'}});
  47. m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable', ui : true});
  48. m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete', ui : true});
  49. m.addSeparator();
  50. // Cell menu
  51. sm = m.addMenu({title : 'table.cell'});
  52. sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps', ui : true});
  53. sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells', ui : true});
  54. sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells', ui : true});
  55. // Row menu
  56. sm = m.addMenu({title : 'table.row'});
  57. sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps', ui : true});
  58. sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'});
  59. sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'});
  60. sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'});
  61. sm.addSeparator();
  62. sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'});
  63. sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'});
  64. sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'});
  65. sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'});
  66. // Column menu
  67. sm = m.addMenu({title : 'table.col'});
  68. sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'});
  69. sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'});
  70. sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'});
  71. } else
  72. m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', ui : true});
  73. });
  74. }
  75. });
  76. // Add undo level when new rows are created using the tab key
  77. ed.onKeyDown.add(function(ed, e) {
  78. if (e.keyCode == 9 && ed.dom.getParent(ed.selection.getNode(), 'TABLE')) {
  79. if (!tinymce.isGecko && !tinymce.isOpera) {
  80. tinyMCE.execInstanceCommand(ed.editorId, "mceTableMoveToNextRow", true);
  81. return tinymce.dom.Event.cancel(e);
  82. }
  83. ed.undoManager.add();
  84. }
  85. });
  86. // Select whole table is a table border is clicked
  87. if (!tinymce.isIE) {
  88. if (ed.getParam('table_selection', true)) {
  89. ed.onClick.add(function(ed, e) {
  90. e = e.target;
  91. if (e.nodeName === 'TABLE')
  92. ed.selection.select(e);
  93. });
  94. }
  95. }
  96. ed.onNodeChange.add(function(ed, cm, n) {
  97. var p = ed.dom.getParent(n, 'td,th,caption');
  98. cm.setActive('table', n.nodeName === 'TABLE' || !!p);
  99. if (p && p.nodeName === 'CAPTION')
  100. p = null;
  101. cm.setDisabled('delete_table', !p);
  102. cm.setDisabled('delete_col', !p);
  103. cm.setDisabled('delete_table', !p);
  104. cm.setDisabled('delete_row', !p);
  105. cm.setDisabled('col_after', !p);
  106. cm.setDisabled('col_before', !p);
  107. cm.setDisabled('row_after', !p);
  108. cm.setDisabled('row_before', !p);
  109. cm.setDisabled('row_props', !p);
  110. cm.setDisabled('cell_props', !p);
  111. cm.setDisabled('split_cells', !p || (parseInt(ed.dom.getAttrib(p, 'colspan', '1')) < 2 && parseInt(ed.dom.getAttrib(p, 'rowspan', '1')) < 2));
  112. cm.setDisabled('merge_cells', !p);
  113. });
  114. // Padd empty table cells
  115. if (!tinymce.isIE) {
  116. ed.onBeforeSetContent.add(function(ed, o) {
  117. if (o.initial)
  118. o.content = o.content.replace(/<(td|th)([^>]+|)>\s*<\/(td|th)>/g, tinymce.isOpera ? '<$1$2>&nbsp;</$1>' : '<$1$2><br mce_bogus="1" /></$1>');
  119. });
  120. }
  121. },
  122. execCommand : function(cmd, ui, val) {
  123. var ed = this.editor, b;
  124. // Is table command
  125. switch (cmd) {
  126. case "mceTableMoveToNextRow":
  127. case "mceInsertTable":
  128. case "mceTableRowProps":
  129. case "mceTableCellProps":
  130. case "mceTableSplitCells":
  131. case "mceTableMergeCells":
  132. case "mceTableInsertRowBefore":
  133. case "mceTableInsertRowAfter":
  134. case "mceTableDeleteRow":
  135. case "mceTableInsertColBefore":
  136. case "mceTableInsertColAfter":
  137. case "mceTableDeleteCol":
  138. case "mceTableCutRow":
  139. case "mceTableCopyRow":
  140. case "mceTablePasteRowBefore":
  141. case "mceTablePasteRowAfter":
  142. case "mceTableDelete":
  143. ed.execCommand('mceBeginUndoLevel');
  144. this._doExecCommand(cmd, ui, val);
  145. ed.execCommand('mceEndUndoLevel');
  146. return true;
  147. }
  148. // Pass to next handler in chain
  149. return false;
  150. },
  151. getInfo : function() {
  152. return {
  153. longname : 'Tables',
  154. author : 'Moxiecode Systems AB',
  155. authorurl : 'http://tinymce.moxiecode.com',
  156. infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/table',
  157. version : tinymce.majorVersion + "." + tinymce.minorVersion
  158. };
  159. },
  160. // Private plugin internal methods
  161. /**
  162. * Executes the table commands.
  163. */
  164. _doExecCommand : function(command, user_interface, value) {
  165. var inst = this.editor, ed = inst, url = this.url;
  166. var focusElm = inst.selection.getNode();
  167. var trElm = inst.dom.getParent(focusElm, "tr");
  168. var tdElm = inst.dom.getParent(focusElm, "td,th");
  169. var tableElm = inst.dom.getParent(focusElm, "table");
  170. var doc = inst.contentWindow.document;
  171. var tableBorder = tableElm ? tableElm.getAttribute("border") : "";
  172. // Get first TD if no TD found
  173. if (trElm && tdElm == null)
  174. tdElm = trElm.cells[0];
  175. function inArray(ar, v) {
  176. for (var i=0; i<ar.length; i++) {
  177. // Is array
  178. if (ar[i].length > 0 && inArray(ar[i], v))
  179. return true;
  180. // Found value
  181. if (ar[i] == v)
  182. return true;
  183. }
  184. return false;
  185. }
  186. function select(dx, dy) {
  187. var td;
  188. grid = getTableGrid(tableElm);
  189. dx = dx || 0;
  190. dy = dy || 0;
  191. dx = Math.max(cpos.cellindex + dx, 0);
  192. dy = Math.max(cpos.rowindex + dy, 0);
  193. // Recalculate grid and select
  194. inst.execCommand('mceRepaint');
  195. td = getCell(grid, dy, dx);
  196. if (td) {
  197. inst.selection.select(td.firstChild || td);
  198. inst.selection.collapse(1);
  199. }
  200. };
  201. function makeTD() {
  202. var newTD = doc.createElement("td");
  203. if (!tinymce.isIE)
  204. newTD.innerHTML = '<br mce_bogus="1"/>';
  205. }
  206. function getColRowSpan(td) {
  207. var colspan = inst.dom.getAttrib(td, "colspan");
  208. var rowspan = inst.dom.getAttrib(td, "rowspan");
  209. colspan = colspan == "" ? 1 : parseInt(colspan);
  210. rowspan = rowspan == "" ? 1 : parseInt(rowspan);
  211. return {colspan : colspan, rowspan : rowspan};
  212. }
  213. function getCellPos(grid, td) {
  214. var x, y;
  215. for (y=0; y<grid.length; y++) {
  216. for (x=0; x<grid[y].length; x++) {
  217. if (grid[y][x] == td)
  218. return {cellindex : x, rowindex : y};
  219. }
  220. }
  221. return null;
  222. }
  223. function getCell(grid, row, col) {
  224. if (grid[row] && grid[row][col])
  225. return grid[row][col];
  226. return null;
  227. }
  228. function getNextCell(table, cell) {
  229. var cells = [], x = 0, i, j, cell, nextCell;
  230. for (i = 0; i < table.rows.length; i++)
  231. for (j = 0; j < table.rows[i].cells.length; j++, x++)
  232. cells[x] = table.rows[i].cells[j];
  233. for (i = 0; i < cells.length; i++)
  234. if (cells[i] == cell)
  235. if (nextCell = cells[i+1])
  236. return nextCell;
  237. }
  238. function getTableGrid(table) {
  239. var grid = [], rows = table.rows, x, y, td, sd, xstart, x2, y2;
  240. for (y=0; y<rows.length; y++) {
  241. for (x=0; x<rows[y].cells.length; x++) {
  242. td = rows[y].cells[x];
  243. sd = getColRowSpan(td);
  244. // All ready filled
  245. for (xstart = x; grid[y] && grid[y][xstart]; xstart++) ;
  246. // Fill box
  247. for (y2=y; y2<y+sd['rowspan']; y2++) {
  248. if (!grid[y2])
  249. grid[y2] = [];
  250. for (x2=xstart; x2<xstart+sd['colspan']; x2++)
  251. grid[y2][x2] = td;
  252. }
  253. }
  254. }
  255. return grid;
  256. }
  257. function trimRow(table, tr, td, new_tr) {
  258. var grid = getTableGrid(table), cpos = getCellPos(grid, td);
  259. var cells, lastElm;
  260. // Time to crop away some
  261. if (new_tr.cells.length != tr.childNodes.length) {
  262. cells = tr.childNodes;
  263. lastElm = null;
  264. for (var x=0; td = getCell(grid, cpos.rowindex, x); x++) {
  265. var remove = true;
  266. var sd = getColRowSpan(td);
  267. // Remove due to rowspan
  268. if (inArray(cells, td)) {
  269. new_tr.childNodes[x]._delete = true;
  270. } else if ((lastElm == null || td != lastElm) && sd.colspan > 1) { // Remove due to colspan
  271. for (var i=x; i<x+td.colSpan; i++)
  272. new_tr.childNodes[i]._delete = true;
  273. }
  274. if ((lastElm == null || td != lastElm) && sd.rowspan > 1)
  275. td.rowSpan = sd.rowspan + 1;
  276. lastElm = td;
  277. }
  278. deleteMarked(tableElm);
  279. }
  280. }
  281. function prevElm(node, name) {
  282. while ((node = node.previousSibling) != null) {
  283. if (node.nodeName == name)
  284. return node;
  285. }
  286. return null;
  287. }
  288. function nextElm(node, names) {
  289. var namesAr = names.split(',');
  290. while ((node = node.nextSibling) != null) {
  291. for (var i=0; i<namesAr.length; i++) {
  292. if (node.nodeName.toLowerCase() == namesAr[i].toLowerCase() )
  293. return node;
  294. }
  295. }
  296. return null;
  297. }
  298. function deleteMarked(tbl) {
  299. if (tbl.rows == 0)
  300. return;
  301. var tr = tbl.rows[0];
  302. do {
  303. var next = nextElm(tr, "TR");
  304. // Delete row
  305. if (tr._delete) {
  306. tr.parentNode.removeChild(tr);
  307. continue;
  308. }
  309. // Delete cells
  310. var td = tr.cells[0];
  311. if (td.cells > 1) {
  312. do {
  313. var nexttd = nextElm(td, "TD,TH");
  314. if (td._delete)
  315. td.parentNode.removeChild(td);
  316. } while ((td = nexttd) != null);
  317. }
  318. } while ((tr = next) != null);
  319. }
  320. function addRows(td_elm, tr_elm, rowspan) {
  321. // Add rows
  322. td_elm.rowSpan = 1;
  323. var trNext = nextElm(tr_elm, "TR");
  324. for (var i=1; i<rowspan && trNext; i++) {
  325. var newTD = doc.createElement("td");
  326. if (!tinymce.isIE)
  327. newTD.innerHTML = '<br mce_bogus="1"/>';
  328. if (tinymce.isIE)
  329. trNext.insertBefore(newTD, trNext.cells(td_elm.cellIndex));
  330. else
  331. trNext.insertBefore(newTD, trNext.cells[td_elm.cellIndex]);
  332. trNext = nextElm(trNext, "TR");
  333. }
  334. }
  335. function copyRow(doc, table, tr) {
  336. var grid = getTableGrid(table);
  337. var newTR = tr.cloneNode(false);
  338. var cpos = getCellPos(grid, tr.cells[0]);
  339. var lastCell = null;
  340. var tableBorder = inst.dom.getAttrib(table, "border");
  341. var tdElm = null;
  342. for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
  343. var newTD = null;
  344. if (lastCell != tdElm) {
  345. for (var i=0; i<tr.cells.length; i++) {
  346. if (tdElm == tr.cells[i]) {
  347. newTD = tdElm.cloneNode(true);
  348. break;
  349. }
  350. }
  351. }
  352. if (newTD == null) {
  353. newTD = doc.createElement("td");
  354. if (!tinymce.isIE)
  355. newTD.innerHTML = '<br mce_bogus="1"/>';
  356. }
  357. // Reset col/row span
  358. newTD.colSpan = 1;
  359. newTD.rowSpan = 1;
  360. newTR.appendChild(newTD);
  361. lastCell = tdElm;
  362. }
  363. return newTR;
  364. }
  365. // ---- Commands -----
  366. // Handle commands
  367. switch (command) {
  368. case "mceTableMoveToNextRow":
  369. var nextCell = getNextCell(tableElm, tdElm);
  370. if (!nextCell) {
  371. inst.execCommand("mceTableInsertRowAfter", tdElm);
  372. nextCell = getNextCell(tableElm, tdElm);
  373. }
  374. inst.selection.select(nextCell);
  375. inst.selection.collapse(true);
  376. return true;
  377. case "mceTableRowProps":
  378. if (trElm == null)
  379. return true;
  380. if (user_interface) {
  381. inst.windowManager.open({
  382. url : url + '/row.htm',
  383. width : 400 + parseInt(inst.getLang('table.rowprops_delta_width', 0)),
  384. height : 295 + parseInt(inst.getLang('table.rowprops_delta_height', 0)),
  385. inline : 1
  386. }, {
  387. plugin_url : url
  388. });
  389. }
  390. return true;
  391. case "mceTableCellProps":
  392. if (tdElm == null)
  393. return true;
  394. if (user_interface) {
  395. inst.windowManager.open({
  396. url : url + '/cell.htm',
  397. width : 400 + parseInt(inst.getLang('table.cellprops_delta_width', 0)),
  398. height : 295 + parseInt(inst.getLang('table.cellprops_delta_height', 0)),
  399. inline : 1
  400. }, {
  401. plugin_url : url
  402. });
  403. }
  404. return true;
  405. case "mceInsertTable":
  406. if (user_interface) {
  407. inst.windowManager.open({
  408. url : url + '/table.htm',
  409. width : 400 + parseInt(inst.getLang('table.table_delta_width', 0)),
  410. height : 320 + parseInt(inst.getLang('table.table_delta_height', 0)),
  411. inline : 1
  412. }, {
  413. plugin_url : url,
  414. action : value ? value.action : 0
  415. });
  416. }
  417. return true;
  418. case "mceTableDelete":
  419. var table = inst.dom.getParent(inst.selection.getNode(), "table");
  420. if (table) {
  421. table.parentNode.removeChild(table);
  422. inst.execCommand('mceRepaint');
  423. }
  424. return true;
  425. case "mceTableSplitCells":
  426. case "mceTableMergeCells":
  427. case "mceTableInsertRowBefore":
  428. case "mceTableInsertRowAfter":
  429. case "mceTableDeleteRow":
  430. case "mceTableInsertColBefore":
  431. case "mceTableInsertColAfter":
  432. case "mceTableDeleteCol":
  433. case "mceTableCutRow":
  434. case "mceTableCopyRow":
  435. case "mceTablePasteRowBefore":
  436. case "mceTablePasteRowAfter":
  437. // No table just return (invalid command)
  438. if (!tableElm)
  439. return true;
  440. // Table has a tbody use that reference
  441. // Changed logic by ApTest 2005.07.12 (www.aptest.com)
  442. // Now lookk at the focused element and take its parentNode. That will be a tbody or a table.
  443. if (trElm && tableElm != trElm.parentNode)
  444. tableElm = trElm.parentNode;
  445. if (tableElm && trElm) {
  446. switch (command) {
  447. case "mceTableCutRow":
  448. if (!trElm || !tdElm)
  449. return true;
  450. inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
  451. inst.execCommand("mceTableDeleteRow");
  452. break;
  453. case "mceTableCopyRow":
  454. if (!trElm || !tdElm)
  455. return true;
  456. inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
  457. break;
  458. case "mceTablePasteRowBefore":
  459. if (!trElm || !tdElm)
  460. return true;
  461. var newTR = inst.tableRowClipboard.cloneNode(true);
  462. var prevTR = prevElm(trElm, "TR");
  463. if (prevTR != null)
  464. trimRow(tableElm, prevTR, prevTR.cells[0], newTR);
  465. trElm.parentNode.insertBefore(newTR, trElm);
  466. break;
  467. case "mceTablePasteRowAfter":
  468. if (!trElm || !tdElm)
  469. return true;
  470. var nextTR = nextElm(trElm, "TR");
  471. var newTR = inst.tableRowClipboard.cloneNode(true);
  472. trimRow(tableElm, trElm, tdElm, newTR);
  473. if (nextTR == null)
  474. trElm.parentNode.appendChild(newTR);
  475. else
  476. nextTR.parentNode.insertBefore(newTR, nextTR);
  477. break;
  478. case "mceTableInsertRowBefore":
  479. if (!trElm || !tdElm)
  480. return true;
  481. var grid = getTableGrid(tableElm);
  482. var cpos = getCellPos(grid, tdElm);
  483. var newTR = doc.createElement("tr");
  484. var lastTDElm = null;
  485. cpos.rowindex--;
  486. if (cpos.rowindex < 0)
  487. cpos.rowindex = 0;
  488. // Create cells
  489. for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
  490. if (tdElm != lastTDElm) {
  491. var sd = getColRowSpan(tdElm);
  492. if (sd['rowspan'] == 1) {
  493. var newTD = doc.createElement("td");
  494. if (!tinymce.isIE)
  495. newTD.innerHTML = '<br mce_bogus="1"/>';
  496. newTD.colSpan = tdElm.colSpan;
  497. newTR.appendChild(newTD);
  498. } else
  499. tdElm.rowSpan = sd['rowspan'] + 1;
  500. lastTDElm = tdElm;
  501. }
  502. }
  503. trElm.parentNode.insertBefore(newTR, trElm);
  504. select(0, 1);
  505. break;
  506. case "mceTableInsertRowAfter":
  507. if (!trElm || !tdElm)
  508. return true;
  509. var grid = getTableGrid(tableElm);
  510. var cpos = getCellPos(grid, tdElm);
  511. var newTR = doc.createElement("tr");
  512. var lastTDElm = null;
  513. // Create cells
  514. for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
  515. if (tdElm != lastTDElm) {
  516. var sd = getColRowSpan(tdElm);
  517. if (sd['rowspan'] == 1) {
  518. var newTD = doc.createElement("td");
  519. if (!tinymce.isIE)
  520. newTD.innerHTML = '<br mce_bogus="1"/>';
  521. newTD.colSpan = tdElm.colSpan;
  522. newTR.appendChild(newTD);
  523. } else
  524. tdElm.rowSpan = sd['rowspan'] + 1;
  525. lastTDElm = tdElm;
  526. }
  527. }
  528. if (newTR.hasChildNodes()) {
  529. var nextTR = nextElm(trElm, "TR");
  530. if (nextTR)
  531. nextTR.parentNode.insertBefore(newTR, nextTR);
  532. else
  533. tableElm.appendChild(newTR);
  534. }
  535. select(0, 1);
  536. break;
  537. case "mceTableDeleteRow":
  538. if (!trElm || !tdElm)
  539. return true;
  540. var grid = getTableGrid(tableElm);
  541. var cpos = getCellPos(grid, tdElm);
  542. // Only one row, remove whole table
  543. if (grid.length == 1) {
  544. inst.dom.remove(inst.dom.getParent(tableElm, "table"));
  545. return true;
  546. }
  547. // Move down row spanned cells
  548. var cells = trElm.cells;
  549. var nextTR = nextElm(trElm, "TR");
  550. for (var x=0; x<cells.length; x++) {
  551. if (cells[x].rowSpan > 1) {
  552. var newTD = cells[x].cloneNode(true);
  553. var sd = getColRowSpan(cells[x]);
  554. newTD.rowSpan = sd.rowspan - 1;
  555. var nextTD = nextTR.cells[x];
  556. if (nextTD == null)
  557. nextTR.appendChild(newTD);
  558. else
  559. nextTR.insertBefore(newTD, nextTD);
  560. }
  561. }
  562. // Delete cells
  563. var lastTDElm = null;
  564. for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
  565. if (tdElm != lastTDElm) {
  566. var sd = getColRowSpan(tdElm);
  567. if (sd.rowspan > 1) {
  568. tdElm.rowSpan = sd.rowspan - 1;
  569. } else {
  570. trElm = tdElm.parentNode;
  571. if (trElm.parentNode)
  572. trElm._delete = true;
  573. }
  574. lastTDElm = tdElm;
  575. }
  576. }
  577. deleteMarked(tableElm);
  578. select(0, -1);
  579. break;
  580. case "mceTableInsertColBefore":
  581. if (!trElm || !tdElm)
  582. return true;
  583. var grid = getTableGrid(tableElm);
  584. var cpos = getCellPos(grid, tdElm);
  585. var lastTDElm = null;
  586. for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
  587. if (tdElm != lastTDElm) {
  588. var sd = getColRowSpan(tdElm);
  589. if (sd['colspan'] == 1) {
  590. var newTD = doc.createElement(tdElm.nodeName);
  591. if (!tinymce.isIE)
  592. newTD.innerHTML = '<br mce_bogus="1"/>';
  593. newTD.rowSpan = tdElm.rowSpan;
  594. tdElm.parentNode.insertBefore(newTD, tdElm);
  595. } else
  596. tdElm.colSpan++;
  597. lastTDElm = tdElm;
  598. }
  599. }
  600. select();
  601. break;
  602. case "mceTableInsertColAfter":
  603. if (!trElm || !tdElm)
  604. return true;
  605. var grid = getTableGrid(tableElm);
  606. var cpos = getCellPos(grid, tdElm);
  607. var lastTDElm = null;
  608. for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
  609. if (tdElm != lastTDElm) {
  610. var sd = getColRowSpan(tdElm);
  611. if (sd['colspan'] == 1) {
  612. var newTD = doc.createElement(tdElm.nodeName);
  613. if (!tinymce.isIE)
  614. newTD.innerHTML = '<br mce_bogus="1"/>';
  615. newTD.rowSpan = tdElm.rowSpan;
  616. var nextTD = nextElm(tdElm, "TD,TH");
  617. if (nextTD == null)
  618. tdElm.parentNode.appendChild(newTD);
  619. else
  620. nextTD.parentNode.insertBefore(newTD, nextTD);
  621. } else
  622. tdElm.colSpan++;
  623. lastTDElm = tdElm;
  624. }
  625. }
  626. select(1);
  627. break;
  628. case "mceTableDeleteCol":
  629. if (!trElm || !tdElm)
  630. return true;
  631. var grid = getTableGrid(tableElm);
  632. var cpos = getCellPos(grid, tdElm);
  633. var lastTDElm = null;
  634. // Only one col, remove whole table
  635. if (grid.length > 1 && grid[0].length <= 1) {
  636. inst.dom.remove(inst.dom.getParent(tableElm, "table"));
  637. return true;
  638. }
  639. // Delete cells
  640. for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
  641. if (tdElm != lastTDElm) {
  642. var sd = getColRowSpan(tdElm);
  643. if (sd['colspan'] > 1)
  644. tdElm.colSpan = sd['colspan'] - 1;
  645. else {
  646. if (tdElm.parentNode)
  647. tdElm.parentNode.removeChild(tdElm);
  648. }
  649. lastTDElm = tdElm;
  650. }
  651. }
  652. select(-1);
  653. break;
  654. case "mceTableSplitCells":
  655. if (!trElm || !tdElm)
  656. return true;
  657. var spandata = getColRowSpan(tdElm);
  658. var colspan = spandata["colspan"];
  659. var rowspan = spandata["rowspan"];
  660. // Needs splitting
  661. if (colspan > 1 || rowspan > 1) {
  662. // Generate cols
  663. tdElm.colSpan = 1;
  664. for (var i=1; i<colspan; i++) {
  665. var newTD = doc.createElement("td");
  666. if (!tinymce.isIE)
  667. newTD.innerHTML = '<br mce_bogus="1"/>';
  668. trElm.insertBefore(newTD, nextElm(tdElm, "TD,TH"));
  669. if (rowspan > 1)
  670. addRows(newTD, trElm, rowspan);
  671. }
  672. addRows(tdElm, trElm, rowspan);
  673. }
  674. // Apply visual aids
  675. tableElm = inst.dom.getParent(inst.selection.getNode(), "table");
  676. break;
  677. case "mceTableMergeCells":
  678. var rows = [];
  679. var sel = inst.selection.getSel();
  680. var grid = getTableGrid(tableElm);
  681. if (tinymce.isIE || sel.rangeCount == 1) {
  682. if (user_interface) {
  683. // Setup template
  684. var sp = getColRowSpan(tdElm);
  685. inst.windowManager.open({
  686. url : url + '/merge_cells.htm',
  687. width : 240 + parseInt(inst.getLang('table.merge_cells_delta_width', 0)),
  688. height : 110 + parseInt(inst.getLang('table.merge_cells_delta_height', 0)),
  689. inline : 1
  690. }, {
  691. action : "update",
  692. numcols : sp.colspan,
  693. numrows : sp.rowspan,
  694. plugin_url : url
  695. });
  696. return true;
  697. } else {
  698. var numRows = parseInt(value['numrows']);
  699. var numCols = parseInt(value['numcols']);
  700. var cpos = getCellPos(grid, tdElm);
  701. if (("" + numRows) == "NaN")
  702. numRows = 1;
  703. if (("" + numCols) == "NaN")
  704. numCols = 1;
  705. // Get rows and cells
  706. var tRows = tableElm.rows;
  707. for (var y=cpos.rowindex; y<grid.length; y++) {
  708. var rowCells = [];
  709. for (var x=cpos.cellindex; x<grid[y].length; x++) {
  710. var td = getCell(grid, y, x);
  711. if (td && !inArray(rows, td) && !inArray(rowCells, td)) {
  712. var cp = getCellPos(grid, td);
  713. // Within range
  714. if (cp.cellindex < cpos.cellindex+numCols && cp.rowindex < cpos.rowindex+numRows)
  715. rowCells[rowCells.length] = td;
  716. }
  717. }
  718. if (rowCells.length > 0)
  719. rows[rows.length] = rowCells;
  720. var td = getCell(grid, cpos.rowindex, cpos.cellindex);
  721. each(ed.dom.select('br', td), function(e, i) {
  722. if (i > 0 && ed.dom.getAttrib('mce_bogus'))
  723. ed.dom.remove(e);
  724. });
  725. }
  726. //return true;
  727. }
  728. } else {
  729. var cells = [];
  730. var sel = inst.selection.getSel();
  731. var lastTR = null;
  732. var curRow = null;
  733. var x1 = -1, y1 = -1, x2, y2;
  734. // Only one cell selected, whats the point?
  735. if (sel.rangeCount < 2)
  736. return true;
  737. // Get all selected cells
  738. for (var i=0; i<sel.rangeCount; i++) {
  739. var rng = sel.getRangeAt(i);
  740. var tdElm = rng.startContainer.childNodes[rng.startOffset];
  741. if (!tdElm)
  742. break;
  743. if (tdElm.nodeName == "TD" || tdElm.nodeName == "TH")
  744. cells[cells.length] = tdElm;
  745. }
  746. // Get rows and cells
  747. var tRows = tableElm.rows;
  748. for (var y=0; y<tRows.length; y++) {
  749. var rowCells = [];
  750. for (var x=0; x<tRows[y].cells.length; x++) {
  751. var td = tRows[y].cells[x];
  752. for (var i=0; i<cells.length; i++) {
  753. if (td == cells[i]) {
  754. rowCells[rowCells.length] = td;
  755. }
  756. }
  757. }
  758. if (rowCells.length > 0)
  759. rows[rows.length] = rowCells;
  760. }
  761. // Find selected cells in grid and box
  762. var curRow = [];
  763. var lastTR = null;
  764. for (var y=0; y<grid.length; y++) {
  765. for (var x=0; x<grid[y].length; x++) {
  766. grid[y][x]._selected = false;
  767. for (var i=0; i<cells.length; i++) {
  768. if (grid[y][x] == cells[i]) {
  769. // Get start pos
  770. if (x1 == -1) {
  771. x1 = x;
  772. y1 = y;
  773. }
  774. // Get end pos
  775. x2 = x;
  776. y2 = y;
  777. grid[y][x]._selected = true;
  778. }
  779. }
  780. }
  781. }
  782. // Is there gaps, if so deny
  783. for (var y=y1; y<=y2; y++) {
  784. for (var x=x1; x<=x2; x++) {
  785. if (!grid[y][x]._selected) {
  786. alert("Invalid selection for merge.");
  787. return true;
  788. }
  789. }
  790. }
  791. }
  792. // Validate selection and get total rowspan and colspan
  793. var rowSpan = 1, colSpan = 1;
  794. // Validate horizontal and get total colspan
  795. var lastRowSpan = -1;
  796. for (var y=0; y<rows.length; y++) {
  797. var rowColSpan = 0;
  798. for (var x=0; x<rows[y].length; x++) {
  799. var sd = getColRowSpan(rows[y][x]);
  800. rowColSpan += sd['colspan'];
  801. if (lastRowSpan != -1 && sd['rowspan'] != lastRowSpan) {
  802. alert("Invalid selection for merge.");
  803. return true;
  804. }
  805. lastRowSpan = sd['rowspan'];
  806. }
  807. if (rowColSpan > colSpan)
  808. colSpan = rowColSpan;
  809. lastRowSpan = -1;
  810. }
  811. // Validate vertical and get total rowspan
  812. var lastColSpan = -1;
  813. for (var x=0; x<rows[0].length; x++) {
  814. var colRowSpan = 0;
  815. for (var y=0; y<rows.length; y++) {
  816. var sd = getColRowSpan(rows[y][x]);
  817. colRowSpan += sd['rowspan'];
  818. if (lastColSpan != -1 && sd['colspan'] != lastColSpan) {
  819. alert("Invalid selection for merge.");
  820. return true;
  821. }
  822. lastColSpan = sd['colspan'];
  823. }
  824. if (colRowSpan > rowSpan)
  825. rowSpan = colRowSpan;
  826. lastColSpan = -1;
  827. }
  828. // Setup td
  829. tdElm = rows[0][0];
  830. tdElm.rowSpan = rowSpan;
  831. tdElm.colSpan = colSpan;
  832. // Merge cells
  833. for (var y=0; y<rows.length; y++) {
  834. for (var x=0; x<rows[y].length; x++) {
  835. var html = rows[y][x].innerHTML;
  836. var chk = html.replace(/[ \t\r\n]/g, "");
  837. if (chk != "<br/>" && chk != "<br>" && chk != '<br mce_bogus="1"/>' && (x+y > 0))
  838. tdElm.innerHTML += html;
  839. // Not current cell
  840. if (rows[y][x] != tdElm && !rows[y][x]._deleted) {
  841. var cpos = getCellPos(grid, rows[y][x]);
  842. var tr = rows[y][x].parentNode;
  843. tr.removeChild(rows[y][x]);
  844. rows[y][x]._deleted = true;
  845. // Empty TR, remove it
  846. if (!tr.hasChildNodes()) {
  847. tr.parentNode.removeChild(tr);
  848. var lastCell = null;
  849. for (var x=0; cellElm = getCell(grid, cpos.rowindex, x); x++) {
  850. if (cellElm != lastCell && cellElm.rowSpan > 1)
  851. cellElm.rowSpan--;
  852. lastCell = cellElm;
  853. }
  854. if (tdElm.rowSpan > 1)
  855. tdElm.rowSpan--;
  856. }
  857. }
  858. }
  859. }
  860. // Remove all but one bogus br
  861. each(ed.dom.select('br', tdElm), function(e, i) {
  862. if (i > 0 && ed.dom.getAttrib(e, 'mce_bogus'))
  863. ed.dom.remove(e);
  864. });
  865. break;
  866. }
  867. tableElm = inst.dom.getParent(inst.selection.getNode(), "table");
  868. inst.addVisual(tableElm);
  869. inst.nodeChanged();
  870. }
  871. return true;
  872. }
  873. // Pass to next handler in chain
  874. return false;
  875. }
  876. });
  877. // Register plugin
  878. tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);
  879. })();