plugin.js 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314
  1. /**
  2. * Compiled inline version. (Library mode)
  3. */
  4. /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
  5. /*globals $code */
  6. (function(exports, undefined) {
  7. "use strict";
  8. var modules = {};
  9. function require(ids, callback) {
  10. var module, defs = [];
  11. for (var i = 0; i < ids.length; ++i) {
  12. module = modules[ids[i]] || resolve(ids[i]);
  13. if (!module) {
  14. throw 'module definition dependecy not found: ' + ids[i];
  15. }
  16. defs.push(module);
  17. }
  18. callback.apply(null, defs);
  19. }
  20. function define(id, dependencies, definition) {
  21. if (typeof id !== 'string') {
  22. throw 'invalid module definition, module id must be defined and be a string';
  23. }
  24. if (dependencies === undefined) {
  25. throw 'invalid module definition, dependencies must be specified';
  26. }
  27. if (definition === undefined) {
  28. throw 'invalid module definition, definition function must be specified';
  29. }
  30. require(dependencies, function() {
  31. modules[id] = definition.apply(null, arguments);
  32. });
  33. }
  34. function defined(id) {
  35. return !!modules[id];
  36. }
  37. function resolve(id) {
  38. var target = exports;
  39. var fragments = id.split(/[.\/]/);
  40. for (var fi = 0; fi < fragments.length; ++fi) {
  41. if (!target[fragments[fi]]) {
  42. return;
  43. }
  44. target = target[fragments[fi]];
  45. }
  46. return target;
  47. }
  48. function expose(ids) {
  49. for (var i = 0; i < ids.length; i++) {
  50. var target = exports;
  51. var id = ids[i];
  52. var fragments = id.split(/[.\/]/);
  53. for (var fi = 0; fi < fragments.length - 1; ++fi) {
  54. if (target[fragments[fi]] === undefined) {
  55. target[fragments[fi]] = {};
  56. }
  57. target = target[fragments[fi]];
  58. }
  59. target[fragments[fragments.length - 1]] = modules[id];
  60. }
  61. }
  62. // Included from: js/tinymce/plugins/table/classes/TableGrid.js
  63. /**
  64. * TableGrid.js
  65. *
  66. * Copyright, Moxiecode Systems AB
  67. * Released under LGPL License.
  68. *
  69. * License: http://www.tinymce.com/license
  70. * Contributing: http://www.tinymce.com/contributing
  71. */
  72. /**
  73. * This class creates a grid out of a table element. This
  74. * makes it a whole lot easier to handle complex tables with
  75. * col/row spans.
  76. *
  77. * @class tinymce.tableplugin.TableGrid
  78. * @private
  79. */
  80. define("tinymce/tableplugin/TableGrid", [
  81. "tinymce/util/Tools",
  82. "tinymce/Env"
  83. ], function(Tools, Env) {
  84. var each = Tools.each;
  85. function getSpanVal(td, name) {
  86. return parseInt(td.getAttribute(name) || 1, 10);
  87. }
  88. return function(editor, table) {
  89. var grid, startPos, endPos, selectedCell, selection = editor.selection, dom = selection.dom;
  90. function buildGrid() {
  91. var startY = 0;
  92. grid = [];
  93. each(['thead', 'tbody', 'tfoot'], function(part) {
  94. var rows = dom.select('> ' + part + ' tr', table);
  95. each(rows, function(tr, y) {
  96. y += startY;
  97. each(dom.select('> td, > th', tr), function(td, x) {
  98. var x2, y2, rowspan, colspan;
  99. // Skip over existing cells produced by rowspan
  100. if (grid[y]) {
  101. while (grid[y][x]) {
  102. x++;
  103. }
  104. }
  105. // Get col/rowspan from cell
  106. rowspan = getSpanVal(td, 'rowspan');
  107. colspan = getSpanVal(td, 'colspan');
  108. // Fill out rowspan/colspan right and down
  109. for (y2 = y; y2 < y + rowspan; y2++) {
  110. if (!grid[y2]) {
  111. grid[y2] = [];
  112. }
  113. for (x2 = x; x2 < x + colspan; x2++) {
  114. grid[y2][x2] = {
  115. part: part,
  116. real: y2 == y && x2 == x,
  117. elm: td,
  118. rowspan: rowspan,
  119. colspan: colspan
  120. };
  121. }
  122. }
  123. });
  124. });
  125. startY += rows.length;
  126. });
  127. }
  128. function cloneNode(node, children) {
  129. node = node.cloneNode(children);
  130. node.removeAttribute('id');
  131. return node;
  132. }
  133. function getCell(x, y) {
  134. var row;
  135. row = grid[y];
  136. if (row) {
  137. return row[x];
  138. }
  139. }
  140. function setSpanVal(td, name, val) {
  141. if (td) {
  142. val = parseInt(val, 10);
  143. if (val === 1) {
  144. td.removeAttribute(name, 1);
  145. } else {
  146. td.setAttribute(name, val, 1);
  147. }
  148. }
  149. }
  150. function isCellSelected(cell) {
  151. return cell && (dom.hasClass(cell.elm, 'mce-item-selected') || cell == selectedCell);
  152. }
  153. function getSelectedRows() {
  154. var rows = [];
  155. each(table.rows, function(row) {
  156. each(row.cells, function(cell) {
  157. if (dom.hasClass(cell, 'mce-item-selected') || (selectedCell && cell == selectedCell.elm)) {
  158. rows.push(row);
  159. return false;
  160. }
  161. });
  162. });
  163. return rows;
  164. }
  165. function deleteTable() {
  166. var rng = dom.createRng();
  167. rng.setStartAfter(table);
  168. rng.setEndAfter(table);
  169. selection.setRng(rng);
  170. dom.remove(table);
  171. }
  172. function cloneCell(cell) {
  173. var formatNode, cloneFormats = {};
  174. if (editor.settings.table_clone_elements !== false) {
  175. cloneFormats = Tools.makeMap(
  176. (editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
  177. /[ ,]/
  178. );
  179. }
  180. // Clone formats
  181. Tools.walk(cell, function(node) {
  182. var curNode;
  183. if (node.nodeType == 3) {
  184. each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
  185. if (!cloneFormats[node.nodeName]) {
  186. return;
  187. }
  188. node = cloneNode(node, false);
  189. if (!formatNode) {
  190. formatNode = curNode = node;
  191. } else if (curNode) {
  192. curNode.appendChild(node);
  193. }
  194. curNode = node;
  195. });
  196. // Add something to the inner node
  197. if (curNode) {
  198. curNode.innerHTML = Env.ie ? '&nbsp;' : '<br data-mce-bogus="1" />';
  199. }
  200. return false;
  201. }
  202. }, 'childNodes');
  203. cell = cloneNode(cell, false);
  204. setSpanVal(cell, 'rowSpan', 1);
  205. setSpanVal(cell, 'colSpan', 1);
  206. if (formatNode) {
  207. cell.appendChild(formatNode);
  208. } else {
  209. if (!Env.ie) {
  210. cell.innerHTML = '<br data-mce-bogus="1" />';
  211. }
  212. }
  213. return cell;
  214. }
  215. function cleanup() {
  216. var rng = dom.createRng(), row;
  217. // Empty rows
  218. each(dom.select('tr', table), function(tr) {
  219. if (tr.cells.length === 0) {
  220. dom.remove(tr);
  221. }
  222. });
  223. // Empty table
  224. if (dom.select('tr', table).length === 0) {
  225. rng.setStartBefore(table);
  226. rng.setEndBefore(table);
  227. selection.setRng(rng);
  228. dom.remove(table);
  229. return;
  230. }
  231. // Empty header/body/footer
  232. each(dom.select('thead,tbody,tfoot', table), function(part) {
  233. if (part.rows.length === 0) {
  234. dom.remove(part);
  235. }
  236. });
  237. // Restore selection to start position if it still exists
  238. buildGrid();
  239. // If we have a valid startPos object
  240. if (startPos) {
  241. // Restore the selection to the closest table position
  242. row = grid[Math.min(grid.length - 1, startPos.y)];
  243. if (row) {
  244. selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
  245. selection.collapse(true);
  246. }
  247. }
  248. }
  249. function fillLeftDown(x, y, rows, cols) {
  250. var tr, x2, r, c, cell;
  251. tr = grid[y][x].elm.parentNode;
  252. for (r = 1; r <= rows; r++) {
  253. tr = dom.getNext(tr, 'tr');
  254. if (tr) {
  255. // Loop left to find real cell
  256. for (x2 = x; x2 >= 0; x2--) {
  257. cell = grid[y + r][x2].elm;
  258. if (cell.parentNode == tr) {
  259. // Append clones after
  260. for (c = 1; c <= cols; c++) {
  261. dom.insertAfter(cloneCell(cell), cell);
  262. }
  263. break;
  264. }
  265. }
  266. if (x2 == -1) {
  267. // Insert nodes before first cell
  268. for (c = 1; c <= cols; c++) {
  269. tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
  270. }
  271. }
  272. }
  273. }
  274. }
  275. function split() {
  276. each(grid, function(row, y) {
  277. each(row, function(cell, x) {
  278. var colSpan, rowSpan, i;
  279. if (isCellSelected(cell)) {
  280. cell = cell.elm;
  281. colSpan = getSpanVal(cell, 'colspan');
  282. rowSpan = getSpanVal(cell, 'rowspan');
  283. if (colSpan > 1 || rowSpan > 1) {
  284. setSpanVal(cell, 'rowSpan', 1);
  285. setSpanVal(cell, 'colSpan', 1);
  286. // Insert cells right
  287. for (i = 0; i < colSpan - 1; i++) {
  288. dom.insertAfter(cloneCell(cell), cell);
  289. }
  290. fillLeftDown(x, y, rowSpan - 1, colSpan);
  291. }
  292. }
  293. });
  294. });
  295. }
  296. function merge(cell, cols, rows) {
  297. var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count;
  298. // Use specified cell and cols/rows
  299. if (cell) {
  300. pos = getPos(cell);
  301. startX = pos.x;
  302. startY = pos.y;
  303. endX = startX + (cols - 1);
  304. endY = startY + (rows - 1);
  305. } else {
  306. startPos = endPos = null;
  307. // Calculate start/end pos by checking for selected cells in grid works better with context menu
  308. each(grid, function(row, y) {
  309. each(row, function(cell, x) {
  310. if (isCellSelected(cell)) {
  311. if (!startPos) {
  312. startPos = {x: x, y: y};
  313. }
  314. endPos = {x: x, y: y};
  315. }
  316. });
  317. });
  318. // Use selection, but make sure startPos is valid before accessing
  319. if (startPos) {
  320. startX = startPos.x;
  321. startY = startPos.y;
  322. endX = endPos.x;
  323. endY = endPos.y;
  324. }
  325. }
  326. // Find start/end cells
  327. startCell = getCell(startX, startY);
  328. endCell = getCell(endX, endY);
  329. // Check if the cells exists and if they are of the same part for example tbody = tbody
  330. if (startCell && endCell && startCell.part == endCell.part) {
  331. // Split and rebuild grid
  332. split();
  333. buildGrid();
  334. // Set row/col span to start cell
  335. startCell = getCell(startX, startY).elm;
  336. setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
  337. setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
  338. // Remove other cells and add it's contents to the start cell
  339. for (y = startY; y <= endY; y++) {
  340. for (x = startX; x <= endX; x++) {
  341. if (!grid[y] || !grid[y][x]) {
  342. continue;
  343. }
  344. cell = grid[y][x].elm;
  345. /*jshint loopfunc:true */
  346. /*eslint no-loop-func:0 */
  347. if (cell != startCell) {
  348. // Move children to startCell
  349. children = Tools.grep(cell.childNodes);
  350. each(children, function(node) {
  351. startCell.appendChild(node);
  352. });
  353. // Remove bogus nodes if there is children in the target cell
  354. if (children.length) {
  355. children = Tools.grep(startCell.childNodes);
  356. count = 0;
  357. each(children, function(node) {
  358. if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1) {
  359. startCell.removeChild(node);
  360. }
  361. });
  362. }
  363. dom.remove(cell);
  364. }
  365. }
  366. }
  367. // Remove empty rows etc and restore caret location
  368. cleanup();
  369. }
  370. }
  371. function insertRow(before) {
  372. var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
  373. // Find first/last row
  374. each(grid, function(row, y) {
  375. each(row, function(cell) {
  376. if (isCellSelected(cell)) {
  377. cell = cell.elm;
  378. rowElm = cell.parentNode;
  379. newRow = cloneNode(rowElm, false);
  380. posY = y;
  381. if (before) {
  382. return false;
  383. }
  384. }
  385. });
  386. if (before) {
  387. return !posY;
  388. }
  389. });
  390. // If posY is undefined there is nothing for us to do here...just return to avoid crashing below
  391. if (posY === undefined) {
  392. return;
  393. }
  394. for (x = 0; x < grid[0].length; x++) {
  395. // Cell not found could be because of an invalid table structure
  396. if (!grid[posY][x]) {
  397. continue;
  398. }
  399. cell = grid[posY][x].elm;
  400. if (cell != lastCell) {
  401. if (!before) {
  402. rowSpan = getSpanVal(cell, 'rowspan');
  403. if (rowSpan > 1) {
  404. setSpanVal(cell, 'rowSpan', rowSpan + 1);
  405. continue;
  406. }
  407. } else {
  408. // Check if cell above can be expanded
  409. if (posY > 0 && grid[posY - 1][x]) {
  410. otherCell = grid[posY - 1][x].elm;
  411. rowSpan = getSpanVal(otherCell, 'rowSpan');
  412. if (rowSpan > 1) {
  413. setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
  414. continue;
  415. }
  416. }
  417. }
  418. // Insert new cell into new row
  419. newCell = cloneCell(cell);
  420. setSpanVal(newCell, 'colSpan', cell.colSpan);
  421. newRow.appendChild(newCell);
  422. lastCell = cell;
  423. }
  424. }
  425. if (newRow.hasChildNodes()) {
  426. if (!before) {
  427. dom.insertAfter(newRow, rowElm);
  428. } else {
  429. rowElm.parentNode.insertBefore(newRow, rowElm);
  430. }
  431. }
  432. }
  433. function insertCol(before) {
  434. var posX, lastCell;
  435. // Find first/last column
  436. each(grid, function(row) {
  437. each(row, function(cell, x) {
  438. if (isCellSelected(cell)) {
  439. posX = x;
  440. if (before) {
  441. return false;
  442. }
  443. }
  444. });
  445. if (before) {
  446. return !posX;
  447. }
  448. });
  449. each(grid, function(row, y) {
  450. var cell, rowSpan, colSpan;
  451. if (!row[posX]) {
  452. return;
  453. }
  454. cell = row[posX].elm;
  455. if (cell != lastCell) {
  456. colSpan = getSpanVal(cell, 'colspan');
  457. rowSpan = getSpanVal(cell, 'rowspan');
  458. if (colSpan == 1) {
  459. if (!before) {
  460. dom.insertAfter(cloneCell(cell), cell);
  461. fillLeftDown(posX, y, rowSpan - 1, colSpan);
  462. } else {
  463. cell.parentNode.insertBefore(cloneCell(cell), cell);
  464. fillLeftDown(posX, y, rowSpan - 1, colSpan);
  465. }
  466. } else {
  467. setSpanVal(cell, 'colSpan', cell.colSpan + 1);
  468. }
  469. lastCell = cell;
  470. }
  471. });
  472. }
  473. function deleteCols() {
  474. var cols = [];
  475. // Get selected column indexes
  476. each(grid, function(row) {
  477. each(row, function(cell, x) {
  478. if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
  479. each(grid, function(row) {
  480. var cell = row[x].elm, colSpan;
  481. colSpan = getSpanVal(cell, 'colSpan');
  482. if (colSpan > 1) {
  483. setSpanVal(cell, 'colSpan', colSpan - 1);
  484. } else {
  485. dom.remove(cell);
  486. }
  487. });
  488. cols.push(x);
  489. }
  490. });
  491. });
  492. cleanup();
  493. }
  494. function deleteRows() {
  495. var rows;
  496. function deleteRow(tr) {
  497. var nextTr, pos, lastCell;
  498. nextTr = dom.getNext(tr, 'tr');
  499. // Move down row spanned cells
  500. each(tr.cells, function(cell) {
  501. var rowSpan = getSpanVal(cell, 'rowSpan');
  502. if (rowSpan > 1) {
  503. setSpanVal(cell, 'rowSpan', rowSpan - 1);
  504. pos = getPos(cell);
  505. fillLeftDown(pos.x, pos.y, 1, 1);
  506. }
  507. });
  508. // Delete cells
  509. pos = getPos(tr.cells[0]);
  510. each(grid[pos.y], function(cell) {
  511. var rowSpan;
  512. cell = cell.elm;
  513. if (cell != lastCell) {
  514. rowSpan = getSpanVal(cell, 'rowSpan');
  515. if (rowSpan <= 1) {
  516. dom.remove(cell);
  517. } else {
  518. setSpanVal(cell, 'rowSpan', rowSpan - 1);
  519. }
  520. lastCell = cell;
  521. }
  522. });
  523. }
  524. // Get selected rows and move selection out of scope
  525. rows = getSelectedRows();
  526. // Delete all selected rows
  527. each(rows.reverse(), function(tr) {
  528. deleteRow(tr);
  529. });
  530. cleanup();
  531. }
  532. function cutRows() {
  533. var rows = getSelectedRows();
  534. dom.remove(rows);
  535. cleanup();
  536. return rows;
  537. }
  538. function copyRows() {
  539. var rows = getSelectedRows();
  540. each(rows, function(row, i) {
  541. rows[i] = cloneNode(row, true);
  542. });
  543. return rows;
  544. }
  545. function pasteRows(rows, before) {
  546. var selectedRows = getSelectedRows(),
  547. targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
  548. targetCellCount = targetRow.cells.length;
  549. // Nothing to paste
  550. if (!rows) {
  551. return;
  552. }
  553. // Calc target cell count
  554. each(grid, function(row) {
  555. var match;
  556. targetCellCount = 0;
  557. each(row, function(cell) {
  558. if (cell.real) {
  559. targetCellCount += cell.colspan;
  560. }
  561. if (cell.elm.parentNode == targetRow) {
  562. match = 1;
  563. }
  564. });
  565. if (match) {
  566. return false;
  567. }
  568. });
  569. if (!before) {
  570. rows.reverse();
  571. }
  572. each(rows, function(row) {
  573. var i, cellCount = row.cells.length, cell;
  574. // Remove col/rowspans
  575. for (i = 0; i < cellCount; i++) {
  576. cell = row.cells[i];
  577. setSpanVal(cell, 'colSpan', 1);
  578. setSpanVal(cell, 'rowSpan', 1);
  579. }
  580. // Needs more cells
  581. for (i = cellCount; i < targetCellCount; i++) {
  582. row.appendChild(cloneCell(row.cells[cellCount - 1]));
  583. }
  584. // Needs less cells
  585. for (i = targetCellCount; i < cellCount; i++) {
  586. dom.remove(row.cells[i]);
  587. }
  588. // Add before/after
  589. if (before) {
  590. targetRow.parentNode.insertBefore(row, targetRow);
  591. } else {
  592. dom.insertAfter(row, targetRow);
  593. }
  594. });
  595. // Remove current selection
  596. dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
  597. }
  598. function getPos(target) {
  599. var pos;
  600. each(grid, function(row, y) {
  601. each(row, function(cell, x) {
  602. if (cell.elm == target) {
  603. pos = {x : x, y : y};
  604. return false;
  605. }
  606. });
  607. return !pos;
  608. });
  609. return pos;
  610. }
  611. function setStartCell(cell) {
  612. startPos = getPos(cell);
  613. }
  614. function findEndPos() {
  615. var maxX, maxY;
  616. maxX = maxY = 0;
  617. each(grid, function(row, y) {
  618. each(row, function(cell, x) {
  619. var colSpan, rowSpan;
  620. if (isCellSelected(cell)) {
  621. cell = grid[y][x];
  622. if (x > maxX) {
  623. maxX = x;
  624. }
  625. if (y > maxY) {
  626. maxY = y;
  627. }
  628. if (cell.real) {
  629. colSpan = cell.colspan - 1;
  630. rowSpan = cell.rowspan - 1;
  631. if (colSpan) {
  632. if (x + colSpan > maxX) {
  633. maxX = x + colSpan;
  634. }
  635. }
  636. if (rowSpan) {
  637. if (y + rowSpan > maxY) {
  638. maxY = y + rowSpan;
  639. }
  640. }
  641. }
  642. }
  643. });
  644. });
  645. return {x : maxX, y : maxY};
  646. }
  647. function setEndCell(cell) {
  648. var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
  649. endPos = getPos(cell);
  650. if (startPos && endPos) {
  651. // Get start/end positions
  652. startX = Math.min(startPos.x, endPos.x);
  653. startY = Math.min(startPos.y, endPos.y);
  654. endX = Math.max(startPos.x, endPos.x);
  655. endY = Math.max(startPos.y, endPos.y);
  656. // Expand end positon to include spans
  657. maxX = endX;
  658. maxY = endY;
  659. // Expand startX
  660. for (y = startY; y <= maxY; y++) {
  661. cell = grid[y][startX];
  662. if (!cell.real) {
  663. if (startX - (cell.colspan - 1) < startX) {
  664. startX -= cell.colspan - 1;
  665. }
  666. }
  667. }
  668. // Expand startY
  669. for (x = startX; x <= maxX; x++) {
  670. cell = grid[startY][x];
  671. if (!cell.real) {
  672. if (startY - (cell.rowspan - 1) < startY) {
  673. startY -= cell.rowspan - 1;
  674. }
  675. }
  676. }
  677. // Find max X, Y
  678. for (y = startY; y <= endY; y++) {
  679. for (x = startX; x <= endX; x++) {
  680. cell = grid[y][x];
  681. if (cell.real) {
  682. colSpan = cell.colspan - 1;
  683. rowSpan = cell.rowspan - 1;
  684. if (colSpan) {
  685. if (x + colSpan > maxX) {
  686. maxX = x + colSpan;
  687. }
  688. }
  689. if (rowSpan) {
  690. if (y + rowSpan > maxY) {
  691. maxY = y + rowSpan;
  692. }
  693. }
  694. }
  695. }
  696. }
  697. // Remove current selection
  698. dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
  699. // Add new selection
  700. for (y = startY; y <= maxY; y++) {
  701. for (x = startX; x <= maxX; x++) {
  702. if (grid[y][x]) {
  703. dom.addClass(grid[y][x].elm, 'mce-item-selected');
  704. }
  705. }
  706. }
  707. }
  708. }
  709. table = table || dom.getParent(selection.getStart(), 'table');
  710. buildGrid();
  711. selectedCell = dom.getParent(selection.getStart(), 'th,td');
  712. if (selectedCell) {
  713. startPos = getPos(selectedCell);
  714. endPos = findEndPos();
  715. selectedCell = getCell(startPos.x, startPos.y);
  716. }
  717. Tools.extend(this, {
  718. deleteTable: deleteTable,
  719. split: split,
  720. merge: merge,
  721. insertRow: insertRow,
  722. insertCol: insertCol,
  723. deleteCols: deleteCols,
  724. deleteRows: deleteRows,
  725. cutRows: cutRows,
  726. copyRows: copyRows,
  727. pasteRows: pasteRows,
  728. getPos: getPos,
  729. setStartCell: setStartCell,
  730. setEndCell: setEndCell
  731. });
  732. };
  733. });
  734. // Included from: js/tinymce/plugins/table/classes/Quirks.js
  735. /**
  736. * Quirks.js
  737. *
  738. * Copyright, Moxiecode Systems AB
  739. * Released under LGPL License.
  740. *
  741. * License: http://www.tinymce.com/license
  742. * Contributing: http://www.tinymce.com/contributing
  743. */
  744. /**
  745. * This class includes fixes for various browser quirks.
  746. *
  747. * @class tinymce.tableplugin.Quirks
  748. * @private
  749. */
  750. define("tinymce/tableplugin/Quirks", [
  751. "tinymce/util/VK",
  752. "tinymce/Env",
  753. "tinymce/util/Tools"
  754. ], function(VK, Env, Tools) {
  755. var each = Tools.each;
  756. function getSpanVal(td, name) {
  757. return parseInt(td.getAttribute(name) || 1, 10);
  758. }
  759. return function(editor) {
  760. /**
  761. * Fixed caret movement around tables on WebKit.
  762. */
  763. function moveWebKitSelection() {
  764. function eventHandler(e) {
  765. var key = e.keyCode;
  766. function handle(upBool, sourceNode) {
  767. var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
  768. var currentRow = editor.dom.getParent(sourceNode, 'tr');
  769. var siblingRow = currentRow[siblingDirection];
  770. if (siblingRow) {
  771. moveCursorToRow(editor, sourceNode, siblingRow, upBool);
  772. e.preventDefault();
  773. return true;
  774. } else {
  775. var tableNode = editor.dom.getParent(currentRow, 'table');
  776. var middleNode = currentRow.parentNode;
  777. var parentNodeName = middleNode.nodeName.toLowerCase();
  778. if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
  779. var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
  780. if (targetParent !== null) {
  781. return moveToRowInTarget(upBool, targetParent, sourceNode);
  782. }
  783. }
  784. return escapeTable(upBool, currentRow, siblingDirection, tableNode);
  785. }
  786. }
  787. function getTargetParent(upBool, topNode, secondNode, nodeName) {
  788. var tbodies = editor.dom.select('>' + nodeName, topNode);
  789. var position = tbodies.indexOf(secondNode);
  790. if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
  791. return getFirstHeadOrFoot(upBool, topNode);
  792. } else if (position === -1) {
  793. var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
  794. return tbodies[topOrBottom];
  795. } else {
  796. return tbodies[position + (upBool ? -1 : 1)];
  797. }
  798. }
  799. function getFirstHeadOrFoot(upBool, parent) {
  800. var tagName = upBool ? 'thead' : 'tfoot';
  801. var headOrFoot = editor.dom.select('>' + tagName, parent);
  802. return headOrFoot.length !== 0 ? headOrFoot[0] : null;
  803. }
  804. function moveToRowInTarget(upBool, targetParent, sourceNode) {
  805. var targetRow = getChildForDirection(targetParent, upBool);
  806. if (targetRow) {
  807. moveCursorToRow(editor, sourceNode, targetRow, upBool);
  808. }
  809. e.preventDefault();
  810. return true;
  811. }
  812. function escapeTable(upBool, currentRow, siblingDirection, table) {
  813. var tableSibling = table[siblingDirection];
  814. if (tableSibling) {
  815. moveCursorToStartOfElement(tableSibling);
  816. return true;
  817. } else {
  818. var parentCell = editor.dom.getParent(table, 'td,th');
  819. if (parentCell) {
  820. return handle(upBool, parentCell, e);
  821. } else {
  822. var backUpSibling = getChildForDirection(currentRow, !upBool);
  823. moveCursorToStartOfElement(backUpSibling);
  824. e.preventDefault();
  825. return false;
  826. }
  827. }
  828. }
  829. function getChildForDirection(parent, up) {
  830. var child = parent && parent[up ? 'lastChild' : 'firstChild'];
  831. // BR is not a valid table child to return in this case we return the table cell
  832. return child && child.nodeName === 'BR' ? editor.dom.getParent(child, 'td,th') : child;
  833. }
  834. function moveCursorToStartOfElement(n) {
  835. editor.selection.setCursorLocation(n, 0);
  836. }
  837. function isVerticalMovement() {
  838. return key == VK.UP || key == VK.DOWN;
  839. }
  840. function isInTable(editor) {
  841. var node = editor.selection.getNode();
  842. var currentRow = editor.dom.getParent(node, 'tr');
  843. return currentRow !== null;
  844. }
  845. function columnIndex(column) {
  846. var colIndex = 0;
  847. var c = column;
  848. while (c.previousSibling) {
  849. c = c.previousSibling;
  850. colIndex = colIndex + getSpanVal(c, "colspan");
  851. }
  852. return colIndex;
  853. }
  854. function findColumn(rowElement, columnIndex) {
  855. var c = 0, r = 0;
  856. each(rowElement.children, function(cell, i) {
  857. c = c + getSpanVal(cell, "colspan");
  858. r = i;
  859. if (c > columnIndex) {
  860. return false;
  861. }
  862. });
  863. return r;
  864. }
  865. function moveCursorToRow(ed, node, row, upBool) {
  866. var srcColumnIndex = columnIndex(editor.dom.getParent(node, 'td,th'));
  867. var tgtColumnIndex = findColumn(row, srcColumnIndex);
  868. var tgtNode = row.childNodes[tgtColumnIndex];
  869. var rowCellTarget = getChildForDirection(tgtNode, upBool);
  870. moveCursorToStartOfElement(rowCellTarget || tgtNode);
  871. }
  872. function shouldFixCaret(preBrowserNode) {
  873. var newNode = editor.selection.getNode();
  874. var newParent = editor.dom.getParent(newNode, 'td,th');
  875. var oldParent = editor.dom.getParent(preBrowserNode, 'td,th');
  876. return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent);
  877. }
  878. function checkSameParentTable(nodeOne, NodeTwo) {
  879. return editor.dom.getParent(nodeOne, 'TABLE') === editor.dom.getParent(NodeTwo, 'TABLE');
  880. }
  881. if (isVerticalMovement() && isInTable(editor)) {
  882. var preBrowserNode = editor.selection.getNode();
  883. setTimeout(function() {
  884. if (shouldFixCaret(preBrowserNode)) {
  885. handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
  886. }
  887. }, 0);
  888. }
  889. }
  890. editor.on('KeyDown', function(e) {
  891. eventHandler(e);
  892. });
  893. }
  894. function fixBeforeTableCaretBug() {
  895. // Checks if the selection/caret is at the start of the specified block element
  896. function isAtStart(rng, par) {
  897. var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
  898. rng2.setStartBefore(par);
  899. rng2.setEnd(rng.endContainer, rng.endOffset);
  900. elm = doc.createElement('body');
  901. elm.appendChild(rng2.cloneContents());
  902. // Check for text characters of other elements that should be treated as content
  903. return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length === 0;
  904. }
  905. // Fixes an bug where it's impossible to place the caret before a table in Gecko
  906. // this fix solves it by detecting when the caret is at the beginning of such a table
  907. // and then manually moves the caret infront of the table
  908. editor.on('KeyDown', function(e) {
  909. var rng, table, dom = editor.dom;
  910. // On gecko it's not possible to place the caret before a table
  911. if (e.keyCode == 37 || e.keyCode == 38) {
  912. rng = editor.selection.getRng();
  913. table = dom.getParent(rng.startContainer, 'table');
  914. if (table && editor.getBody().firstChild == table) {
  915. if (isAtStart(rng, table)) {
  916. rng = dom.createRng();
  917. rng.setStartBefore(table);
  918. rng.setEndBefore(table);
  919. editor.selection.setRng(rng);
  920. e.preventDefault();
  921. }
  922. }
  923. }
  924. });
  925. }
  926. // Fixes an issue on Gecko where it's impossible to place the caret behind a table
  927. // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
  928. function fixTableCaretPos() {
  929. editor.on('KeyDown SetContent VisualAid', function() {
  930. var last;
  931. // Skip empty text nodes from the end
  932. for (last = editor.getBody().lastChild; last; last = last.previousSibling) {
  933. if (last.nodeType == 3) {
  934. if (last.nodeValue.length > 0) {
  935. break;
  936. }
  937. } else if (last.nodeType == 1 && !last.getAttribute('data-mce-bogus')) {
  938. break;
  939. }
  940. }
  941. if (last && last.nodeName == 'TABLE') {
  942. if (editor.settings.forced_root_block) {
  943. editor.dom.add(
  944. editor.getBody(),
  945. editor.settings.forced_root_block,
  946. editor.settings.forced_root_block_attrs,
  947. Env.ie && Env.ie < 11 ? '&nbsp;' : '<br data-mce-bogus="1" />'
  948. );
  949. } else {
  950. editor.dom.add(editor.getBody(), 'br', {'data-mce-bogus': '1'});
  951. }
  952. }
  953. });
  954. editor.on('PreProcess', function(o) {
  955. var last = o.node.lastChild;
  956. if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
  957. (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) &&
  958. last.previousSibling && last.previousSibling.nodeName == "TABLE") {
  959. editor.dom.remove(last);
  960. }
  961. });
  962. }
  963. // this nasty hack is here to work around some WebKit selection bugs.
  964. function fixTableCellSelection() {
  965. function tableCellSelected(ed, rng, n, currentCell) {
  966. // The decision of when a table cell is selected is somewhat involved. The fact that this code is
  967. // required is actually a pointer to the root cause of this bug. A cell is selected when the start
  968. // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
  969. // or the parent of the table (in the case of the selection containing the last cell of a table).
  970. var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE');
  971. var tableParent, allOfCellSelected, tableCellSelection;
  972. if (table) {
  973. tableParent = table.parentNode;
  974. }
  975. allOfCellSelected = rng.startContainer.nodeType == TEXT_NODE &&
  976. rng.startOffset === 0 &&
  977. rng.endOffset === 0 &&
  978. currentCell &&
  979. (n.nodeName == "TR" || n == tableParent);
  980. tableCellSelection = (n.nodeName == "TD" || n.nodeName == "TH") && !currentCell;
  981. return allOfCellSelected || tableCellSelection;
  982. }
  983. function fixSelection() {
  984. var rng = editor.selection.getRng();
  985. var n = editor.selection.getNode();
  986. var currentCell = editor.dom.getParent(rng.startContainer, 'TD,TH');
  987. if (!tableCellSelected(editor, rng, n, currentCell)) {
  988. return;
  989. }
  990. if (!currentCell) {
  991. currentCell = n;
  992. }
  993. // Get the very last node inside the table cell
  994. var end = currentCell.lastChild;
  995. while (end.lastChild) {
  996. end = end.lastChild;
  997. }
  998. // Select the entire table cell. Nothing outside of the table cell should be selected.
  999. rng.setEnd(end, end.nodeValue.length);
  1000. editor.selection.setRng(rng);
  1001. }
  1002. editor.on('KeyDown', function() {
  1003. fixSelection();
  1004. });
  1005. editor.on('MouseDown', function(e) {
  1006. if (e.button != 2) {
  1007. fixSelection();
  1008. }
  1009. });
  1010. }
  1011. /**
  1012. * Delete table if all cells are selected.
  1013. */
  1014. function deleteTable() {
  1015. editor.on('keydown', function(e) {
  1016. if ((e.keyCode == VK.DELETE || e.keyCode == VK.BACKSPACE) && !e.isDefaultPrevented()) {
  1017. var table = editor.dom.getParent(editor.selection.getStart(), 'table');
  1018. if (table) {
  1019. var cells = editor.dom.select('td,th', table), i = cells.length;
  1020. while (i--) {
  1021. if (!editor.dom.hasClass(cells[i], 'mce-item-selected')) {
  1022. return;
  1023. }
  1024. }
  1025. e.preventDefault();
  1026. editor.execCommand('mceTableDelete');
  1027. }
  1028. }
  1029. });
  1030. }
  1031. deleteTable();
  1032. if (Env.webkit) {
  1033. moveWebKitSelection();
  1034. fixTableCellSelection();
  1035. }
  1036. if (Env.gecko) {
  1037. fixBeforeTableCaretBug();
  1038. fixTableCaretPos();
  1039. }
  1040. if (Env.ie > 10) {
  1041. fixBeforeTableCaretBug();
  1042. fixTableCaretPos();
  1043. }
  1044. };
  1045. });
  1046. // Included from: js/tinymce/plugins/table/classes/CellSelection.js
  1047. /**
  1048. * CellSelection.js
  1049. *
  1050. * Copyright, Moxiecode Systems AB
  1051. * Released under LGPL License.
  1052. *
  1053. * License: http://www.tinymce.com/license
  1054. * Contributing: http://www.tinymce.com/contributing
  1055. */
  1056. /**
  1057. * This class handles table cell selection by faking it using a css class that gets applied
  1058. * to cells when dragging the mouse from one cell to another.
  1059. *
  1060. * @class tinymce.tableplugin.CellSelection
  1061. * @private
  1062. */
  1063. define("tinymce/tableplugin/CellSelection", [
  1064. "tinymce/tableplugin/TableGrid",
  1065. "tinymce/dom/TreeWalker",
  1066. "tinymce/util/Tools"
  1067. ], function(TableGrid, TreeWalker, Tools) {
  1068. return function(editor) {
  1069. var dom = editor.dom, tableGrid, startCell, startTable, hasCellSelection = true;
  1070. function clear() {
  1071. // Restore selection possibilities
  1072. editor.getBody().style.webkitUserSelect = '';
  1073. if (hasCellSelection) {
  1074. editor.dom.removeClass(
  1075. editor.dom.select('td.mce-item-selected,th.mce-item-selected'),
  1076. 'mce-item-selected'
  1077. );
  1078. hasCellSelection = false;
  1079. }
  1080. }
  1081. function cellSelectionHandler(e) {
  1082. var sel, table, target = e.target;
  1083. if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
  1084. table = dom.getParent(target, 'table');
  1085. if (table == startTable) {
  1086. if (!tableGrid) {
  1087. tableGrid = new TableGrid(editor, table);
  1088. tableGrid.setStartCell(startCell);
  1089. editor.getBody().style.webkitUserSelect = 'none';
  1090. }
  1091. tableGrid.setEndCell(target);
  1092. hasCellSelection = true;
  1093. }
  1094. // Remove current selection
  1095. sel = editor.selection.getSel();
  1096. try {
  1097. if (sel.removeAllRanges) {
  1098. sel.removeAllRanges();
  1099. } else {
  1100. sel.empty();
  1101. }
  1102. } catch (ex) {
  1103. // IE9 might throw errors here
  1104. }
  1105. e.preventDefault();
  1106. }
  1107. }
  1108. // Add cell selection logic
  1109. editor.on('MouseDown', function(e) {
  1110. if (e.button != 2) {
  1111. clear();
  1112. startCell = dom.getParent(e.target, 'td,th');
  1113. startTable = dom.getParent(startCell, 'table');
  1114. }
  1115. });
  1116. editor.on('mouseover', cellSelectionHandler);
  1117. editor.on('remove', function() {
  1118. dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
  1119. });
  1120. editor.on('MouseUp', function() {
  1121. var rng, sel = editor.selection, selectedCells, walker, node, lastNode, endNode;
  1122. function setPoint(node, start) {
  1123. var walker = new TreeWalker(node, node);
  1124. do {
  1125. // Text node
  1126. if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
  1127. if (start) {
  1128. rng.setStart(node, 0);
  1129. } else {
  1130. rng.setEnd(node, node.nodeValue.length);
  1131. }
  1132. return;
  1133. }
  1134. // BR element
  1135. if (node.nodeName == 'BR') {
  1136. if (start) {
  1137. rng.setStartBefore(node);
  1138. } else {
  1139. rng.setEndBefore(node);
  1140. }
  1141. return;
  1142. }
  1143. } while ((node = (start ? walker.next() : walker.prev())));
  1144. }
  1145. // Move selection to startCell
  1146. if (startCell) {
  1147. if (tableGrid) {
  1148. editor.getBody().style.webkitUserSelect = '';
  1149. }
  1150. // Try to expand text selection as much as we can only Gecko supports cell selection
  1151. selectedCells = dom.select('td.mce-item-selected,th.mce-item-selected');
  1152. if (selectedCells.length > 0) {
  1153. rng = dom.createRng();
  1154. node = selectedCells[0];
  1155. endNode = selectedCells[selectedCells.length - 1];
  1156. rng.setStartBefore(node);
  1157. rng.setEndAfter(node);
  1158. setPoint(node, 1);
  1159. walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
  1160. do {
  1161. if (node.nodeName == 'TD' || node.nodeName == 'TH') {
  1162. if (!dom.hasClass(node, 'mce-item-selected')) {
  1163. break;
  1164. }
  1165. lastNode = node;
  1166. }
  1167. } while ((node = walker.next()));
  1168. setPoint(lastNode);
  1169. sel.setRng(rng);
  1170. }
  1171. editor.nodeChanged();
  1172. startCell = tableGrid = startTable = null;
  1173. }
  1174. });
  1175. editor.on('KeyUp Drop', function() {
  1176. clear();
  1177. startCell = tableGrid = startTable = null;
  1178. });
  1179. return {
  1180. clear: clear
  1181. };
  1182. };
  1183. });
  1184. // Included from: js/tinymce/plugins/table/classes/Plugin.js
  1185. /**
  1186. * Plugin.js
  1187. *
  1188. * Copyright, Moxiecode Systems AB
  1189. * Released under LGPL License.
  1190. *
  1191. * License: http://www.tinymce.com/license
  1192. * Contributing: http://www.tinymce.com/contributing
  1193. */
  1194. /**
  1195. * This class contains all core logic for the table plugin.
  1196. *
  1197. * @class tinymce.tableplugin.Plugin
  1198. * @private
  1199. */
  1200. define("tinymce/tableplugin/Plugin", [
  1201. "tinymce/tableplugin/TableGrid",
  1202. "tinymce/tableplugin/Quirks",
  1203. "tinymce/tableplugin/CellSelection",
  1204. "tinymce/util/Tools",
  1205. "tinymce/dom/TreeWalker",
  1206. "tinymce/Env",
  1207. "tinymce/PluginManager"
  1208. ], function(TableGrid, Quirks, CellSelection, Tools, TreeWalker, Env, PluginManager) {
  1209. var each = Tools.each;
  1210. function Plugin(editor) {
  1211. var winMan, clipboardRows, self = this; // Might be selected cells on reload
  1212. function removePxSuffix(size) {
  1213. return size ? size.replace(/px$/, '') : "";
  1214. }
  1215. function addSizeSuffix(size) {
  1216. if (/^[0-9]+$/.test(size)) {
  1217. size += "px";
  1218. }
  1219. return size;
  1220. }
  1221. function unApplyAlign(elm) {
  1222. each('left center right'.split(' '), function(name) {
  1223. editor.formatter.remove('align' + name, {}, elm);
  1224. });
  1225. }
  1226. function unApplyVAlign(elm) {
  1227. each('top middle bottom'.split(' '), function(name) {
  1228. editor.formatter.remove('valign' + name, {}, elm);
  1229. });
  1230. }
  1231. function tableDialog() {
  1232. var dom = editor.dom, tableElm, colsCtrl, rowsCtrl, data;
  1233. tableElm = dom.getParent(editor.selection.getStart(), 'table');
  1234. data = {
  1235. width: removePxSuffix(dom.getStyle(tableElm, 'width') || dom.getAttrib(tableElm, 'width')),
  1236. height: removePxSuffix(dom.getStyle(tableElm, 'height') || dom.getAttrib(tableElm, 'height')),
  1237. cellspacing: tableElm ? dom.getAttrib(tableElm, 'cellspacing') : '',
  1238. cellpadding: tableElm ? dom.getAttrib(tableElm, 'cellpadding') : '',
  1239. border: tableElm ? dom.getAttrib(tableElm, 'border') : '',
  1240. caption: !!dom.select('caption', tableElm)[0]
  1241. };
  1242. each('left center right'.split(' '), function(name) {
  1243. if (editor.formatter.matchNode(tableElm, 'align' + name)) {
  1244. data.align = name;
  1245. }
  1246. });
  1247. if (!tableElm) {
  1248. colsCtrl = {label: 'Cols', name: 'cols'};
  1249. rowsCtrl = {label: 'Rows', name: 'rows'};
  1250. }
  1251. editor.windowManager.open({
  1252. title: "Table properties",
  1253. items: {
  1254. type: 'form',
  1255. layout: 'grid',
  1256. columns: 2,
  1257. data: data,
  1258. defaults: {
  1259. type: 'textbox',
  1260. maxWidth: 50
  1261. },
  1262. items: [
  1263. colsCtrl,
  1264. rowsCtrl,
  1265. {label: 'Width', name: 'width'},
  1266. {label: 'Height', name: 'height'},
  1267. {label: 'Cell spacing', name: 'cellspacing'},
  1268. {label: 'Cell padding', name: 'cellpadding'},
  1269. {label: 'Border', name: 'border'},
  1270. {label: 'Caption', name: 'caption', type: 'checkbox'},
  1271. {
  1272. label: 'Alignment',
  1273. minWidth: 90,
  1274. name: 'align',
  1275. type: 'listbox',
  1276. text: 'None',
  1277. maxWidth: null,
  1278. values: [
  1279. {text: 'None', value: ''},
  1280. {text: 'Left', value: 'left'},
  1281. {text: 'Center', value: 'center'},
  1282. {text: 'Right', value: 'right'}
  1283. ]
  1284. }
  1285. ]
  1286. },
  1287. onsubmit: function() {
  1288. var data = this.toJSON(), captionElm;
  1289. editor.undoManager.transact(function() {
  1290. if (!tableElm) {
  1291. tableElm = insertTable(data.cols || 1, data.rows || 1);
  1292. }
  1293. editor.dom.setAttribs(tableElm, {
  1294. cellspacing: data.cellspacing,
  1295. cellpadding: data.cellpadding,
  1296. border: data.border
  1297. });
  1298. editor.dom.setStyles(tableElm, {
  1299. width: addSizeSuffix(data.width),
  1300. height: addSizeSuffix(data.height)
  1301. });
  1302. // Toggle caption on/off
  1303. captionElm = dom.select('caption', tableElm)[0];
  1304. if (captionElm && !data.caption) {
  1305. dom.remove(captionElm);
  1306. }
  1307. if (!captionElm && data.caption) {
  1308. captionElm = dom.create('caption');
  1309. captionElm.innerHTML = !Env.ie ? '<br data-mce-bogus="1"/>' : '\u00a0';
  1310. tableElm.insertBefore(captionElm, tableElm.firstChild);
  1311. }
  1312. unApplyAlign(tableElm);
  1313. if (data.align) {
  1314. editor.formatter.apply('align' + data.align, {}, tableElm);
  1315. }
  1316. editor.focus();
  1317. editor.addVisual();
  1318. });
  1319. }
  1320. });
  1321. }
  1322. function mergeDialog(grid, cell) {
  1323. editor.windowManager.open({
  1324. title: "Merge cells",
  1325. body: [
  1326. {label: 'Cols', name: 'cols', type: 'textbox', size: 10},
  1327. {label: 'Rows', name: 'rows', type: 'textbox', size: 10}
  1328. ],
  1329. onsubmit: function() {
  1330. var data = this.toJSON();
  1331. editor.undoManager.transact(function() {
  1332. grid.merge(cell, data.cols, data.rows);
  1333. });
  1334. }
  1335. });
  1336. }
  1337. function cellDialog() {
  1338. var dom = editor.dom, cellElm, data, cells = [];
  1339. // Get selected cells or the current cell
  1340. cells = editor.dom.select('td.mce-item-selected,th.mce-item-selected');
  1341. cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
  1342. if (!cells.length && cellElm) {
  1343. cells.push(cellElm);
  1344. }
  1345. cellElm = cellElm || cells[0];
  1346. if (!cellElm) {
  1347. // If this element is null, return now to avoid crashing.
  1348. return;
  1349. }
  1350. data = {
  1351. width: removePxSuffix(dom.getStyle(cellElm, 'width') || dom.getAttrib(cellElm, 'width')),
  1352. height: removePxSuffix(dom.getStyle(cellElm, 'height') || dom.getAttrib(cellElm, 'height')),
  1353. scope: dom.getAttrib(cellElm, 'scope')
  1354. };
  1355. data.type = cellElm.nodeName.toLowerCase();
  1356. each('left center right'.split(' '), function(name) {
  1357. if (editor.formatter.matchNode(cellElm, 'align' + name)) {
  1358. data.align = name;
  1359. }
  1360. });
  1361. each('top middle bottom'.split(' '), function(name) {
  1362. if (editor.formatter.matchNode(cellElm, 'valign' + name)) {
  1363. data.valign = name;
  1364. }
  1365. });
  1366. editor.windowManager.open({
  1367. title: "Cell properties",
  1368. items: {
  1369. type: 'form',
  1370. data: data,
  1371. layout: 'grid',
  1372. columns: 2,
  1373. defaults: {
  1374. type: 'textbox',
  1375. maxWidth: 50
  1376. },
  1377. items: [
  1378. {label: 'Width', name: 'width'},
  1379. {label: 'Height', name: 'height'},
  1380. {
  1381. label: 'Cell type',
  1382. name: 'type',
  1383. type: 'listbox',
  1384. text: 'None',
  1385. minWidth: 90,
  1386. maxWidth: null,
  1387. values: [
  1388. {text: 'Cell', value: 'td'},
  1389. {text: 'Header cell', value: 'th'}
  1390. ]
  1391. },
  1392. {
  1393. label: 'Scope',
  1394. name: 'scope',
  1395. type: 'listbox',
  1396. text: 'None',
  1397. minWidth: 90,
  1398. maxWidth: null,
  1399. values: [
  1400. {text: 'None', value: ''},
  1401. {text: 'Row', value: 'row'},
  1402. {text: 'Column', value: 'col'},
  1403. {text: 'Row group', value: 'rowgroup'},
  1404. {text: 'Column group', value: 'colgroup'}
  1405. ]
  1406. },
  1407. {
  1408. label: 'H Align',
  1409. name: 'align',
  1410. type: 'listbox',
  1411. text: 'None',
  1412. minWidth: 90,
  1413. maxWidth: null,
  1414. values: [
  1415. {text: 'None', value: ''},
  1416. {text: 'Left', value: 'left'},
  1417. {text: 'Center', value: 'center'},
  1418. {text: 'Right', value: 'right'}
  1419. ]
  1420. },
  1421. {
  1422. label: 'V Align',
  1423. name: 'valign',
  1424. type: 'listbox',
  1425. text: 'None',
  1426. minWidth: 90,
  1427. maxWidth: null,
  1428. values: [
  1429. {text: 'None', value: ''},
  1430. {text: 'Top', value: 'top'},
  1431. {text: 'Middle', value: 'middle'},
  1432. {text: 'Bottom', value: 'bottom'}
  1433. ]
  1434. }
  1435. ]
  1436. },
  1437. onsubmit: function() {
  1438. var data = this.toJSON();
  1439. editor.undoManager.transact(function() {
  1440. each(cells, function(cellElm) {
  1441. editor.dom.setAttrib(cellElm, 'scope', data.scope);
  1442. editor.dom.setStyles(cellElm, {
  1443. width: addSizeSuffix(data.width),
  1444. height: addSizeSuffix(data.height)
  1445. });
  1446. // Switch cell type
  1447. if (data.type && cellElm.nodeName.toLowerCase() != data.type) {
  1448. cellElm = dom.rename(cellElm, data.type);
  1449. }
  1450. // Apply/remove alignment
  1451. unApplyAlign(cellElm);
  1452. if (data.align) {
  1453. editor.formatter.apply('align' + data.align, {}, cellElm);
  1454. }
  1455. // Apply/remove vertical alignment
  1456. unApplyVAlign(cellElm);
  1457. if (data.valign) {
  1458. editor.formatter.apply('valign' + data.valign, {}, cellElm);
  1459. }
  1460. });
  1461. editor.focus();
  1462. });
  1463. }
  1464. });
  1465. }
  1466. function rowDialog() {
  1467. var dom = editor.dom, tableElm, cellElm, rowElm, data, rows = [];
  1468. tableElm = editor.dom.getParent(editor.selection.getStart(), 'table');
  1469. cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
  1470. each(tableElm.rows, function(row) {
  1471. each(row.cells, function(cell) {
  1472. if (dom.hasClass(cell, 'mce-item-selected') || cell == cellElm) {
  1473. rows.push(row);
  1474. return false;
  1475. }
  1476. });
  1477. });
  1478. rowElm = rows[0];
  1479. if (!rowElm) {
  1480. // If this element is null, return now to avoid crashing.
  1481. return;
  1482. }
  1483. data = {
  1484. height: removePxSuffix(dom.getStyle(rowElm, 'height') || dom.getAttrib(rowElm, 'height')),
  1485. scope: dom.getAttrib(rowElm, 'scope')
  1486. };
  1487. data.type = rowElm.parentNode.nodeName.toLowerCase();
  1488. each('left center right'.split(' '), function(name) {
  1489. if (editor.formatter.matchNode(rowElm, 'align' + name)) {
  1490. data.align = name;
  1491. }
  1492. });
  1493. editor.windowManager.open({
  1494. title: "Row properties",
  1495. items: {
  1496. type: 'form',
  1497. data: data,
  1498. columns: 2,
  1499. defaults: {
  1500. type: 'textbox'
  1501. },
  1502. items: [
  1503. {
  1504. type: 'listbox',
  1505. name: 'type',
  1506. label: 'Row type',
  1507. text: 'None',
  1508. maxWidth: null,
  1509. values: [
  1510. {text: 'Header', value: 'thead'},
  1511. {text: 'Body', value: 'tbody'},
  1512. {text: 'Footer', value: 'tfoot'}
  1513. ]
  1514. },
  1515. {
  1516. type: 'listbox',
  1517. name: 'align',
  1518. label: 'Alignment',
  1519. text: 'None',
  1520. maxWidth: null,
  1521. values: [
  1522. {text: 'None', value: ''},
  1523. {text: 'Left', value: 'left'},
  1524. {text: 'Center', value: 'center'},
  1525. {text: 'Right', value: 'right'}
  1526. ]
  1527. },
  1528. {label: 'Height', name: 'height'}
  1529. ]
  1530. },
  1531. onsubmit: function() {
  1532. var data = this.toJSON(), tableElm, oldParentElm, parentElm;
  1533. editor.undoManager.transact(function() {
  1534. var toType = data.type;
  1535. each(rows, function(rowElm) {
  1536. editor.dom.setAttrib(rowElm, 'scope', data.scope);
  1537. editor.dom.setStyles(rowElm, {
  1538. height: addSizeSuffix(data.height)
  1539. });
  1540. if (toType != rowElm.parentNode.nodeName.toLowerCase()) {
  1541. tableElm = dom.getParent(rowElm, 'table');
  1542. oldParentElm = rowElm.parentNode;
  1543. parentElm = dom.select(toType, tableElm)[0];
  1544. if (!parentElm) {
  1545. parentElm = dom.create(toType);
  1546. if (tableElm.firstChild) {
  1547. tableElm.insertBefore(parentElm, tableElm.firstChild);
  1548. } else {
  1549. tableElm.appendChild(parentElm);
  1550. }
  1551. }
  1552. parentElm.appendChild(rowElm);
  1553. if (!oldParentElm.hasChildNodes()) {
  1554. dom.remove(oldParentElm);
  1555. }
  1556. }
  1557. // Apply/remove alignment
  1558. unApplyAlign(rowElm);
  1559. if (data.align) {
  1560. editor.formatter.apply('align' + data.align, {}, rowElm);
  1561. }
  1562. });
  1563. editor.focus();
  1564. });
  1565. }
  1566. });
  1567. }
  1568. function cmd(command) {
  1569. return function() {
  1570. editor.execCommand(command);
  1571. };
  1572. }
  1573. function insertTable(cols, rows) {
  1574. var y, x, html;
  1575. html = '<table id="__mce"><tbody>';
  1576. for (y = 0; y < rows; y++) {
  1577. html += '<tr>';
  1578. for (x = 0; x < cols; x++) {
  1579. html += '<td>' + (Env.ie ? " " : '<br>') + '</td>';
  1580. }
  1581. html += '</tr>';
  1582. }
  1583. html += '</tbody></table>';
  1584. editor.insertContent(html);
  1585. var tableElm = editor.dom.get('__mce');
  1586. editor.dom.setAttrib(tableElm, 'id', null);
  1587. return tableElm;
  1588. }
  1589. function handleDisabledState(ctrl, selector) {
  1590. function bindStateListener() {
  1591. ctrl.disabled(!editor.dom.getParent(editor.selection.getStart(), selector));
  1592. editor.selection.selectorChanged(selector, function(state) {
  1593. ctrl.disabled(!state);
  1594. });
  1595. }
  1596. if (editor.initialized) {
  1597. bindStateListener();
  1598. } else {
  1599. editor.on('init', bindStateListener);
  1600. }
  1601. }
  1602. function postRender() {
  1603. /*jshint validthis:true*/
  1604. handleDisabledState(this, 'table');
  1605. }
  1606. function postRenderCell() {
  1607. /*jshint validthis:true*/
  1608. handleDisabledState(this, 'td,th');
  1609. }
  1610. function generateTableGrid() {
  1611. var html = '';
  1612. html = '<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';
  1613. for (var y = 0; y < 10; y++) {
  1614. html += '<tr>';
  1615. for (var x = 0; x < 10; x++) {
  1616. html += '<td role="gridcell" tabindex="-1"><a id="mcegrid' + (y * 10 + x) + '" href="#" ' +
  1617. 'data-mce-x="' + x + '" data-mce-y="' + y + '"></a></td>';
  1618. }
  1619. html += '</tr>';
  1620. }
  1621. html += '</table>';
  1622. html += '<div class="mce-text-center" role="presentation">1 x 1</div>';
  1623. return html;
  1624. }
  1625. function selectGrid(tx, ty, control) {
  1626. var table = control.getEl().getElementsByTagName('table')[0];
  1627. var x, y, focusCell, cell, active;
  1628. var rtl = control.isRtl() || control.parent().rel == 'tl-tr';
  1629. table.nextSibling.innerHTML = (tx + 1) + ' x ' + (ty + 1);
  1630. if (rtl) {
  1631. tx = 9 - tx;
  1632. }
  1633. for (y = 0; y < 10; y++) {
  1634. for (x = 0; x < 10; x++) {
  1635. cell = table.rows[y].childNodes[x].firstChild;
  1636. active = (rtl ? x >= tx : x <= tx) && y <= ty;
  1637. editor.dom.toggleClass(cell, 'mce-active', active);
  1638. if (active) {
  1639. focusCell = cell;
  1640. }
  1641. }
  1642. }
  1643. return focusCell.parentNode;
  1644. }
  1645. if (editor.settings.table_grid === false) {
  1646. editor.addMenuItem('inserttable', {
  1647. text: 'Insert table',
  1648. icon: 'table',
  1649. context: 'table',
  1650. onclick: tableDialog
  1651. });
  1652. } else {
  1653. editor.addMenuItem('inserttable', {
  1654. text: 'Insert table',
  1655. icon: 'table',
  1656. context: 'table',
  1657. ariaHideMenu: true,
  1658. onclick: function(e) {
  1659. if (e.aria) {
  1660. this.parent().hideAll();
  1661. e.stopImmediatePropagation();
  1662. tableDialog();
  1663. }
  1664. },
  1665. onshow: function() {
  1666. selectGrid(0, 0, this.menu.items()[0]);
  1667. },
  1668. onhide: function() {
  1669. var elements = this.menu.items()[0].getEl().getElementsByTagName('a');
  1670. editor.dom.removeClass(elements, 'mce-active');
  1671. editor.dom.addClass(elements[0], 'mce-active');
  1672. },
  1673. menu: [
  1674. {
  1675. type: 'container',
  1676. html: generateTableGrid(),
  1677. onPostRender: function() {
  1678. this.lastX = this.lastY = 0;
  1679. },
  1680. onmousemove: function(e) {
  1681. var target = e.target, x, y;
  1682. if (target.tagName.toUpperCase() == 'A') {
  1683. x = parseInt(target.getAttribute('data-mce-x'), 10);
  1684. y = parseInt(target.getAttribute('data-mce-y'), 10);
  1685. if (this.isRtl() || this.parent().rel == 'tl-tr') {
  1686. x = 9 - x;
  1687. }
  1688. if (x !== this.lastX || y !== this.lastY) {
  1689. selectGrid(x, y, e.control);
  1690. this.lastX = x;
  1691. this.lastY = y;
  1692. }
  1693. }
  1694. },
  1695. onkeydown: function(e) {
  1696. var x = this.lastX, y = this.lastY, isHandled;
  1697. switch (e.keyCode) {
  1698. case 37: // DOM_VK_LEFT
  1699. if (x > 0) {
  1700. x--;
  1701. isHandled = true;
  1702. }
  1703. break;
  1704. case 39: // DOM_VK_RIGHT
  1705. isHandled = true;
  1706. if (x < 9) {
  1707. x++;
  1708. }
  1709. break;
  1710. case 38: // DOM_VK_UP
  1711. isHandled = true;
  1712. if (y > 0) {
  1713. y--;
  1714. }
  1715. break;
  1716. case 40: // DOM_VK_DOWN
  1717. isHandled = true;
  1718. if (y < 9) {
  1719. y++;
  1720. }
  1721. break;
  1722. }
  1723. if (isHandled) {
  1724. e.preventDefault();
  1725. e.stopPropagation();
  1726. selectGrid(x, y, e.control).focus();
  1727. this.lastX = x;
  1728. this.lastY = y;
  1729. }
  1730. },
  1731. onclick: function(e) {
  1732. if (e.target.tagName.toUpperCase() == 'A') {
  1733. e.preventDefault();
  1734. e.stopPropagation();
  1735. this.parent().cancel();
  1736. insertTable(this.lastX + 1, this.lastY + 1);
  1737. }
  1738. }
  1739. }
  1740. ]
  1741. });
  1742. }
  1743. editor.addMenuItem('tableprops', {
  1744. text: 'Table properties',
  1745. context: 'table',
  1746. onPostRender: postRender,
  1747. onclick: tableDialog
  1748. });
  1749. editor.addMenuItem('deletetable', {
  1750. text: 'Delete table',
  1751. context: 'table',
  1752. onPostRender: postRender,
  1753. cmd: 'mceTableDelete'
  1754. });
  1755. editor.addMenuItem('cell', {
  1756. separator: 'before',
  1757. text: 'Cell',
  1758. context: 'table',
  1759. menu: [
  1760. {text: 'Cell properties', onclick: cmd('mceTableCellProps'), onPostRender: postRenderCell},
  1761. {text: 'Merge cells', onclick: cmd('mceTableMergeCells'), onPostRender: postRenderCell},
  1762. {text: 'Split cell', onclick: cmd('mceTableSplitCells'), onPostRender: postRenderCell}
  1763. ]
  1764. });
  1765. editor.addMenuItem('row', {
  1766. text: 'Row',
  1767. context: 'table',
  1768. menu: [
  1769. {text: 'Insert row before', onclick: cmd('mceTableInsertRowBefore'), onPostRender: postRenderCell},
  1770. {text: 'Insert row after', onclick: cmd('mceTableInsertRowAfter'), onPostRender: postRenderCell},
  1771. {text: 'Delete row', onclick: cmd('mceTableDeleteRow'), onPostRender: postRenderCell},
  1772. {text: 'Row properties', onclick: cmd('mceTableRowProps'), onPostRender: postRenderCell},
  1773. {text: '-'},
  1774. {text: 'Cut row', onclick: cmd('mceTableCutRow'), onPostRender: postRenderCell},
  1775. {text: 'Copy row', onclick: cmd('mceTableCopyRow'), onPostRender: postRenderCell},
  1776. {text: 'Paste row before', onclick: cmd('mceTablePasteRowBefore'), onPostRender: postRenderCell},
  1777. {text: 'Paste row after', onclick: cmd('mceTablePasteRowAfter'), onPostRender: postRenderCell}
  1778. ]
  1779. });
  1780. editor.addMenuItem('column', {
  1781. text: 'Column',
  1782. context: 'table',
  1783. menu: [
  1784. {text: 'Insert column before', onclick: cmd('mceTableInsertColBefore'), onPostRender: postRenderCell},
  1785. {text: 'Insert column after', onclick: cmd('mceTableInsertColAfter'), onPostRender: postRenderCell},
  1786. {text: 'Delete column', onclick: cmd('mceTableDeleteCol'), onPostRender: postRenderCell}
  1787. ]
  1788. });
  1789. var menuItems = [];
  1790. each("inserttable tableprops deletetable | cell row column".split(' '), function(name) {
  1791. if (name == '|') {
  1792. menuItems.push({text: '-'});
  1793. } else {
  1794. menuItems.push(editor.menuItems[name]);
  1795. }
  1796. });
  1797. editor.addButton("table", {
  1798. type: "menubutton",
  1799. title: "Table",
  1800. menu: menuItems
  1801. });
  1802. // Select whole table is a table border is clicked
  1803. if (!Env.isIE) {
  1804. editor.on('click', function(e) {
  1805. e = e.target;
  1806. if (e.nodeName === 'TABLE') {
  1807. editor.selection.select(e);
  1808. editor.nodeChanged();
  1809. }
  1810. });
  1811. }
  1812. self.quirks = new Quirks(editor);
  1813. editor.on('Init', function() {
  1814. winMan = editor.windowManager;
  1815. self.cellSelection = new CellSelection(editor);
  1816. });
  1817. // Register action commands
  1818. each({
  1819. mceTableSplitCells: function(grid) {
  1820. grid.split();
  1821. },
  1822. mceTableMergeCells: function(grid) {
  1823. var rowSpan, colSpan, cell;
  1824. cell = editor.dom.getParent(editor.selection.getStart(), 'th,td');
  1825. if (cell) {
  1826. rowSpan = cell.rowSpan;
  1827. colSpan = cell.colSpan;
  1828. }
  1829. if (!editor.dom.select('td.mce-item-selected,th.mce-item-selected').length) {
  1830. mergeDialog(grid, cell);
  1831. } else {
  1832. grid.merge();
  1833. }
  1834. },
  1835. mceTableInsertRowBefore: function(grid) {
  1836. grid.insertRow(true);
  1837. },
  1838. mceTableInsertRowAfter: function(grid) {
  1839. grid.insertRow();
  1840. },
  1841. mceTableInsertColBefore: function(grid) {
  1842. grid.insertCol(true);
  1843. },
  1844. mceTableInsertColAfter: function(grid) {
  1845. grid.insertCol();
  1846. },
  1847. mceTableDeleteCol: function(grid) {
  1848. grid.deleteCols();
  1849. },
  1850. mceTableDeleteRow: function(grid) {
  1851. grid.deleteRows();
  1852. },
  1853. mceTableCutRow: function(grid) {
  1854. clipboardRows = grid.cutRows();
  1855. },
  1856. mceTableCopyRow: function(grid) {
  1857. clipboardRows = grid.copyRows();
  1858. },
  1859. mceTablePasteRowBefore: function(grid) {
  1860. grid.pasteRows(clipboardRows, true);
  1861. },
  1862. mceTablePasteRowAfter: function(grid) {
  1863. grid.pasteRows(clipboardRows);
  1864. },
  1865. mceTableDelete: function(grid) {
  1866. grid.deleteTable();
  1867. }
  1868. }, function(func, name) {
  1869. editor.addCommand(name, function() {
  1870. var grid = new TableGrid(editor);
  1871. if (grid) {
  1872. func(grid);
  1873. editor.execCommand('mceRepaint');
  1874. self.cellSelection.clear();
  1875. }
  1876. });
  1877. });
  1878. // Register dialog commands
  1879. each({
  1880. mceInsertTable: function() {
  1881. tableDialog();
  1882. },
  1883. mceTableRowProps: rowDialog,
  1884. mceTableCellProps: cellDialog
  1885. }, function(func, name) {
  1886. editor.addCommand(name, function(ui, val) {
  1887. func(val);
  1888. });
  1889. });
  1890. }
  1891. PluginManager.add('table', Plugin);
  1892. });
  1893. expose(["tinymce/tableplugin/TableGrid","tinymce/tableplugin/Quirks","tinymce/tableplugin/CellSelection","tinymce/tableplugin/Plugin"]);
  1894. })(this);