14b908c11959ae8d40763c297971b04158504277
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / htmlarea / plugins / TableOperations / table-operations.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2002 interactivetools.com, inc. Authored by Mihai Bazon, sponsored by http://www.bloki.com.
5 * (c) 2005 Xinha, http://xinha.gogo.co.nz/ for the original toggle borders function.
6 * (c) 2004-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This script is a modified version of a script published under the htmlArea License.
27 * A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
28 *
29 * This copyright notice MUST APPEAR in all copies of the script!
30 ***************************************************************/
31 /*
32 * Table Operations Plugin for TYPO3 htmlArea RTE
33 *
34 * TYPO3 SVN ID: $Id$
35 */
36 TableOperations = HTMLArea.Plugin.extend({
37
38 constructor : function(editor, pluginName) {
39 this.base(editor, pluginName);
40 },
41
42 /*
43 * This function gets called by the class constructor
44 */
45 configurePlugin : function (editor) {
46
47 this.buttonsConfiguration = this.editorConfiguration.buttons;
48
49 /*
50 * Registering plugin "About" information
51 */
52 var pluginInformation = {
53 version : "3.7",
54 developer : "Mihai Bazon & Stanislas Rolland",
55 developerUrl : "http://www.fructifor.ca/",
56 copyrightOwner : "Mihai Bazon & Stanislas Rolland",
57 sponsor : "Zapatec Inc. & Fructifor Inc.",
58 sponsorUrl : "http://www.fructifor.ca/",
59 license : "GPL"
60 };
61 this.registerPluginInformation(pluginInformation);
62
63 /*
64 * Registering the buttons
65 */
66 var hideToggleBorders = this.editorConfiguration.hideTableOperationsInToolbar && !(this.buttonsConfiguration.toggleborders && this.buttonsConfiguration.toggleborders.keepInToolbar);
67 var buttonList = this.buttonList;
68 for (var i = 0, n = buttonList.length; i < n; ++i) {
69 var button = buttonList[i];
70 buttonId = (button[0] === "InsertTable") ? button[0] : ("TO-" + button[0]);
71 var buttonConfiguration = {
72 id : buttonId,
73 tooltip : this.localize((buttonId === "InsertTable") ? "Insert Table" : buttonId),
74 action : "onButtonPress",
75 hotKey : (this.buttonsConfiguration[button[2]] ? this.buttonsConfiguration[button[2]].hotKey : null),
76 context : button[1],
77 hide : ((buttonId == "TO-toggle-borders") ? hideToggleBorders : ((button[0] === "InsertTable") ? false : this.editorConfiguration.hideTableOperationsInToolbar))
78 };
79 this.registerButton(buttonConfiguration);
80 }
81
82 return true;
83 },
84
85 /*
86 * The list of buttons added by this plugin
87 */
88 buttonList : [
89 ["InsertTable", null, "table"],
90 ["toggle-borders", null, "toggleborders"],
91 ["table-prop", "table", "tableproperties"],
92 ["row-prop", "tr", "rowproperties"],
93 ["row-insert-above", "tr", "rowinsertabove"],
94 ["row-insert-under", "tr", "rowinsertunder"],
95 ["row-delete", "tr", "rowdelete"],
96 ["row-split", "td,th[rowSpan!=1]", "rowsplit"],
97 ["col-insert-before", "td,th", "columninsertbefore"],
98 ["col-insert-after", "td,th", "columninsertafter"],
99 ["col-delete", "td,th", "columndelete"],
100 ["col-split", "td,th[colSpan!=1]", "columnsplit"],
101 ["cell-prop", "td,th", "cellproperties"],
102 ["cell-insert-before", "td,th", "cellinsertbefore"],
103 ["cell-insert-after", "td,th", "cellinsertafter"],
104 ["cell-delete", "td,th", "celldelete"],
105 ["cell-merge", "tr", "cellmerge"],
106 ["cell-split", "td,th[colSpan!=1,rowSpan!=1]", "cellsplit"]
107 ],
108
109 /************************
110 * UTILITIES
111 ************************/
112 /*
113 * Retrieve the closest element having the specified tagName in the list of
114 * ancestors of the current selection/caret.
115 */
116 getClosest : function (tagName) {
117 var editor = this.editor;
118 var ancestors = editor.getAllAncestors();
119 var ret = null;
120 tagName = ("" + tagName).toLowerCase();
121 for (var i=0; i < ancestors.length; ++i) {
122 var el = ancestors[i];
123 if (el.tagName.toLowerCase() == tagName) {
124 ret = el;
125 break;
126 }
127 }
128 return ret;
129 },
130
131 /*
132 * Open the table properties dialog.
133 */
134 dialogTableProperties : function () {
135 // retrieve existing values
136 var table = this.getClosest("table");
137 var tablePropertiesInitFunctRef = TableOperations.tablePropertiesInit(table);
138 var tablePropertiesUpdateFunctRef = TableOperations.tablePropertiesUpdate(table);
139 var dialog = new PopupWin(this.editor, this.localize("Table Properties"), tablePropertiesUpdateFunctRef, tablePropertiesInitFunctRef, 570, 600);
140 },
141
142 /*
143 * Open the row/cell properties dialog.
144 * This function requires the file PopupWin to be loaded.
145 */
146 dialogRowCellProperties : function (cell) {
147 // retrieve existing values
148 if (cell) {
149 var element = this.getClosest("td");
150 if (!element) var element = this.getClosest("th");
151 } else {
152 var element = this.getClosest("tr");
153 }
154 if(element) {
155 var rowCellPropertiesInitFunctRef = TableOperations.rowCellPropertiesInit(element, cell);
156 var rowCellPropertiesUpdateFunctRef = TableOperations.rowCellPropertiesUpdate(element);
157 var dialog = new PopupWin(this.editor, this.localize(cell ? "Cell Properties" : "Row Properties"), rowCellPropertiesUpdateFunctRef, rowCellPropertiesInitFunctRef, 700, 425);
158 }
159 },
160
161 /*
162 * This function gets called when a Table Operations button was pressed.
163 *
164 * @param object editor: the editor instance
165 * @param string id: the button id or the key
166 *
167 * @return boolean false if action is completed
168 */
169 onButtonPress : function (editor, id, target) {
170 // Could be a button or its hotkey
171 var buttonId = this.translateHotKey(id);
172 buttonId = buttonId ? buttonId : id;
173
174 var mozbr = HTMLArea.is_gecko ? "<br />" : "";
175 var tableParts = ["tfoot", "thead", "tbody"];
176 var tablePartsIndex = { tfoot : 0, thead : 1, tbody : 2 };
177
178 // helper function that clears the content in a table row
179 function clearRow(tr) {
180 var tds = tr.getElementsByTagName("td");
181 for (var i = tds.length; --i >= 0;) {
182 var td = tds[i];
183 td.rowSpan = 1;
184 td.innerHTML = mozbr;
185 }
186 var tds = tr.getElementsByTagName("th");
187 for (var i = tds.length; --i >= 0;) {
188 var td = tds[i];
189 td.rowSpan = 1;
190 td.innerHTML = mozbr;
191 }
192 };
193
194 function splitRow(td) {
195 var n = parseInt("" + td.rowSpan);
196 var colSpan = td.colSpan;
197 var tagName = td.tagName.toLowerCase();
198 td.rowSpan = 1;
199 var tr = td.parentNode;
200 var sectionRowIndex = tr.sectionRowIndex;
201 var rows = tr.parentNode.rows;
202 var index = td.cellIndex;
203 while (--n > 0) {
204 tr = rows[++sectionRowIndex];
205 // Last row
206 if (!tr) tr = td.parentNode.parentNode.appendChild(editor._doc.createElement("tr"));
207 var otd = editor._doc.createElement(tagName);
208 otd.colSpan = colSpan;
209 otd.innerHTML = mozbr;
210 tr.insertBefore(otd, tr.cells[index]);
211 }
212 };
213
214 function splitCol(td) {
215 var nc = parseInt("" + td.colSpan);
216 var tagName = td.tagName.toLowerCase();
217 td.colSpan = 1;
218 var tr = td.parentNode;
219 var ref = td.nextSibling;
220 while (--nc > 0) {
221 var otd = editor._doc.createElement(tagName);
222 otd.rowSpan = td.rowSpan;
223 otd.innerHTML = mozbr;
224 tr.insertBefore(otd, ref);
225 }
226 };
227
228 function splitCell(td) {
229 var nc = parseInt("" + td.colSpan);
230 splitCol(td);
231 var cells = td.parentNode.cells;
232 var index = td.cellIndex;
233 while (nc-- > 0) {
234 splitRow(cells[index++]);
235 }
236 };
237
238 function selectNextNode(el) {
239 var node = el.nextSibling;
240 while (node && node.nodeType != 1) {
241 node = node.nextSibling;
242 }
243 if (!node) {
244 node = el.previousSibling;
245 while (node && node.nodeType != 1) {
246 node = node.previousSibling;
247 }
248 }
249 if (!node) node = el.parentNode;
250 editor.selectNodeContents(node);
251 };
252
253 function getSelectedCells(sel) {
254 var cell, range, i = 0, cells = [];
255 try {
256 while (range = sel.getRangeAt(i++)) {
257 cell = range.startContainer.childNodes[range.startOffset];
258 while (!/^(td|th|body)$/.test(cell.tagName.toLowerCase())) cell = cell.parentNode;
259 if (/^(td|th)$/.test(cell.tagName.toLowerCase())) cells.push(cell);
260 }
261 } catch(e) {
262 /* finished walking through selection */
263 }
264 return cells;
265 };
266
267 function deleteEmptyTable(table) {
268 var lastPart = true;
269 for (var j = tableParts.length; --j >= 0;) {
270 var tablePart = table.getElementsByTagName(tableParts[j])[0];
271 if (tablePart) lastPart = false;
272 }
273 if (lastPart) {
274 selectNextNode(table);
275 table.parentNode.removeChild(table);
276 }
277 };
278
279 function computeCellIndexes(table) {
280 var matrix = [];
281 var lookup = {};
282 for (var m = tableParts.length; --m >= 0;) {
283 var tablePart = table.getElementsByTagName(tableParts[m])[0];
284 if (tablePart) {
285 var rows = tablePart.rows;
286 for (var i = 0, n = rows.length; i < n; i++) {
287 var cells = rows[i].cells;
288 for (var j=0; j< cells.length; j++) {
289 var cell = cells[j];
290 var rowIndex = cell.parentNode.rowIndex;
291 var cellId = tableParts[m]+"-"+rowIndex+"-"+cell.cellIndex;
292 var rowSpan = cell.rowSpan || 1;
293 var colSpan = cell.colSpan || 1;
294 var firstAvailCol;
295 if(typeof(matrix[rowIndex])=="undefined") { matrix[rowIndex] = []; }
296 // Find first available column in the first row
297 for (var k=0; k<matrix[rowIndex].length+1; k++) {
298 if (typeof(matrix[rowIndex][k])=="undefined") {
299 firstAvailCol = k;
300 break;
301 }
302 }
303 lookup[cellId] = firstAvailCol;
304 for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
305 if (typeof(matrix[k])=="undefined") { matrix[k] = []; }
306 var matrixrow = matrix[k];
307 for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
308 matrixrow[l] = "x";
309 }
310 }
311 }
312 }
313 }
314 }
315 return lookup;
316 };
317
318 function getActualCellIndex(cell, lookup) {
319 return lookup[cell.parentNode.parentNode.nodeName.toLowerCase()+"-"+cell.parentNode.rowIndex+"-"+cell.cellIndex];
320 };
321
322 switch (buttonId) {
323 // ROWS
324 case "TO-row-insert-above":
325 case "TO-row-insert-under":
326 var tr = this.getClosest("tr");
327 if (!tr) break;
328 var otr = tr.cloneNode(true);
329 clearRow(otr);
330 otr = tr.parentNode.insertBefore(otr, (/under/.test(buttonId) ? tr.nextSibling : tr));
331 this.editor.selectNodeContents(otr.firstChild, true);
332 break;
333 case "TO-row-delete":
334 var tr = this.getClosest("tr");
335 if (!tr) break;
336 var part = tr.parentNode;
337 var table = part.parentNode;
338 if(part.rows.length == 1) { // this the last row, delete the whole table part
339 selectNextNode(part);
340 table.removeChild(part);
341 deleteEmptyTable(table);
342 } else {
343 // set the caret first to a position that doesn't disappear.
344 selectNextNode(tr);
345 part.removeChild(tr);
346 }
347 editor.forceRedraw();
348 editor.focusEditor();
349 editor.updateToolbar();
350 break;
351 case "TO-row-split":
352 var cell = this.getClosest("td");
353 if (!cell) var cell = this.getClosest("th");
354 if (!cell) break;
355 var sel = editor._getSelection();
356 if (HTMLArea.is_gecko && !sel.isCollapsed && !HTMLArea.is_safari && !HTMLArea.is_opera) {
357 var cells = getSelectedCells(sel);
358 for (i = 0; i < cells.length; ++i) splitRow(cells[i]);
359 } else {
360 splitRow(cell);
361 }
362 editor.forceRedraw();
363 editor.updateToolbar();
364 break;
365
366 // COLUMNS
367 case "TO-col-insert-before":
368 case "TO-col-insert-after":
369 var cell = this.getClosest("td");
370 if (!cell) var cell = this.getClosest("th");
371 if (!cell) break;
372 var index = cell.cellIndex;
373 var table = cell.parentNode.parentNode.parentNode;
374 for (var j = tableParts.length; --j >= 0;) {
375 var tablePart = table.getElementsByTagName(tableParts[j])[0];
376 if (tablePart) {
377 var rows = tablePart.rows;
378 for (var i = rows.length; --i >= 0;) {
379 var tr = rows[i];
380 var ref = tr.cells[index + (/after/.test(buttonId) ? 1 : 0)];
381 if (!ref) {
382 var otd = editor._doc.createElement(tr.lastChild.tagName.toLowerCase());
383 otd.innerHTML = mozbr;
384 tr.appendChild(otd);
385 } else {
386 var otd = editor._doc.createElement(ref.tagName.toLowerCase());
387 otd.innerHTML = mozbr;
388 tr.insertBefore(otd, ref);
389 }
390 }
391 }
392 }
393 editor.focusEditor();
394 break;
395 case "TO-col-split":
396 var cell = this.getClosest("td");
397 if (!cell) var cell = this.getClosest("th");
398 if (!cell) break;
399 var sel = editor._getSelection();
400 if (HTMLArea.is_gecko && !sel.isCollapsed && !HTMLArea.is_safari && !HTMLArea.is_opera) {
401 var cells = getSelectedCells(sel);
402 for (i = 0; i < cells.length; ++i) splitCol(cells[i]);
403 } else {
404 splitCol(cell);
405 }
406 editor.forceRedraw();
407 editor.updateToolbar();
408 break;
409 case "TO-col-delete":
410 var cell = this.getClosest("td");
411 if (!cell) var cell = this.getClosest("th");
412 if (!cell) break;
413 var index = cell.cellIndex;
414 var part = cell.parentNode.parentNode;
415 var table = part.parentNode;
416 var lastPart = true;
417 for (var j = tableParts.length; --j >= 0;) {
418 var tablePart = table.getElementsByTagName(tableParts[j])[0];
419 if (tablePart) {
420 var rows = tablePart.rows;
421 var lastColumn = true;
422 for (var i = rows.length; --i >= 0;) {
423 if(rows[i].cells.length > 1) lastColumn = false;
424 }
425 if (lastColumn) {
426 // this is the last column, delete the whole tablepart
427 // set the caret first to a position that doesn't disappear
428 selectNextNode(tablePart);
429 table.removeChild(tablePart);
430 } else {
431 // set the caret first to a position that doesn't disappear
432 if (part == tablePart) selectNextNode(cell);
433 for (var i = rows.length; --i >= 0;) {
434 if(rows[i].cells[index]) rows[i].removeChild(rows[i].cells[index]);
435 }
436 lastPart = false;
437 }
438 }
439 }
440 if (lastPart) {
441 // the last table section was deleted: delete the whole table
442 // set the caret first to a position that doesn't disappear
443 selectNextNode(table);
444 table.parentNode.removeChild(table);
445 }
446 editor.forceRedraw();
447 editor.focusEditor();
448 editor.updateToolbar();
449 break;
450
451 // CELLS
452 case "TO-cell-split":
453 var cell = this.getClosest("td");
454 if (!cell) var cell = this.getClosest("th");
455 if (!cell) break;
456 var sel = editor._getSelection();
457 if (HTMLArea.is_gecko && !sel.isCollapsed && !HTMLArea.is_safari && !HTMLArea.is_opera) {
458 var cells = getSelectedCells(sel);
459 for (i = 0; i < cells.length; ++i) splitCell(cells[i]);
460 } else {
461 splitCell(cell);
462 }
463 editor.forceRedraw();
464 editor.updateToolbar();
465 break;
466 case "TO-cell-insert-before":
467 case "TO-cell-insert-after":
468 var cell = this.getClosest("td");
469 if (!cell) var cell = this.getClosest("th");
470 if (!cell) break;
471 var tr = cell.parentNode;
472 var otd = editor._doc.createElement(cell.tagName.toLowerCase());
473 otd.innerHTML = mozbr;
474 tr.insertBefore(otd, (/after/.test(buttonId) ? cell.nextSibling : cell));
475 editor.forceRedraw();
476 editor.focusEditor();
477 break;
478 case "TO-cell-delete":
479 var cell = this.getClosest("td");
480 if (!cell) var cell = this.getClosest("th");
481 if (!cell) break;
482 var row = cell.parentNode;
483 if(row.cells.length == 1) { // this is the only cell in the row, delete the row
484 var part = row.parentNode;
485 var table = part.parentNode;
486 if (part.rows.length == 1) { // this the last row, delete the whole table part
487 selectNextNode(part);
488 table.removeChild(part);
489 deleteEmptyTable(table);
490 } else {
491 selectNextNode(row);
492 part.removeChild(row);
493 }
494 } else {
495 // set the caret first to a position that doesn't disappear
496 selectNextNode(cell);
497 row.removeChild(cell);
498 }
499 editor.forceRedraw();
500 editor.focusEditor();
501 editor.updateToolbar();
502 break;
503 case "TO-cell-merge":
504 var sel = editor._getSelection();
505 var range, i = 0;
506 var rows = new Array();
507 for (var k = tableParts.length; --k >= 0;) rows[k] = [];
508 var row = null;
509 var cells = null;
510 if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {
511 try {
512 while (range = sel.getRangeAt(i++)) {
513 var td = range.startContainer.childNodes[range.startOffset];
514 if (td.parentNode != row) {
515 (cells) && rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells);
516 row = td.parentNode;
517 cells = [];
518 }
519 cells.push(td);
520 }
521 } catch(e) {
522 /* finished walking through selection */
523 }
524 rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells);
525 } else {
526 // Internet Explorer, Safari and Opera
527 var cell = this.getClosest("td");
528 if (!cell) var cell = this.getClosest("th");
529 if (!cell) {
530 alert(this.localize("Please click into some cell"));
531 break;
532 }
533 var tr = cell.parentElement;
534 var no_cols = parseInt(prompt(this.localize("How many columns would you like to merge?"), 2));
535 if (!no_cols) break;
536 var no_rows = parseInt(prompt(this.localize("How many rows would you like to merge?"), 2));
537 if (!no_rows) break;
538 var lookup = computeCellIndexes(cell.parentNode.parentNode.parentNode);
539 var first_index = getActualCellIndex(cell, lookup);
540 // Collect cells on first row
541 var td = cell, colspan = 0;
542 cells = [];
543 for (var i = no_cols; --i >= 0;) {
544 if (!td) break;
545 cells.push(td);
546 var last_index = getActualCellIndex(td, lookup);
547 td = td.nextSibling;
548 }
549 rows[tablePartsIndex[tr.parentNode.nodeName.toLowerCase()]].push(cells);
550 // Collect cells on following rows
551 var index, first_index_found, last_index_found;
552 for (var j = 1; j < no_rows; ++j) {
553 tr = tr.nextSibling;
554 if (!tr) break;
555 cells = [];
556 first_index_found = false;
557 for (var i = 0; i < tr.cells.length; ++i) {
558 td = tr.cells[i];
559 if (!td) break;
560 index = getActualCellIndex(td, lookup);
561 if (index > last_index) break;
562 if (index == first_index) first_index_found = true;
563 if (index >= first_index) cells.push(td);
564 }
565 // If not rectangle, we quit!
566 if (!first_index_found) break;
567 rows[tablePartsIndex[tr.parentNode.nodeName.toLowerCase()]].push(cells);
568 }
569 }
570 for (var k = tableParts.length; --k >= 0;) {
571 var cell, row;
572 var cellHTML = "";
573 var cellRowSpan = 0;
574 var cellColSpan, maxCellColSpan = 0;
575 if (rows[k] && rows[k][0]) {
576 for (var i = 0; i < rows[k].length; ++i) {
577 var cells = rows[k][i];
578 var cellColSpan = 0;
579 if (!cells) continue;
580 cellRowSpan += cells[0].rowSpan ? cells[0].rowSpan : 1;
581 for (var j = 0; j < cells.length; ++j) {
582 cell = cells[j];
583 row = cell.parentNode;
584 cellHTML += cell.innerHTML;
585 cellColSpan += cell.colSpan ? cell.colSpan : 1;
586 if (i || j) {
587 cell.parentNode.removeChild(cell);
588 if(!row.cells.length) row.parentNode.removeChild(row);
589 }
590 }
591 if (maxCellColSpan < cellColSpan) {
592 maxCellColSpan = cellColSpan;
593 }
594 }
595 var td = rows[k][0][0];
596 td.innerHTML = cellHTML;
597 td.rowSpan = cellRowSpan;
598 td.colSpan = maxCellColSpan;
599 editor.selectNodeContents(td);
600 }
601 }
602 break;
603 // CREATION AND PROPERTIES
604 case "InsertTable":
605 this.dialogInsertTable();
606 break;
607 case "TO-table-prop":
608 this.dialogTableProperties();
609 break;
610 case "TO-row-prop":
611 this.dialogRowCellProperties(false);
612 break;
613 case "TO-cell-prop":
614 this.dialogRowCellProperties(true);
615 break;
616 case "TO-toggle-borders":
617 this.toggleBorders();
618 break;
619 default:
620 alert("Button [" + buttonId + "] not yet implemented");
621 }
622 },
623
624 /*
625 * Open insert table request
626 */
627 dialogInsertTable : function () {
628 this.dialog = this.openDialog("InsertTable", this.makeUrlFromPopupName("insert_table"), "insertTable", null, {width:520, height:230});
629 return false;
630 },
631
632 /*
633 * Get the insert table action function
634 */
635 insertTable : function(param) {
636 var editor = this.editor;
637 if (!param) return false;
638 var doc = editor._doc;
639 var table = doc.createElement("table");
640 for (var field in param) {
641 if (param.hasOwnProperty(field)) {
642 var value = param[field];
643 if (value) {
644 switch (field) {
645 case "f_width" :
646 if(value != "") {
647 table.style.width = parseInt(value) + param["f_unit"];
648 break;
649 }
650 case "f_align" :
651 table.style.textAlign = value;
652 break;
653 case "f_border" :
654 if(value != "") {
655 table.style.borderWidth = parseInt(value)+"px";
656 table.style.borderStyle = "solid";
657 }
658 break;
659 case "f_spacing" :
660 if(value != "") {
661 table.cellSpacing = parseInt(value);
662 break;
663 }
664 case "f_padding" :
665 if(value != "") {
666 table.cellPadding = parseInt(value);
667 break;
668 }
669 case "f_float" :
670 if (HTMLArea.is_ie) {
671 table.style.styleFloat = ((value != "not set") ? value : "");
672 } else {
673 table.style.cssFloat = ((value != "not set") ? value : "");
674 }
675 break;
676 }
677 }
678 }
679 }
680 var cellwidth = 0;
681 if(param.f_fixed) cellwidth = Math.floor(100 / parseInt(param.f_cols));
682 var tbody = doc.createElement("tbody");
683 table.appendChild(tbody);
684 for (var i = param["f_rows"]; i > 0; i--) {
685 var tr = doc.createElement("tr");
686 tbody.appendChild(tr);
687 for (var j = param["f_cols"]; j > 0; j--) {
688 var td = doc.createElement("td");
689 if (cellwidth) td.style.width = cellwidth + "%";
690 if (HTMLArea.is_opera) { td.innerHTML = '&nbsp;'; }
691 tr.appendChild(td);
692 }
693 }
694 editor.focusEditor();
695 editor.insertNodeAtSelection(table);
696 if (this.buttonsConfiguration.toggleborders && this.buttonsConfiguration.toggleborders.setOnTableCreation) {
697 this.toggleBorders();
698 }
699 return true;
700 },
701
702 toggleBorders : function () {
703 var tables = this.editor._doc.getElementsByTagName("table");
704 if (tables.length != 0) {
705 this.editor.borders = true;
706 for (var ix=0; ix < tables.length; ix++) this.editor.borders = this.editor.borders && /htmlarea-showtableborders/.test(tables[ix].className);
707 for (ix=0; ix < tables.length; ix++) {
708 if (!this.editor.borders) HTMLArea._addClass(tables[ix],'htmlarea-showtableborders');
709 else HTMLArea._removeClass(tables[ix],'htmlarea-showtableborders');
710 }
711 }
712 // The only way to get Firefox to show these borders...
713 if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) this.editor.setMode("wysiwyg");
714 }
715 });
716
717 /*
718 * Set the language file for the plugin
719 */
720 TableOperations.I18N = TableOperations_langArray;
721
722 /*
723 * Initialize the table properties dialog
724 */
725 TableOperations.tablePropertiesInit = function(table) {
726 return (function (dialog) {
727 var doc = dialog.doc;
728 var content = dialog.content;
729 var i18n = TableOperations.I18N;
730 TableOperations.buildTitle(doc, i18n, content, "Table Properties");
731 TableOperations.buildDescriptionFieldset(doc, table, i18n, content);
732 var obj = dialog.editor.config.customSelects.BlockStyle ? dialog.editor.plugins.BlockStyle.instance : dialog.editor.config.customSelects["DynamicCSS-class"];
733 if (obj && (obj.loaded || obj.cssLoaded)) TableOperations.buildStylingFieldset(doc, table, i18n, content, obj.cssArray);
734 if (!dialog.editor.config.disableLayoutFieldsetInTableOperations) TableOperations.buildLayoutFieldset(doc, table, i18n, content);
735 if (!dialog.editor.config.disableAlignmentFieldsetInTableOperations) TableOperations.buildAlignmentFieldset(doc, table, i18n, content, "floating");
736 if (!dialog.editor.config.disableSpacingFieldsetInTableOperations) TableOperations.buildSpacingFieldset(doc, table, i18n, content);
737 if (!dialog.editor.config.disableBordersFieldsetInTableOperations) TableOperations.buildBordersFieldset(dialog.dialogWindow, doc, dialog.editor, table, i18n, content);
738 if (!dialog.editor.config.disableColorFieldsetInTableOperations) TableOperations.buildColorsFieldset(dialog.dialogWindow, doc, dialog.editor, table, i18n, content);
739 dialog.modal = true;
740 dialog.addButtons("ok", "cancel");
741 dialog.showAtElement();
742 });
743 };
744
745 /*
746 * Update the table properties and close the dialog
747 */
748 TableOperations.tablePropertiesUpdate = function(table) {
749 return (function (dialog,params) {
750 dialog.editor.focusEditor();
751 TableOperations.processStyle(params, table);
752 table.removeAttribute("border");
753 for (var i in params) {
754 var val = params[i];
755 switch (i) {
756 case "f_caption":
757 if (/\S/.test(val)) {
758 // contains non white-space characters
759 var caption = table.getElementsByTagName("caption")[0];
760 if (!caption) {
761 caption = dialog.editor._doc.createElement("caption");
762 table.insertBefore(caption, table.firstChild);
763 }
764 caption.innerHTML = val;
765 } else {
766 // search for caption and delete it if found
767 var caption = table.getElementsByTagName("caption")[0];
768 if (caption) caption.parentNode.removeChild(caption);
769 }
770 break;
771 case "f_summary":
772 table.summary = val;
773 break;
774 case "f_width":
775 table.style.width = ("" + val) + params.f_unit;
776 break;
777 case "f_align":
778 table.align = val;
779 break;
780 case "f_spacing":
781 table.cellSpacing = val;
782 break;
783 case "f_padding":
784 table.cellPadding = val;
785 break;
786 case "f_frames":
787 table.frame = (val != "not set") ? val : "";
788 break;
789 case "f_rules":
790 if (val != "not set") table.rules = val;
791 else table.removeAttribute("rules");
792 break;
793 case "f_class":
794 case "f_class_tbody":
795 case "f_class_thead":
796 case "f_class_tfoot":
797 var tpart = table;
798 if (i.length > 7) tpart = table.getElementsByTagName(i.substring(8,13))[0];
799 var cls = tpart.className.trim().split(" ");
800 for (var j = cls.length;j > 0;) {
801 if (!HTMLArea.reservedClassNames.test(cls[--j])) HTMLArea._removeClass(tpart,cls[j]);
802 }
803 if (val != 'none') HTMLArea._addClass(tpart,val);
804 break;
805 }
806 }
807 dialog.editor.focusEditor();
808 dialog.editor.updateToolbar();
809 });
810 };
811
812 /*
813 * Initialize the row/cell properties dialog
814 */
815 TableOperations.rowCellPropertiesInit = function(element,cell) {
816 return (function (dialog) {
817 var doc = dialog.doc;
818 var content = dialog.content;
819 var i18n = TableOperations.I18N;
820 TableOperations.buildTitle(doc, i18n, content, (cell ? "Cell Properties" : "Row Properties"));
821 if (cell) TableOperations.buildCellTypeFieldset(dialog.dialogWindow, doc, dialog.editor, element, i18n, content);
822 else TableOperations.buildRowGroupFieldset(dialog.dialogWindow, doc, dialog.editor, element, i18n, content);
823 var obj = dialog.editor.config.customSelects.BlockStyle ? dialog.editor.plugins.BlockStyle.instance : dialog.editor.config.customSelects["DynamicCSS-class"];
824 if (obj && (obj.loaded || obj.cssLoaded)) TableOperations.buildStylingFieldset(doc, element, i18n, content, obj.cssArray);
825 else TableOperations.insertSpace(doc, content);
826 if (!dialog.editor.config.disableLayoutFieldsetInTableOperations) TableOperations.buildLayoutFieldset(doc, element, i18n, content, "floating");
827 if (!dialog.editor.config.disableAlignmentFieldsetInTableOperations) TableOperations.buildAlignmentFieldset(doc, element, i18n, content);
828 if (!dialog.editor.config.disableBordersFieldsetInTableOperations) TableOperations.buildBordersFieldset(dialog.dialogWindow, doc, dialog.editor, element, i18n, content);
829 if (!dialog.editor.config.disableColorFieldsetInTableOperations) TableOperations.buildColorsFieldset(dialog.dialogWindow, doc, dialog.editor, element, i18n, content);
830 dialog.modal = true;
831 dialog.addButtons("ok", "cancel");
832 dialog.showAtElement();
833 });
834 };
835
836 /*
837 * Update the row/cell properties and close the dialog
838 */
839 TableOperations.rowCellPropertiesUpdate = function(element) {
840 return (function (dialog,params) {
841 dialog.editor.focusEditor();
842 TableOperations.processStyle(params, element);
843 var convertCellType = false;
844 for (var i in params) {
845 var val = params[i];
846 switch (i) {
847 case "f_scope":
848 if (val != "not set") element.scope = val;
849 else element.removeAttribute('scope');
850 break;
851 case "f_cell_type":
852 // Set all cell attributes before cloning it with a new tag
853 if (val != element.tagName.toLowerCase()) {
854 var newCellType = val;
855 convertCellType = true;
856 }
857 break;
858 case "f_rowgroup":
859 var section = element.parentNode;
860 var tagName = section.tagName.toLowerCase();
861 if (val != tagName) {
862 var table = section.parentNode;
863 var newSection = table.getElementsByTagName(val)[0];
864 if (!newSection) var newSection = table.insertBefore(dialog.editor._doc.createElement(val), table.getElementsByTagName("tbody")[0]);
865 if (tagName == "thead" && val == "tbody") var newElement = newSection.insertBefore(element, newSection.firstChild);
866 else var newElement = newSection.appendChild(element);
867 if (!section.hasChildNodes()) table.removeChild(section);
868 }
869 break;
870 case "f_char":
871 element.ch = val;
872 break;
873 case "f_class":
874 var cls = element.className.trim().split(" ");
875 for (var j = cls.length;j > 0;) {
876 if (!HTMLArea.reservedClassNames.test(cls[--j])) HTMLArea._removeClass(element,cls[j]);
877 }
878 if (val != 'none') HTMLArea._addClass(element,val);
879 break;
880 }
881 }
882 if (convertCellType) {
883 var newCell = dialog.editor._doc.createElement(newCellType), p = element.parentNode, a, attrName, name;
884 var attrs = element.attributes;
885 for (var i = attrs.length; --i >= 0 ;) {
886 a = attrs.item(i);
887 attrName = a.nodeName;
888 name = attrName.toLowerCase();
889 // IE5.5 reports wrong values. For this reason we extract the values directly from the root node.
890 if (typeof(element[attrName]) != "undefined" && name != "style" && !/^on/.test(name)) {
891 if (element[attrName]) newCell.setAttribute(attrName, element[attrName]);
892 } else {
893 if (a.nodeValue) newCell.setAttribute(attrName, a.nodeValue);
894 }
895 }
896 // In IE, the above fails to update the classname and style attributes.
897 if (HTMLArea.is_ie) {
898 if (element.style.cssText) newCell.style.cssText = element.style.cssText;
899 if (element.className) {
900 newCell.setAttribute("className", element.className);
901 } else {
902 newCell.className = element.className;
903 newCell.removeAttribute("className");
904 }
905 }
906 while (element.firstChild) newCell.appendChild(element.firstChild);
907 p.insertBefore(newCell, element);
908 p.removeChild(element);
909 dialog.editor.selectNodeContents(newCell, false);
910 }
911 dialog.editor.updateToolbar();
912 });
913 };
914
915 TableOperations.getLength = function(value) {
916 var len = parseInt(value);
917 if (isNaN(len)) len = "";
918 return len;
919 };
920
921 // Applies the style found in "params" to the given element.
922 TableOperations.processStyle = function(params,element) {
923 var style = element.style;
924 for (var i in params) {
925 var val = params[i];
926 switch (i) {
927 case "f_st_backgroundColor":
928 style.backgroundColor = val;
929 break;
930 case "f_st_color":
931 style.color = val;
932 break;
933 case "f_st_backgroundImage":
934 if (/\S/.test(val)) {
935 style.backgroundImage = "url(" + val + ")";
936 } else {
937 style.backgroundImage = "";
938 }
939 break;
940 case "f_st_borderWidth":
941 if (/\S/.test(val)) {
942 style.borderWidth = val + "px";
943 } else {
944 style.borderWidth = "";
945 }
946 if (params["f_st_borderStyle"] == "none") style.borderWidth = "0px";
947 if (params["f_st_borderStyle"] == "not set") style.borderWidth = "";
948 break;
949 case "f_st_borderStyle":
950 style.borderStyle = (val != "not set") ? val : "";
951 break;
952 case "f_st_borderColor":
953 style.borderColor = val;
954 break;
955 case "f_st_borderCollapse":
956 style.borderCollapse = val ? "collapse" : "";
957 break;
958 case "f_st_width":
959 if (/\S/.test(val)) {
960 style.width = val + params["f_st_widthUnit"];
961 } else {
962 style.width = "";
963 }
964 break;
965 case "f_st_height":
966 if (/\S/.test(val)) {
967 style.height = val + params["f_st_heightUnit"];
968 } else {
969 style.height = "";
970 }
971 break;
972 case "f_st_textAlign":
973 if (val == "character") {
974 var ch = params["f_st_textAlignChar"];
975 if (ch == '"') {
976 ch = '\\"';
977 }
978 style.textAlign = '"' + ch + '"';
979 } else {
980 style.textAlign = (val != "not set") ? val : "";
981 }
982 break;
983 case "f_st_vertAlign":
984 style.verticalAlign = (val != "not set") ? val : "";
985 break;
986 case "f_st_float":
987 if (HTMLArea.is_ie) {
988 style.styleFloat = (val != "not set") ? val : "";
989 } else {
990 style.cssFloat = (val != "not set") ? val : "";
991 }
992 break;
993 // case "f_st_margin":
994 // style.margin = val + "px";
995 // break;
996 // case "f_st_padding":
997 // style.padding = val + "px";
998 // break;
999 }
1000 }
1001 };
1002
1003 // Returns an HTML element for a widget that allows color selection. That is,
1004 // a button that contains the given color, if any, and when pressed will popup
1005 // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
1006 // to select some color. If a color is selected, an input field with the name
1007 // "f_st_"+name will be updated with the color value in #123456 format.
1008 TableOperations.createColorButton = function(w, doc, editor, color, name) {
1009 if (!color) {
1010 color = "";
1011 } else if (!/#/.test(color)) {
1012 color = HTMLArea._colorToRgb(color);
1013 }
1014
1015 var df = doc.createElement("span");
1016 var field = doc.createElement("input");
1017 field.type = "hidden";
1018 df.appendChild(field);
1019 field.name = "f_st_" + name;
1020 field.id = "f_st_" + name;
1021 field.value = color;
1022 var button = doc.createElement("span");
1023 button.className = "buttonColor";
1024 df.appendChild(button);
1025 var span = doc.createElement("span");
1026 span.className = "chooser";
1027 span.style.backgroundColor = color;
1028 button.appendChild(span);
1029 button.onmouseover = function() { if (!this.disabled) this.className += " buttonColor-hilite"; };
1030 button.onmouseout = function() { if (!this.disabled) this.className = "buttonColor"; };
1031 span.onclick = function() {
1032 if (this.parentNode.disabled) return false;
1033 var typo3ColorPlugin = editor.plugins.TYPO3Color;
1034 if (typo3ColorPlugin) {
1035 typo3ColorPlugin.instance.dialogSelectColor("color", span, field, w);
1036 } else {
1037 editor._popupDialog("select_color.html", function(color) {
1038 if (color) {
1039 span.style.backgroundColor = "#" + color;
1040 field.value = "#" + color;
1041 }
1042 }, color, 200, 182, w);
1043 }
1044 };
1045 var span2 = doc.createElement("span");
1046 span2.innerHTML = "&#x00d7;";
1047 span2.className = "nocolor";
1048 span2.title = TableOperations.I18N["Unset color"];
1049 button.appendChild(span2);
1050 span2.onmouseover = function() { if (!this.parentNode.disabled) this.className += " nocolor-hilite"; };
1051 span2.onmouseout = function() { if (!this.parentNode.disabled) this.className = "nocolor"; };
1052 span2.onclick = function() {
1053 span.style.backgroundColor = "";
1054 field.value = "";
1055 };
1056 return df;
1057 };
1058 TableOperations.buildTitle = function(doc,i18n,content,title) {
1059 var div = doc.createElement("div");
1060 div.className = "title";
1061 div.innerHTML = i18n[title];
1062 content.appendChild(div);
1063 doc.title = i18n[title];
1064 };
1065 TableOperations.buildDescriptionFieldset = function(doc,el,i18n,content) {
1066 var fieldset = doc.createElement("fieldset");
1067 TableOperations.insertLegend(doc, i18n, fieldset, "Description");
1068 TableOperations.insertSpace(doc, fieldset);
1069 var f_caption = "";
1070 var capel = el.getElementsByTagName("caption")[0];
1071 if (capel) f_caption = capel.innerHTML;
1072 TableOperations.buildInput(doc, el, i18n, fieldset, "f_caption", "Caption:", "Description of the nature of the table", "", "", f_caption, "fr", "value", "");
1073 TableOperations.insertSpace(doc, fieldset);
1074 TableOperations.buildInput(doc, el, i18n, fieldset, "f_summary", "Summary:", "Summary of the table purpose and structure", "", "", el.summary, "fr", "value", "");
1075 TableOperations.insertSpace(doc, fieldset);
1076 content.appendChild(fieldset);
1077 };
1078 TableOperations.buildRowGroupFieldset = function(w,doc,editor,el,i18n,content) {
1079 var fieldset = doc.createElement("fieldset");
1080 TableOperations.insertLegend(doc, i18n, fieldset, "Row group");
1081 TableOperations.insertSpace(doc, fieldset);
1082 selected = el.parentNode.tagName.toLowerCase();
1083 var selectScope = TableOperations.buildSelectField(doc, el, i18n, fieldset, "f_rowgroup", "Row group:", "fr", "", "Table section", ["Table body", "Table header", "Table footer"], ["tbody", "thead", "tfoot"], new RegExp((selected ? selected : "tbody"), "i"));
1084 TableOperations.insertSpace(doc, fieldset);
1085 content.appendChild(fieldset);
1086 };
1087 TableOperations.buildCellTypeFieldset = function(w,doc,editor,el,i18n,content) {
1088 var fieldset = doc.createElement("fieldset");
1089 TableOperations.insertLegend(doc, i18n, fieldset, "Cell Type and Scope");
1090 TableOperations.insertSpace(doc, fieldset);
1091 var ul = doc.createElement("ul");
1092 fieldset.appendChild(ul);
1093 var li = doc.createElement("li");
1094 ul.appendChild(li);
1095 var selectType = TableOperations.buildSelectField(doc, el, i18n, li, "f_cell_type", "Type of cell", "fr", "", "Specifies the type of cell", ["Normal", "Header"], ["td", "th"], new RegExp(el.tagName.toLowerCase(), "i"));
1096 selectType.onchange = function() { TableOperations.setStyleOptions(doc, editor, el, i18n, this); };
1097 var li = doc.createElement("li");
1098 ul.appendChild(li);
1099 selected = el.scope.toLowerCase();
1100 (selected.match(/([^\s]*)\s/)) && (selected = RegExp.$1);
1101 var selectScope = TableOperations.buildSelectField(doc, el, i18n, li, "f_scope", "Scope", "fr", "", "Scope of header cell", ["Not set", "scope_row", "scope_column", "scope_rowgroup"], ["not set", "row", "col", "rowgroup"], new RegExp((selected ? selected : "not set"), "i"));
1102 TableOperations.insertSpace(doc, fieldset);
1103 content.appendChild(fieldset);
1104 };
1105 TableOperations.getCssLabelsClasses = function(cssArray,i18n,tagName,selectedIn) {
1106 var cssLabels = new Array();
1107 var cssClasses = new Array();
1108 cssLabels[0] = i18n["Default"];
1109 cssClasses[0] = "none";
1110 var selected = selectedIn;
1111 var cls = selected.split(" ");
1112 var nonReservedClassName = false;
1113 for (var ia = cls.length; ia > 0;) {
1114 if(!HTMLArea.reservedClassNames.test(cls[--ia])) {
1115 selected = cls[ia];
1116 nonReservedClassName = true;
1117 break;
1118 }
1119 }
1120 var found = false, i = 1, cssClass;
1121 if(cssArray[tagName]) {
1122 for(cssClass in cssArray[tagName]){
1123 if(cssClass != "none") {
1124 cssLabels[i] = cssArray[tagName][cssClass];
1125 cssClasses[i] = cssClass;
1126 if(cssClass == selected) found = true;
1127 i++;
1128 } else {
1129 cssLabels[0] = cssArray[tagName][cssClass];
1130 }
1131 }
1132 }
1133 if(cssArray['all']){
1134 for(cssClass in cssArray['all']){
1135 cssLabels[i] = cssArray['all'][cssClass];
1136 cssClasses[i] = cssClass;
1137 if(cssClass == selected) found = true;
1138 i++;
1139 }
1140 }
1141 if(selected && nonReservedClassName && !found) {
1142 cssLabels[i] = i18n["Undefined"];
1143 cssClasses[i] = selected;
1144 }
1145 return [cssLabels, cssClasses, selected];
1146 };
1147 TableOperations.setStyleOptions = function(doc,editor,el,i18n,typeSelect) {
1148 var tagName = typeSelect.value;
1149 var select = doc.getElementById("f_class");
1150 if (!select) return false;
1151 var obj = dialog.editor.config.customSelects.BlockStyle ? dialog.editor.plugins.BlockStyle.instance : dialog.editor.config.customSelects["DynamicCSS-class"];
1152 if (obj && (obj.loaded || obj.cssLoaded)) var cssArray = obj.cssArray;
1153 else return false;
1154 var cssLabelsClasses = TableOperations.getCssLabelsClasses(cssArray,i18n,tagName,el.className);
1155 var options = cssLabelsClasses[0];
1156 var values = cssLabelsClasses[1];
1157 var selected = cssLabelsClasses[2];
1158 var selectedReg = new RegExp((selected ? selected : "none"), "i");
1159 while(select.options.length>0) select.options[select.length-1] = null;
1160 select.selectedIndex = 0;
1161 var option;
1162 for (var i = 0; i < options.length; ++i) {
1163 option = doc.createElement("option");
1164 select.appendChild(option);
1165 option.value = values[i];
1166 option.appendChild(doc.createTextNode(options[i]));
1167 option.selected = selectedReg.test(values[i]);
1168 }
1169 if(select.options.length>1) select.disabled = false;
1170 else select.disabled = true;
1171 };
1172 TableOperations.buildStylingFieldset = function(doc,el,i18n,content,cssArray) {
1173 var tagName = el.tagName.toLowerCase();
1174 var table = (tagName == "table");
1175 var cssLabelsClasses = TableOperations.getCssLabelsClasses(cssArray,i18n,tagName,el.className);
1176 var cssLabels = cssLabelsClasses[0];
1177 var cssClasses = cssLabelsClasses[1];
1178 var selected = cssLabelsClasses[2];
1179 var fieldset = doc.createElement("fieldset");
1180 TableOperations.insertLegend(doc, i18n, fieldset, "CSS Style");
1181 TableOperations.insertSpace(doc, fieldset);
1182 var ul = doc.createElement("ul");
1183 ul.className = "floating";
1184 fieldset.appendChild(ul);
1185 var li = doc.createElement("li");
1186 ul.appendChild(li);
1187 TableOperations.buildSelectField(doc, el, i18n, li, "f_class", (table ? "Table class:" : "Class:"), "fr", "", (table ? "Table class selector" : "Class selector"), cssLabels, cssClasses, new RegExp((selected ? selected : "none"), "i"), "", false);
1188 if (table) {
1189 var tbody = el.getElementsByTagName("tbody")[0];
1190 if (tbody) {
1191 var li = doc.createElement("li");
1192 ul.appendChild(li);
1193 cssLabelsClasses = TableOperations.getCssLabelsClasses(cssArray, i18n, "tbody", tbody.className);
1194 cssLabels = cssLabelsClasses[0];
1195 cssClasses = cssLabelsClasses[1];
1196 selected = cssLabelsClasses[2];
1197 TableOperations.buildSelectField(doc, el, i18n, li, "f_class_tbody", "Table body class:", "fr", "", "Table body class selector", cssLabels, cssClasses, new RegExp((selected ? selected : "none"), "i"), "", false);
1198 }
1199 ul = null;
1200 var thead = el.getElementsByTagName("thead")[0];
1201 if (thead) {
1202 var ul = doc.createElement("ul");
1203 fieldset.appendChild(ul);
1204 var li = doc.createElement("li");
1205 ul.appendChild(li);
1206 cssLabelsClasses = TableOperations.getCssLabelsClasses(cssArray, i18n, "thead", thead.className);
1207 cssLabels = cssLabelsClasses[0];
1208 cssClasses = cssLabelsClasses[1];
1209 selected = cssLabelsClasses[2];
1210 TableOperations.buildSelectField(doc, el, i18n, li, "f_class_thead", "Table header class:", "fr", "", "Table header class selector", cssLabels, cssClasses, new RegExp((selected ? selected : "none"), "i"), "", false);
1211 }
1212 var tfoot = el.getElementsByTagName("tfoot")[0];
1213 if (tfoot) {
1214 if (!ul) {
1215 var ul = doc.createElement("ul");
1216 fieldset.appendChild(ul);
1217 }
1218 var li = doc.createElement("li");
1219 ul.appendChild(li);
1220 cssLabelsClasses = TableOperations.getCssLabelsClasses(cssArray, i18n, "tfoot", tfoot.className);
1221 cssLabels = cssLabelsClasses[0];
1222 cssClasses = cssLabelsClasses[1];
1223 selected = cssLabelsClasses[2];
1224 TableOperations.buildSelectField(doc, el, i18n, li, "f_class_tfoot", "Table footer class:", "fr", "", "Table footer class selector", cssLabels, cssClasses, new RegExp((selected ? selected : "none"), "i"), "", false);
1225 }
1226 }
1227 TableOperations.insertSpace(doc, fieldset);
1228 content.appendChild(fieldset);
1229 };
1230 TableOperations.buildLayoutFieldset = function(doc,el,i18n,content,fieldsetClass) {
1231 var select, selected;
1232 var fieldset = doc.createElement("fieldset");
1233 if(fieldsetClass) fieldset.className = fieldsetClass;
1234 TableOperations.insertLegend(doc, i18n, fieldset, "Layout");
1235 var f_st_width = TableOperations.getLength(el.style.width);
1236 var f_st_height = TableOperations.getLength(el.style.height);
1237 var selectedWidthUnit = /%/.test(el.style.width) ? '%' : (/px/.test(el.style.width) ? 'px' : 'em');
1238 var selectedHeightUnit = /%/.test(el.style.height) ? '%' : (/px/.test(el.style.height) ? 'px' : 'em');
1239 var tag = el.tagName.toLowerCase();
1240 var ul = doc.createElement("ul");
1241 fieldset.appendChild(ul);
1242 switch(tag) {
1243 case "table" :
1244 var li = doc.createElement("li");
1245 ul.appendChild(li);
1246 TableOperations.buildInput(doc, el, i18n, li, "f_st_width", "Width:", "Table width", "", "5", f_st_width, "fr");
1247 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_widthUnit", "", "", "", "Width unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_width ? selectedWidthUnit : "%"), "i"));
1248 var li = doc.createElement("li");
1249 ul.appendChild(li);
1250 TableOperations.buildInput(doc, el, i18n, li, "f_st_height", "Height:", "Table height", "", "5", f_st_height, "fr");
1251 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_heightUnit", "", "", "", "Height unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_height ? selectedHeightUnit : "%"), "i"));
1252 selected = (HTMLArea._is_ie) ? el.style.styleFloat : el.style.cssFloat;
1253 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_float", "Float:", "", "", "Specifies where the table should float", ["Not set", "Non-floating", "Left", "Right"], ["not set", "none", "left", "right"], new RegExp((selected ? selected : "not set"), "i"));
1254 break;
1255 case "tr" :
1256 var li = doc.createElement("li");
1257 ul.appendChild(li);
1258 TableOperations.buildInput(doc, el, i18n, li, "f_st_width", "Width:", "Row width", "", "5", f_st_width, "fr");
1259 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_widthUnit", "", "", "", "Width unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_width ? selectedWidthUnit : "%"), "i"));
1260 var li = doc.createElement("li");
1261 ul.appendChild(li);
1262 TableOperations.buildInput(doc, el, i18n, li, "f_st_height", "Height:", "Row height", "", "5", f_st_height, "fr");
1263 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_heightUnit", "", "", "", "Height unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_height ? selectedHeightUnit : "%"), "i"));
1264 break;
1265 case "td" :
1266 case "th" :
1267 var li = doc.createElement("li");
1268 ul.appendChild(li);
1269 TableOperations.buildInput(doc, el, i18n, li, "f_st_width", "Width:", "Cell width", "", "5", f_st_width, "fr");
1270 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_widthUnit", "", "", "", "Width unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_width ? selectedWidthUnit : "%"), "i"));
1271 var li = doc.createElement("li");
1272 ul.appendChild(li);
1273 TableOperations.buildInput(doc, el, i18n, li, "f_st_height", "Height:", "Cell height", "", "5", f_st_height, "fr");
1274 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_heightUnit", "", "", "", "Height unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((f_st_height ? selectedHeightUnit : "%"), "i"));
1275 }
1276 content.appendChild(fieldset);
1277 };
1278 TableOperations.buildAlignmentFieldset = function(doc,el,i18n,content,fieldsetClass) {
1279 var select;
1280 var tag = el.tagName.toLowerCase();
1281 var fieldset = doc.createElement("fieldset");
1282 if(fieldsetClass) fieldset.className = fieldsetClass;
1283 TableOperations.insertLegend(doc, i18n, fieldset, "Alignment");
1284 var options = ["Not set", "Left", "Center", "Right", "Justify"];
1285 var values = ["not set", "left", "center", "right", "justify"];
1286 var selected = el.style.textAlign;
1287 (selected.match(/([^\s]*)\s/)) && (selected = RegExp.$1);
1288 /*
1289 if (tag == "td") {
1290 options.push("Character");
1291 values.push("character");
1292 if(f_st_textAlign.charAt(0) == '"') {
1293 var splitArray = f_st_textAlign.split('"');
1294 var f_st_textAlignChar = splitArray[0];
1295 f_st_textAlign = "character";
1296 }
1297 }
1298 */
1299 var ul = doc.createElement("ul");
1300 fieldset.appendChild(ul);
1301 var li = doc.createElement("li");
1302 ul.appendChild(li);
1303 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_textAlign", "Text alignment:", "fl", "", "Horizontal alignment of text within cell", options, values, new RegExp((selected ? selected : "not set"), "i"));
1304 /*
1305 if (tag == "td") {
1306 var characterFields = [];
1307 TableOperations.buildInput(doc, el, i18n, fieldset, "f_st_textAlignChar", "", "Align on this character", "", "1", f_st_textAlignChar, "", "floating", "", characterFields);
1308 function setCharVisibility(value) {
1309 for (var i = 0; i < characterFields.length; ++i) {
1310 var characterFieldElement = characterFields[i];
1311 characterFieldElement.style.visibility = value ? "visible" : "hidden";
1312 if (value && (characterFieldElement.tagName.toLowerCase() == "input" )) {
1313 characterFieldElement.focus();
1314 characterFieldElement.select();
1315 }
1316 }
1317 };
1318 select.onchange = function() { setCharVisibility(this.value == "character"); };
1319 setCharVisibility(select.value == "character");
1320 }
1321 */
1322 var li = doc.createElement("li");
1323 ul.appendChild(li);
1324 selected = el.style.verticalAlign;
1325 (selected.match(/([^\s]*)\s/)) && (selected = RegExp.$1);
1326 select = TableOperations.buildSelectField(doc, el, i18n, li, "f_st_vertAlign", "Vertical alignment:", "fl", "", "Vertical alignment of content within cell", ["Not set", "Top", "Middle", "Bottom", "Baseline"], ["not set", "top", "middle", "bottom", "baseline"], new RegExp((selected ? selected : "not set"), "i"));
1327 content.appendChild(fieldset);
1328 };
1329 TableOperations.buildSpacingFieldset = function(doc,el,i18n,content) {
1330 var fieldset = doc.createElement("fieldset");
1331 TableOperations.insertLegend(doc, i18n, fieldset, "Spacing and padding");
1332 var ul = doc.createElement("ul");
1333 fieldset.appendChild(ul);
1334 var li = doc.createElement("li");
1335 ul.appendChild(li);
1336 TableOperations.buildInput(doc, el, i18n, li, "f_spacing", "Cell spacing:", "Space between adjacent cells", "pixels", "5", el.cellSpacing, "fr", "", "postlabel");
1337 var li = doc.createElement("li");
1338 ul.appendChild(li);
1339 TableOperations.buildInput(doc, el, i18n, li, "f_padding", "Cell padding:", "Space between content and border in cell", "pixels", "5", el.cellPadding, "fr", "", "postlabel");
1340 content.appendChild(fieldset);
1341 };
1342 TableOperations.buildBordersFieldset = function(w,doc,editor,el,i18n,content,fieldsetClass) {
1343 var select;
1344 var selected;
1345 var borderFields = [];
1346 function setBorderFieldsVisibility(value) {
1347 for (var i = 0; i < borderFields.length; ++i) {
1348 var borderFieldElement = borderFields[i];
1349 borderFieldElement.style.visibility = value ? "hidden" : "visible";
1350 if (!value && (borderFieldElement.tagName.toLowerCase() == "input")) {
1351 borderFieldElement.focus();
1352 borderFieldElement.select();
1353 }
1354 }
1355 };
1356 var fieldset = doc.createElement("fieldset");
1357 fieldset.className = fieldsetClass;
1358 TableOperations.insertLegend(doc, i18n, fieldset, "Frame and borders");
1359 TableOperations.insertSpace(doc, fieldset);
1360 // Gecko reports "solid solid solid solid" for "border-style: solid".
1361 // That is, "top right bottom left" -- we only consider the first value.
1362 selected = el.style.borderStyle;
1363 (selected.match(/([^\s]*)\s/)) && (selected = RegExp.$1);
1364 selectBorderStyle = TableOperations.buildSelectField(doc, el, i18n, fieldset, "f_st_borderStyle", "Border style:", "fr", "floating", "Border style", ["Not set", "No border", "Dotted", "Dashed", "Solid", "Double", "Groove", "Ridge", "Inset", "Outset"], ["not set", "none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"], new RegExp((selected ? selected : "not set"), "i"));
1365 selectBorderStyle.onchange = function() { setBorderFieldsVisibility(this.value == "none"); };
1366 TableOperations.buildInput(doc, el, i18n, fieldset, "f_st_borderWidth", "Border width:", "Border width", "pixels", "5", TableOperations.getLength(el.style.borderWidth), "fr", "floating", "postlabel", borderFields);
1367 TableOperations.insertSpace(doc, fieldset, borderFields);
1368
1369 if (el.tagName.toLowerCase() == "table") {
1370 TableOperations.buildColorField(w, doc, editor, el, i18n, fieldset, "", "Color:", "fr", "colorButton", el.style.borderColor, "borderColor", borderFields);
1371 var label = doc.createElement("label");
1372 label.className = "fl-borderCollapse";
1373 label.htmlFor = "f_st_borderCollapse";
1374 label.innerHTML = i18n["Collapsed borders"];
1375 fieldset.appendChild(label);
1376 borderFields.push(label);
1377 var input = doc.createElement("input");
1378 input.className = "checkbox";
1379 input.type = "checkbox";
1380 input.name = "f_st_borderCollapse";
1381 input.id = "f_st_borderCollapse";
1382 input.defaultChecked = /collapse/i.test(el.style.borderCollapse);
1383 input.checked = input.defaultChecked;
1384 fieldset.appendChild(input);
1385 borderFields.push(input);
1386 TableOperations.insertSpace(doc, fieldset, borderFields);
1387 select = TableOperations.buildSelectField(doc, el, i18n, fieldset, "f_frames", "Frames:", "fr", "floating", "Specifies which sides should have a border", ["Not set", "No sides", "The top side only", "The bottom side only", "The top and bottom sides only", "The right and left sides only", "The left-hand side only", "The right-hand side only", "All four sides"], ["not set", "void", "above", "below", "hsides", "vsides", "lhs", "rhs", "box"], new RegExp((el.frame ? el.frame : "not set"), "i"), borderFields);
1388 TableOperations.insertSpace(doc, fieldset, borderFields);
1389 select = TableOperations.buildSelectField(doc, el, i18n, fieldset, "f_rules", "Rules:", "fr", "floating", "Specifies where rules should be displayed", ["Not set", "No rules", "Rules will appear between rows only", "Rules will appear between columns only", "Rules will appear between all rows and columns"], ["not set", "none", "rows", "cols", "all"], new RegExp((el.rules ? el.rules : "not set"), "i"), borderFields);
1390 } else {
1391 TableOperations.insertSpace(doc, fieldset, borderFields);
1392 TableOperations.buildColorField(w, doc, editor, el, i18n, fieldset, "", "Color:", "fr", "colorButton", el.style.borderColor, "borderColor", borderFields);
1393 }
1394 setBorderFieldsVisibility(selectBorderStyle.value == "none");
1395 TableOperations.insertSpace(doc, fieldset);
1396 content.appendChild(fieldset);
1397 };
1398 TableOperations.buildColorsFieldset = function(w,doc,editor,el,i18n,content) {
1399 var fieldset = doc.createElement("fieldset");
1400 TableOperations.insertLegend(doc, i18n, fieldset, "Background and colors");
1401 var ul = doc.createElement("ul");
1402 fieldset.appendChild(ul);
1403 var li = doc.createElement("li");
1404 ul.appendChild(li);
1405 TableOperations.buildColorField(w, doc, editor, el, i18n, li, "", "FG Color:", "fr", "colorButtonNoFloat", el.style.color, "color");
1406 var li = doc.createElement("li");
1407 ul.appendChild(li);
1408 TableOperations.buildColorField(w, doc, editor, el, i18n, li, "", "Background:", "fr", "colorButtonNoFloat", el.style.backgroundColor, "backgroundColor");
1409 var url;
1410 if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) url = RegExp.$1;
1411 TableOperations.buildInput(doc, el, i18n, li, "f_st_backgroundImage", "Image URL:", "URL of the background image", "", "", url, "", "shorter-value");
1412 content.appendChild(fieldset);
1413 };
1414 TableOperations.insertLegend = function(doc,i18n, fieldset,legend) {
1415 var legendNode = doc.createElement("legend");
1416 legendNode.innerHTML = i18n[legend];
1417 fieldset.appendChild(legendNode);
1418 };
1419 TableOperations.insertSpace = function(doc,fieldset,fields) {
1420 var space = doc.createElement("div");
1421 space.className = "space";
1422 fieldset.appendChild(space);
1423 if(fields) fields.push(space);
1424 };
1425 TableOperations.buildInput = function(doc,el,i18n,fieldset,fieldName,fieldLabel,fieldTitle,postLabel,fieldSize,fieldValue,labelClass,inputClass,postClass,fields) {
1426 var label;
1427 // Field label
1428 if(fieldLabel) {
1429 label = doc.createElement("label");
1430 if(labelClass) label.className = labelClass;
1431 label.innerHTML = i18n[fieldLabel];
1432 label.htmlFor = fieldName;
1433 fieldset.appendChild(label);
1434 if(fields) fields.push(label);
1435 }
1436 // Input field
1437 var input = doc.createElement("input");
1438 input.type = "text";
1439 input.id = fieldName;
1440 input.name = fieldName;
1441 if(inputClass) input.className = inputClass;
1442 if(fieldTitle) input.title = i18n[fieldTitle];
1443 if(fieldSize) input.size = fieldSize;
1444 if(fieldValue) input.value = fieldValue;
1445 fieldset.appendChild(input);
1446 if(fields) fields.push(input);
1447 // Field post label
1448 if(postLabel) {
1449 label = doc.createElement("span");
1450 if(postClass) label.className = postClass;
1451 label.innerHTML = i18n[postLabel];
1452 fieldset.appendChild(label);
1453 if(fields) fields.push(label);
1454 }
1455 };
1456 TableOperations.buildSelectField = function(doc,el,i18n,fieldset,fieldName,fieldLabel,labelClass,selectClass,fieldTitle,options,values,selected,fields,translateOptions) {
1457 if(typeof(translateOptions) == "undefined") var translateOptions = true;
1458 // Field Label
1459 if(fieldLabel) {
1460 var label = doc.createElement("label");
1461 if(labelClass) label.className = labelClass;
1462 label.innerHTML = i18n[fieldLabel];
1463 label.htmlFor = fieldName;
1464 fieldset.appendChild(label);
1465 if(fields) fields.push(label);
1466 }
1467 // Text Alignment Select Box
1468 var select = doc.createElement("select");
1469 if (selectClass) select.className = selectClass;
1470 select.id = fieldName;
1471 select.name = fieldName;
1472 select.title= i18n[fieldTitle];
1473 select.selectedIndex = 0;
1474 var option;
1475 for (var i = 0; i < options.length; ++i) {
1476 option = doc.createElement("option");
1477 select.appendChild(option);
1478 option.value = values[i];
1479 if(translateOptions) option.appendChild(doc.createTextNode(i18n[options[i]]));
1480 else option.appendChild(doc.createTextNode(options[i]));
1481 option.selected = selected.test(option.value);
1482 }
1483 if (select.options.length>1) select.disabled = false;
1484 else select.disabled = true;
1485 fieldset.appendChild(select);
1486 if(fields) fields.push(select);
1487 return select;
1488 };
1489 TableOperations.buildColorField = function(w,doc,editor,el,i18n,fieldset,fieldName,fieldLabel,labelClass, buttonClass, fieldValue,fieldType,fields) {
1490 // Field Label
1491 if(fieldLabel) {
1492 var label = doc.createElement("label");
1493 if(labelClass) label.className = labelClass;
1494 label.innerHTML = i18n[fieldLabel];
1495 fieldset.appendChild(label);
1496 if(fields) fields.push(label);
1497 }
1498 var colorButton = TableOperations.createColorButton(w, doc, editor, fieldValue, fieldType);
1499 colorButton.className = buttonClass;
1500 fieldset.appendChild(colorButton);
1501 if(fields) fields.push(colorButton);
1502 };