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