1 /***************************************************************
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-2009 Stanislas Rolland <typo3(arobas)sjbr.ca>
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.
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.
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.
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.
29 * This copyright notice MUST APPEAR in all copies of the script!
30 ***************************************************************/
32 * Table Operations Plugin for TYPO3 htmlArea RTE
36 TableOperations
= HTMLArea
.Plugin
.extend({
38 constructor : function(editor
, pluginName
) {
39 this.base(editor
, pluginName
);
43 * This function gets called by the class constructor
45 configurePlugin : function (editor
) {
47 this.classesUrl
= this.editorConfiguration
.classesUrl
;
48 this.buttonsConfiguration
= this.editorConfiguration
.buttons
;
49 this.disableEnterParagraphs
= this.buttonsConfiguration
.table
? this.buttonsConfiguration
.table
.disableEnterParagraphs
: false;
50 this.floatLeft
= "float-left";
51 this.floatRight
= "float-right";
52 this.floatDefault
= "not set";
53 this.useHeaderClass
= "thead";
54 if (this.buttonsConfiguration
.table
&& this.buttonsConfiguration
.table
.properties
) {
55 if (this.buttonsConfiguration
.table
.properties
["float"]) {
56 var floatConfiguration
= this.buttonsConfiguration
.table
.properties
["float"];
57 this.floatLeft
= (floatConfiguration
.left
&& floatConfiguration
.left
.useClass
) ? floatConfiguration
.left
.useClass
: "float-left";
58 this.floatRight
= (floatConfiguration
.right
&& floatConfiguration
.right
.useClass
) ? floatConfiguration
.right
.useClass
: "float-right";
59 this.floatDefault
= (floatConfiguration
.defaultValue
) ? floatConfiguration
.defaultValue
: "not set";
61 if (this.buttonsConfiguration
.table
.properties
.headers
&& this.buttonsConfiguration
.table
.properties
.headers
.both
62 && this.buttonsConfiguration
.table
.properties
.headers
.both
.useHeaderClass
) {
63 this.useHeaderClass
= this.buttonsConfiguration
.table
.properties
.headers
.both
.useHeaderClass
;
65 if (this.buttonsConfiguration
.table
.properties
.tableClass
) {
66 this.defaultClass
= this.buttonsConfiguration
.table
.properties
.tableClass
.defaultValue
;
70 if (this.buttonsConfiguration
.blockstyle
) {
71 this.tags
= this.editorConfiguration
.buttons
.blockstyle
.tags
;
74 this.tableParts
= ["tfoot", "thead", "tbody"];
75 this.convertAlignment
= { "not set" : "none", "left" : "JustifyLeft", "center" : "JustifyCenter", "right" : "JustifyRight", "justify" : "JustifyFull" };
78 * Registering plugin "About" information
80 var pluginInformation
= {
82 developer
: "Mihai Bazon & Stanislas Rolland",
83 developerUrl
: "http://www.sjbr.ca/",
84 copyrightOwner
: "Mihai Bazon & Stanislas Rolland",
85 sponsor
: this.localize("Technische Universitat Ilmenau") + " & Zapatec Inc.",
86 sponsorUrl
: "http://www.tu-ilmenau.de/",
89 this.registerPluginInformation(pluginInformation
);
92 * Registering the buttons
94 var hideToggleBorders
= this.editorConfiguration
.hideTableOperationsInToolbar
&& !(this.buttonsConfiguration
.toggleborders
&& this.buttonsConfiguration
.toggleborders
.keepInToolbar
);
95 var buttonList
= this.buttonList
, buttonId
;
96 for (var i
= 0, n
= buttonList
.length
; i
< n
; ++i
) {
97 var button
= buttonList
[i
];
98 buttonId
= (button
[0] === "InsertTable") ? button
[0] : ("TO-" + button
[0]);
99 var buttonConfiguration
= {
101 tooltip
: this.localize((buttonId
=== "InsertTable") ? "Insert Table" : buttonId
),
102 action
: "onButtonPress",
103 hotKey
: (this.buttonsConfiguration
[button
[2]] ? this.buttonsConfiguration
[button
[2]].hotKey
: null),
105 hide
: ((buttonId
== "TO-toggle-borders") ? hideToggleBorders
: ((button
[0] === "InsertTable") ? false : this.editorConfiguration
.hideTableOperationsInToolbar
)),
108 this.registerButton(buttonConfiguration
);
115 * The list of buttons added by this plugin
118 ["InsertTable", null, "table", true],
119 ["toggle-borders", null, "toggleborders", false],
120 ["table-prop", "table", "tableproperties", true],
121 ["table-restyle", "table", "tablerestyle", false],
122 ["row-prop", "tr", "rowproperties", true],
123 ["row-insert-above", "tr", "rowinsertabove", false],
124 ["row-insert-under", "tr", "rowinsertunder", false],
125 ["row-delete", "tr", "rowdelete", false],
126 ["row-split", "td,th[rowSpan!=1]", "rowsplit", false],
127 ["col-prop", "td,th", "columnproperties", true],
128 ["col-insert-before", "td,th", "columninsertbefore", false],
129 ["col-insert-after", "td,th", "columninsertafter", false],
130 ["col-delete", "td,th", "columndelete", false],
131 ["col-split", "td,th[colSpan!=1]", "columnsplit", false],
132 ["cell-prop", "td,th", "cellproperties", true],
133 ["cell-insert-before", "td,th", "cellinsertbefore", false],
134 ["cell-insert-after", "td,th", "cellinsertafter", false],
135 ["cell-delete", "td,th", "celldelete", false],
136 ["cell-merge", "tr", "cellmerge", false],
137 ["cell-split", "td,th[colSpan!=1,rowSpan!=1]", "cellsplit", false]
141 * Retrieve the closest element having the specified nodeName in the list of
142 * ancestors of the current selection/caret.
144 getClosest : function (nodeName
) {
145 var editor
= this.editor
;
146 var ancestors
= editor
.getAllAncestors();
148 nodeName
= ("" + nodeName
).toLowerCase();
149 for (var i
=0; i
< ancestors
.length
; ++i
) {
150 var el
= ancestors
[i
];
151 if (el
.nodeName
.toLowerCase() == nodeName
) {
160 * Open the table properties dialogue
162 dialogTableProperties : function (buttonId
) {
163 var tablePropertiesInitFunctRef
= this.makeFunctionReference("tablePropertiesInit");
164 var insert
= (buttonId
=== "InsertTable");
167 title
: (insert
? "Insert Table" : "Table Properties"),
168 initialize
: tablePropertiesInitFunctRef
,
169 element
: (insert
? null : this.getClosest("table"))
175 this.dialog
= this.openDialog((insert
? "InsertTable" : "TO-table-prop"), "", "tablePropertiesUpdate", arguments
, dimensions
);
179 * Initialize the table insertion or table properties dialog
181 tablePropertiesInit : function(dialog
) {
182 var doc
= dialog
.document
;
183 var content
= dialog
.content
;
184 var table
= dialog
.arguments
.element
;
185 this.removedFieldsets
= this.buttonsConfiguration
[table
?"tableproperties":"table"].removeFieldsets
? this.buttonsConfiguration
[table
?"tableproperties":"table"].removeFieldsets
: "";
186 this.properties
= this.buttonsConfiguration
.table
.properties
;
187 this.removedProperties
= (this.properties
&& this.properties
.removed
) ? this.properties
.removed
: "";
188 TableOperations
.buildTitle(doc
, content
, dialog
.arguments
.title
);
189 TableOperations
.insertSpace(doc
, content
);
190 if (this.removedFieldsets
.indexOf("description") == -1) {
191 TableOperations
.buildDescriptionFieldset(doc
, table
, content
, "floating");
193 if (this.removedFieldsets
.indexOf("spacing") == -1) TableOperations
.buildSpacingFieldset(doc
, table
, content
);
194 TableOperations
.insertSpace(doc
, content
);
195 this.buildSizeAndHeadersFieldset(doc
, table
, content
, "floating");
196 if (this.removedFieldsets
.indexOf("style") == -1 && dialog
.editor
.config
.customSelects
.BlockStyle
) {
197 var blockStyle
= dialog
.editor
.plugins
.BlockStyle
.instance
;
198 if (blockStyle
&& blockStyle
.cssLoaded
) {
199 this.buildStylingFieldset(doc
, table
, content
, null, dialog
.arguments
.buttonId
);
200 TableOperations
.insertSpace(doc
, content
);
203 this.buildLanguageFieldset(doc
, table
, content
, "floating");
204 if (this.removedFieldsets
.indexOf("layout") == -1) this.buildLayoutFieldset(doc
, table
, content
, "floating");
205 if (this.removedFieldsets
.indexOf("alignment") == -1) this.buildAlignmentFieldset(doc
, table
, content
);
206 TableOperations
.insertSpace(doc
, content
);
207 if (this.removedFieldsets
.indexOf("borders") == -1) this.buildBordersFieldset(dialog
.dialogWindow
, doc
, dialog
.editor
, table
, content
);
208 if (this.removedFieldsets
.indexOf("color") == -1) TableOperations
.buildColorsFieldset(dialog
.dialogWindow
, doc
, dialog
.editor
, table
, content
);
209 dialog
.addButtons("ok", "cancel");
213 * Insert the table or update the table properties and close the dialogue
215 tablePropertiesUpdate : function(dialog
, params
) {
216 if (this.buttonsConfiguration
.table
.properties
&& this.buttonsConfiguration
.table
.properties
.required
) {
217 if (this.buttonsConfiguration
.table
.properties
.required
.indexOf("captionOrSummary") != -1) {
218 if (!/\S/.test(params
.f_caption
) && !/\S/.test(params
.f_summary
)) {
219 dialog
.dialogWindow
.alert(this.localize("captionOrSummary" + "-required"));
220 var el
= dialog
.document
.getElementById("f_caption");
225 var required
= { "f_caption": "caption", "f_summary": "summary" };
226 for (var i
in required
) {
227 if (required
.hasOwnProperty(i
)) {
228 var el
= dialog
.document
.getElementById(i
);
229 if (!el
.value
&& this.buttonsConfiguration
.table
.properties
.required
.indexOf(required
[i
]) != -1) {
230 dialog
.dialogWindow
.alert(this.localize(required
[i
] + "-required"));
238 var doc
= dialog
.editor
._doc
;
239 if (dialog
.buttonId
=== "InsertTable") {
240 var required
= { "f_rows": "You must enter a number of rows", "f_cols": "You must enter a number of columns" };
241 for (var i
in required
) {
242 if (required
.hasOwnProperty(i
)) {
243 var el
= dialog
.document
.getElementById(i
);
245 dialog
.dialogWindow
.alert(this.localize(required
[i
]));
251 var table
= doc
.createElement("table");
252 var tbody
= doc
.createElement("tbody");
253 table
.appendChild(tbody
);
254 for (var i
= params
.f_rows
; --i
>= 0;) {
255 var tr
= doc
.createElement("tr");
256 tbody
.appendChild(tr
);
257 for (var j
= params
.f_cols
; --j
>= 0;) {
258 var td
= doc
.createElement("td");
259 if (HTMLArea
.is_gecko
) td
.innerHTML
= "<br />";
264 var table
= dialog
.arguments
.element
;
266 table
= this.setHeaders(table
, params
);
267 table
= this.processStyle(table
, params
);
268 table
.removeAttribute("border");
269 for (var i
in params
) {
270 if (params
.hasOwnProperty(i
)) {
274 if (/\S/.test(val
)) {
275 // contains non white-space characters
276 var caption
= table
.getElementsByTagName("caption");
278 caption
= caption
[0];
281 var caption
= doc
.createElement("caption");
282 table
.insertBefore(caption
, table
.firstChild
);
284 caption
.innerHTML
= val
;
286 // delete the caption if found
287 if (table
.caption
) table
.deleteCaption();
294 table
.style
.width
= ("" + val
) + params
.f_unit
;
300 table
.cellSpacing
= val
;
303 table
.cellPadding
= val
;
306 table
.frame
= (val
!= "not set") ? val
: "";
309 if (val
!= "not set") table
.rules
= val
;
310 else table
.removeAttribute("rules");
315 HTMLArea
._removeClass(table
, this.floatRight
);
316 HTMLArea
._removeClass(table
, this.floatLeft
);
319 HTMLArea
._removeClass(table
, this.floatLeft
);
320 HTMLArea
._addClass(table
, this.floatRight
);
323 HTMLArea
._removeClass(table
, this.floatRight
);
324 HTMLArea
._addClass(table
, this.floatLeft
);
328 case "f_st_textAlign":
329 if (this.editor
.plugins
.BlockElements
) {
330 this.editor
.plugins
.BlockElements
.instance
.toggleAlignmentClass(table
, this.convertAlignment
[val
]);
331 table
.style
.textAlign
= "";
335 case "f_class_tbody":
336 case "f_class_thead":
337 case "f_class_tfoot":
339 if (i
.length
> 7) tpart
= table
.getElementsByTagName(i
.substring(8,13))[0];
341 this.editor
.plugins
.BlockStyle
.instance
.applyClassChange(tpart
, val
);
345 this.getPluginInstance("Language").setLanguageAttributes(table
, val
);
348 table
.dir
= (val
!= "not set") ? val
: "";
353 if (dialog
.buttonId
=== "InsertTable") {
354 if (HTMLArea
.is_gecko
) {
355 this.editor
.insertNodeAtSelection(table
);
357 table
.id
= "htmlarea_table_insert";
358 this.editor
.insertNodeAtSelection(table
);
359 table
= this.editor
._doc
.getElementById(table
.id
);
362 this.editor
.selectNodeContents(table
.rows
[0].cells
[0], true);
363 if (this.buttonsConfiguration
.toggleborders
&& this.buttonsConfiguration
.toggleborders
.setOnTableCreation
) {
364 this.toggleBorders(true);
371 * Open the row/column/cell properties dialogue
373 dialogRowCellProperties : function (cell
, column
) {
374 // retrieve existing values
376 var element
= this.getClosest("td");
377 if (!element
) var element
= this.getClosest("th");
379 var element
= this.getClosest("tr");
382 var rowCellPropertiesInitFunctRef
= this.makeFunctionReference("rowCellPropertiesInit");
384 title
: (cell
? (column
? "Column Properties" : "Cell Properties") : "Row Properties"),
385 initialize
: rowCellPropertiesInitFunctRef
,
390 this.dialog
= this.openDialog("TO-" + (cell
? (column
? "col-prop" : "cell-prop") :"row-prop"), "", "rowCellPropertiesUpdate", arguments
, { width
: 660, height
: 460 });
395 * Initialize the row/column/cell properties dialogue
397 rowCellPropertiesInit : function(dialog
) {
398 var doc
= dialog
.document
;
399 var content
= dialog
.content
;
400 var element
= dialog
.arguments
.element
;
401 var cell
= dialog
.arguments
.cell
;
402 var column
= dialog
.arguments
.column
;
403 this.removedFieldsets
= this.buttonsConfiguration
[cell
?(column
?"columnproperties":"cellproperties"):"rowproperties"].removeFieldsets
? this.buttonsConfiguration
[cell
?(column
?"columnproperties":"cellproperties"):"rowproperties"].removeFieldsets
: "";
404 this.properties
= this.buttonsConfiguration
[(cell
||column
)?"cellproperties":"rowproperties"].properties
;
405 this.removedProperties
= (this.properties
&& this.properties
.removed
) ? this.properties
.removed
: "";
406 TableOperations
.buildTitle(doc
, content
, (cell
? (column
? "Column Properties" : "Cell Properties") : "Row Properties"));
407 TableOperations
.insertSpace(doc
, content
);
409 if (this.removedFieldsets
.indexOf("columntype") == -1) this.buildCellTypeFieldset(doc
, element
, content
, true);
411 if (this.removedFieldsets
.indexOf("celltype") == -1) this.buildCellTypeFieldset(doc
, element
, content
, false);
413 if (this.removedFieldsets
.indexOf("rowgroup") == -1) TableOperations
.buildRowGroupFieldset(dialog
.dialogWindow
, doc
, dialog
.editor
, element
, content
);
415 if (this.removedFieldsets
.indexOf("style") == -1 && this.editor
.config
.customSelects
.BlockStyle
) {
416 var blockStyle
= this.editor
.plugins
.BlockStyle
.instance
;
417 if (blockStyle
&& blockStyle
.cssLoaded
) {
418 this.buildStylingFieldset(doc
, element
, content
);
419 TableOperations
.insertSpace(doc
, content
);
421 TableOperations
.insertSpace(doc
, content
);
424 TableOperations
.insertSpace(doc
, content
);
426 this.buildLanguageFieldset(doc
, element
, content
, "floating");
427 if (this.removedFieldsets
.indexOf("layout") == -1) this.buildLayoutFieldset(doc
, element
, content
, "floating");
428 if (this.removedFieldsets
.indexOf("alignment") == -1) this.buildAlignmentFieldset(doc
, element
, content
);
429 if (this.removedFieldsets
.indexOf("borders") == -1) this.buildBordersFieldset(dialog
.dialogWindow
, doc
, dialog
.editor
, element
, content
);
430 if (this.removedFieldsets
.indexOf("color") == -1) TableOperations
.buildColorsFieldset(dialog
.dialogWindow
, doc
, dialog
.editor
, element
, content
);
431 dialog
.addButtons("ok", "cancel");
435 * Update the row/column/cell properties
437 rowCellPropertiesUpdate : function(dialog
, params
) {
438 var element
= dialog
.arguments
.element
;
439 var cell
= dialog
.arguments
.cell
;
440 var column
= dialog
.arguments
.column
;
441 var section
= (cell
|| column
) ? element
.parentNode
.parentNode
: element
.parentNode
;
442 var table
= section
.parentNode
;
443 var elements
= new Array();
445 elements
= this.getColumnCells(dialog
.arguments
.element
);
447 elements
.push(dialog
.arguments
.element
);
449 for (var k
= elements
.length
; --k
>= 0;) {
450 var element
= elements
[k
];
451 element
= this.processStyle(element
, params
);
452 for (var i
in params
) {
456 if (val
.substring(0,2) != element
.nodeName
.toLowerCase()) {
457 element
= this.remapCell(element
, val
.substring(0,2));
458 this.editor
.selectNodeContents(element
, true);
460 if (val
.substring(2,10) != element
.scope
) {
461 element
.scope
= val
.substring(2,10);
465 var nodeName
= section
.nodeName
.toLowerCase();
466 if (val
!= nodeName
) {
467 var newSection
= table
.getElementsByTagName(val
)[0];
468 if (!newSection
) var newSection
= table
.insertBefore(dialog
.editor
._doc
.createElement(val
), table
.getElementsByTagName("tbody")[0]);
469 if (nodeName
== "thead" && val
== "tbody") var newElement
= newSection
.insertBefore(element
, newSection
.firstChild
);
470 else var newElement
= newSection
.appendChild(element
);
471 if (!section
.hasChildNodes()) table
.removeChild(section
);
473 if (params
.f_convertCells
) {
474 if (val
== "thead") {
475 this.remapRowCells(element
, "th");
477 this.remapRowCells(element
, "td");
481 case "f_st_textAlign":
482 if (this.editor
.plugins
.BlockElements
) {
483 this.editor
.plugins
.BlockElements
.instance
.toggleAlignmentClass(element
, this.convertAlignment
[val
]);
484 element
.style
.textAlign
= "";
488 this.editor
.plugins
.BlockStyle
.instance
.applyClassChange(element
, val
);
491 this.getPluginInstance("Language").setLanguageAttributes(element
, val
);
494 element
.dir
= (val
!= "not set") ? val
: "";
499 this.reStyleTable(table
);
504 * This function gets called when the plugin is generated
505 * Set table borders if requested by configuration
507 onGenerate : function() {
508 if (this.buttonsConfiguration
.toggleborders
&& this.buttonsConfiguration
.toggleborders
.setOnRTEOpen
) {
509 this.toggleBorders(true);
514 * This function gets called when the toolbar is being updated
516 onUpdateToolbar : function() {
517 if (this.editor
.getMode() === "wysiwyg" && this.editor
.isEditable() && this.isButtonInToolbar("TO-toggle-borders")) {
518 this.editor
._toolbarObjects
["TO-toggle-borders"].state("active", HTMLArea
._hasClass(this.editor
._doc
.body
, 'htmlarea-showtableborders'));
523 * This function gets called when a Table Operations button was pressed.
525 * @param object editor: the editor instance
526 * @param string id: the button id or the key
528 * @return boolean false if action is completed
530 onButtonPress : function (editor
, id
, target
) {
531 // Could be a button or its hotkey
532 var buttonId
= this.translateHotKey(id
);
533 buttonId
= buttonId
? buttonId
: id
;
535 var mozbr
= HTMLArea
.is_gecko
? "<br />" : "";
536 var tableParts
= ["tfoot", "thead", "tbody"];
537 var tablePartsIndex
= { tfoot
: 0, thead
: 1, tbody
: 2 };
539 // helper function that clears the content in a table row
540 function clearRow(tr
) {
541 var tds
= tr
.getElementsByTagName("td");
542 for (var i
= tds
.length
; --i
>= 0;) {
545 td
.innerHTML
= mozbr
;
547 var tds
= tr
.getElementsByTagName("th");
548 for (var i
= tds
.length
; --i
>= 0;) {
551 td
.innerHTML
= mozbr
;
555 function splitRow(td
) {
556 var n
= parseInt("" + td
.rowSpan
);
557 var colSpan
= td
.colSpan
;
558 var nodeName
= td
.nodeName
.toLowerCase();
560 var tr
= td
.parentNode
;
561 var sectionRowIndex
= tr
.sectionRowIndex
;
562 var rows
= tr
.parentNode
.rows
;
563 var index
= td
.cellIndex
;
565 tr
= rows
[++sectionRowIndex
];
567 if (!tr
) tr
= td
.parentNode
.parentNode
.appendChild(editor
._doc
.createElement("tr"));
568 var otd
= editor
._doc
.createElement(nodeName
);
569 otd
.colSpan
= colSpan
;
570 otd
.innerHTML
= mozbr
;
571 tr
.insertBefore(otd
, tr
.cells
[index
]);
575 function splitCol(td
) {
576 var nc
= parseInt("" + td
.colSpan
);
577 var nodeName
= td
.nodeName
.toLowerCase();
579 var tr
= td
.parentNode
;
580 var ref
= td
.nextSibling
;
582 var otd
= editor
._doc
.createElement(nodeName
);
583 otd
.rowSpan
= td
.rowSpan
;
584 otd
.innerHTML
= mozbr
;
585 tr
.insertBefore(otd
, ref
);
589 function splitCell(td
) {
590 var nc
= parseInt("" + td
.colSpan
);
592 var cells
= td
.parentNode
.cells
;
593 var index
= td
.cellIndex
;
595 splitRow(cells
[index
++]);
599 function selectNextNode(el
) {
600 var node
= el
.nextSibling
;
601 while (node
&& node
.nodeType
!= 1) {
602 node
= node
.nextSibling
;
605 node
= el
.previousSibling
;
606 while (node
&& node
.nodeType
!= 1) {
607 node
= node
.previousSibling
;
610 if (!node
) node
= el
.parentNode
;
611 editor
.selectNodeContents(node
);
614 function getSelectedCells(sel
) {
615 var cell
, range
, i
= 0, cells
= [];
617 while (range
= sel
.getRangeAt(i
++)) {
618 cell
= range
.startContainer
.childNodes
[range
.startOffset
];
619 while (!/^(td|th|body)$/.test(cell
.nodeName
.toLowerCase())) cell
= cell
.parentNode
;
620 if (/^(td|th)$/.test(cell
.nodeName
.toLowerCase())) cells
.push(cell
);
623 /* finished walking through selection */
628 function deleteEmptyTable(table
) {
630 for (var j
= tableParts
.length
; --j
>= 0;) {
631 var tablePart
= table
.getElementsByTagName(tableParts
[j
])[0];
632 if (tablePart
) lastPart
= false;
635 selectNextNode(table
);
636 table
.parentNode
.removeChild(table
);
640 function computeCellIndexes(table
) {
643 for (var m
= tableParts
.length
; --m
>= 0;) {
644 var tablePart
= table
.getElementsByTagName(tableParts
[m
])[0];
646 var rows
= tablePart
.rows
;
647 for (var i
= 0, n
= rows
.length
; i
< n
; i
++) {
648 var cells
= rows
[i
].cells
;
649 for (var j
=0; j
< cells
.length
; j
++) {
651 var rowIndex
= cell
.parentNode
.rowIndex
;
652 var cellId
= tableParts
[m
]+"-"+rowIndex
+"-"+cell
.cellIndex
;
653 var rowSpan
= cell
.rowSpan
|| 1;
654 var colSpan
= cell
.colSpan
|| 1;
656 if(typeof(matrix
[rowIndex
])=="undefined") { matrix
[rowIndex
] = []; }
657 // Find first available column in the first row
658 for (var k
=0; k
<matrix
[rowIndex
].length
+1; k
++) {
659 if (typeof(matrix
[rowIndex
][k
])=="undefined") {
664 lookup
[cellId
] = firstAvailCol
;
665 for (var k
=rowIndex
; k
<rowIndex
+rowSpan
; k
++) {
666 if (typeof(matrix
[k
])=="undefined") { matrix
[k
] = []; }
667 var matrixrow
= matrix
[k
];
668 for (var l
=firstAvailCol
; l
<firstAvailCol
+colSpan
; l
++) {
679 function getActualCellIndex(cell
, lookup
) {
680 return lookup
[cell
.parentNode
.parentNode
.nodeName
.toLowerCase()+"-"+cell
.parentNode
.rowIndex
+"-"+cell
.cellIndex
];
685 case "TO-row-insert-above":
686 case "TO-row-insert-under":
687 var tr
= this.getClosest("tr");
689 var otr
= tr
.cloneNode(true);
691 otr
= tr
.parentNode
.insertBefore(otr
, (/under/.test(buttonId
) ? tr
.nextSibling
: tr
));
692 this.editor
.selectNodeContents(otr
.firstChild
, true);
693 this.reStyleTable(tr
.parentNode
.parentNode
);
695 case "TO-row-delete":
696 var tr
= this.getClosest("tr");
698 var part
= tr
.parentNode
;
699 var table
= part
.parentNode
;
700 if(part
.rows
.length
== 1) { // this the last row, delete the whole table part
701 selectNextNode(part
);
702 table
.removeChild(part
);
703 deleteEmptyTable(table
);
705 // set the caret first to a position that doesn't disappear.
707 part
.removeChild(tr
);
709 this.reStyleTable(table
);
712 var cell
= this.getClosest("td");
713 if (!cell
) var cell
= this.getClosest("th");
715 var sel
= editor
._getSelection();
716 if (HTMLArea
.is_gecko
&& !sel
.isCollapsed
&& !HTMLArea
.is_safari
&& !HTMLArea
.is_opera
) {
717 var cells
= getSelectedCells(sel
);
718 for (i
= 0; i
< cells
.length
; ++i
) splitRow(cells
[i
]);
725 case "TO-col-insert-before":
726 case "TO-col-insert-after":
727 var cell
= this.getClosest("td");
728 if (!cell
) var cell
= this.getClosest("th");
730 var index
= cell
.cellIndex
;
731 var table
= cell
.parentNode
.parentNode
.parentNode
;
732 for (var j
= tableParts
.length
; --j
>= 0;) {
733 var tablePart
= table
.getElementsByTagName(tableParts
[j
])[0];
735 var rows
= tablePart
.rows
;
736 for (var i
= rows
.length
; --i
>= 0;) {
738 var ref
= tr
.cells
[index
+ (/after/.test(buttonId
) ? 1 : 0)];
740 var otd
= editor
._doc
.createElement(tr
.lastChild
.nodeName
.toLowerCase());
741 otd
.innerHTML
= mozbr
;
744 var otd
= editor
._doc
.createElement(ref
.nodeName
.toLowerCase());
745 otd
.innerHTML
= mozbr
;
746 tr
.insertBefore(otd
, ref
);
751 this.reStyleTable(table
);
754 var cell
= this.getClosest("td");
755 if (!cell
) var cell
= this.getClosest("th");
757 var sel
= editor
._getSelection();
758 if (HTMLArea
.is_gecko
&& !sel
.isCollapsed
&& !HTMLArea
.is_safari
&& !HTMLArea
.is_opera
) {
759 var cells
= getSelectedCells(sel
);
760 for (i
= 0; i
< cells
.length
; ++i
) splitCol(cells
[i
]);
764 this.reStyleTable(table
);
766 case "TO-col-delete":
767 var cell
= this.getClosest("td");
768 if (!cell
) var cell
= this.getClosest("th");
770 var index
= cell
.cellIndex
;
771 var part
= cell
.parentNode
.parentNode
;
772 var table
= part
.parentNode
;
774 for (var j
= tableParts
.length
; --j
>= 0;) {
775 var tablePart
= table
.getElementsByTagName(tableParts
[j
])[0];
777 var rows
= tablePart
.rows
;
778 var lastColumn
= true;
779 for (var i
= rows
.length
; --i
>= 0;) {
780 if(rows
[i
].cells
.length
> 1) lastColumn
= false;
783 // this is the last column, delete the whole tablepart
784 // set the caret first to a position that doesn't disappear
785 selectNextNode(tablePart
);
786 table
.removeChild(tablePart
);
788 // set the caret first to a position that doesn't disappear
789 if (part
== tablePart
) selectNextNode(cell
);
790 for (var i
= rows
.length
; --i
>= 0;) {
791 if(rows
[i
].cells
[index
]) rows
[i
].removeChild(rows
[i
].cells
[index
]);
798 // the last table section was deleted: delete the whole table
799 // set the caret first to a position that doesn't disappear
800 selectNextNode(table
);
801 table
.parentNode
.removeChild(table
);
803 this.reStyleTable(table
);
807 case "TO-cell-split":
808 var cell
= this.getClosest("td");
809 if (!cell
) var cell
= this.getClosest("th");
811 var sel
= editor
._getSelection();
812 if (HTMLArea
.is_gecko
&& !sel
.isCollapsed
&& !HTMLArea
.is_safari
&& !HTMLArea
.is_opera
) {
813 var cells
= getSelectedCells(sel
);
814 for (i
= 0; i
< cells
.length
; ++i
) splitCell(cells
[i
]);
818 this.reStyleTable(table
);
820 case "TO-cell-insert-before":
821 case "TO-cell-insert-after":
822 var cell
= this.getClosest("td");
823 if (!cell
) var cell
= this.getClosest("th");
825 var tr
= cell
.parentNode
;
826 var otd
= editor
._doc
.createElement(cell
.nodeName
.toLowerCase());
827 otd
.innerHTML
= mozbr
;
828 tr
.insertBefore(otd
, (/after/.test(buttonId
) ? cell
.nextSibling
: cell
));
829 this.reStyleTable(tr
.parentNode
.parentNode
);
831 case "TO-cell-delete":
832 var cell
= this.getClosest("td");
833 if (!cell
) var cell
= this.getClosest("th");
835 var row
= cell
.parentNode
;
836 if(row
.cells
.length
== 1) { // this is the only cell in the row, delete the row
837 var part
= row
.parentNode
;
838 var table
= part
.parentNode
;
839 if (part
.rows
.length
== 1) { // this the last row, delete the whole table part
840 selectNextNode(part
);
841 table
.removeChild(part
);
842 deleteEmptyTable(table
);
845 part
.removeChild(row
);
848 // set the caret first to a position that doesn't disappear
849 selectNextNode(cell
);
850 row
.removeChild(cell
);
852 this.reStyleTable(table
);
854 case "TO-cell-merge":
855 var sel
= editor
._getSelection();
857 var rows
= new Array();
858 for (var k
= tableParts
.length
; --k
>= 0;) rows
[k
] = [];
861 if (HTMLArea
.is_gecko
&& !HTMLArea
.is_safari
&& !HTMLArea
.is_opera
) {
863 while (range
= sel
.getRangeAt(i
++)) {
864 var td
= range
.startContainer
.childNodes
[range
.startOffset
];
865 if (td
.parentNode
!= row
) {
866 (cells
) && rows
[tablePartsIndex
[row
.parentNode
.nodeName
.toLowerCase()]].push(cells
);
873 /* finished walking through selection */
875 try { rows
[tablePartsIndex
[row
.parentNode
.nodeName
.toLowerCase()]].push(cells
); } catch(e
) { }
877 // Internet Explorer, Safari and Opera
878 var cell
= this.getClosest("td");
879 if (!cell
) var cell
= this.getClosest("th");
881 alert(this.localize("Please click into some cell"));
884 var tr
= cell
.parentElement
;
885 var no_cols
= parseInt(prompt(this.localize("How many columns would you like to merge?"), 2));
887 var no_rows
= parseInt(prompt(this.localize("How many rows would you like to merge?"), 2));
889 var lookup
= computeCellIndexes(cell
.parentNode
.parentNode
.parentNode
);
890 var first_index
= getActualCellIndex(cell
, lookup
);
891 // Collect cells on first row
892 var td
= cell
, colspan
= 0;
894 for (var i
= no_cols
; --i
>= 0;) {
897 var last_index
= getActualCellIndex(td
, lookup
);
900 rows
[tablePartsIndex
[tr
.parentNode
.nodeName
.toLowerCase()]].push(cells
);
901 // Collect cells on following rows
902 var index
, first_index_found
, last_index_found
;
903 for (var j
= 1; j
< no_rows
; ++j
) {
907 first_index_found
= false;
908 for (var i
= 0; i
< tr
.cells
.length
; ++i
) {
911 index
= getActualCellIndex(td
, lookup
);
912 if (index
> last_index
) break;
913 if (index
== first_index
) first_index_found
= true;
914 if (index
>= first_index
) cells
.push(td
);
916 // If not rectangle, we quit!
917 if (!first_index_found
) break;
918 rows
[tablePartsIndex
[tr
.parentNode
.nodeName
.toLowerCase()]].push(cells
);
921 for (var k
= tableParts
.length
; --k
>= 0;) {
925 var cellColSpan
, maxCellColSpan
= 0;
926 if (rows
[k
] && rows
[k
][0]) {
927 for (var i
= 0; i
< rows
[k
].length
; ++i
) {
928 var cells
= rows
[k
][i
];
930 if (!cells
) continue;
931 cellRowSpan
+= cells
[0].rowSpan
? cells
[0].rowSpan
: 1;
932 for (var j
= 0; j
< cells
.length
; ++j
) {
934 row
= cell
.parentNode
;
935 cellHTML
+= cell
.innerHTML
;
936 cellColSpan
+= cell
.colSpan
? cell
.colSpan
: 1;
938 cell
.parentNode
.removeChild(cell
);
939 if(!row
.cells
.length
) row
.parentNode
.removeChild(row
);
942 if (maxCellColSpan
< cellColSpan
) {
943 maxCellColSpan
= cellColSpan
;
946 var td
= rows
[k
][0][0];
947 td
.innerHTML
= cellHTML
;
948 td
.rowSpan
= cellRowSpan
;
949 td
.colSpan
= maxCellColSpan
;
950 editor
.selectNodeContents(td
);
953 this.reStyleTable(table
);
956 // CREATION AND PROPERTIES
958 case "TO-table-prop":
959 this.dialogTableProperties(buttonId
);
961 case "TO-table-restyle":
962 this.reStyleTable(this.getClosest("table"));
965 this.dialogRowCellProperties(false, false);
968 this.dialogRowCellProperties(true, true);
971 this.dialogRowCellProperties(true, false);
973 case "TO-toggle-borders":
974 this.toggleBorders();
977 alert("Button [" + buttonId
+ "] not yet implemented");
982 * Returns an array of all cells in the column containing the given cell
984 * @param object cell: the cell serving as reference point for the column
986 * @return array the array of cells of the column
988 getColumnCells : function (cell
) {
989 var cells
= new Array();
990 var index
= cell
.cellIndex
;
991 var table
= cell
.parentNode
.parentNode
.parentNode
;
992 for (var j
= this.tableParts
.length
; --j
>= 0;) {
993 var tablePart
= table
.getElementsByTagName(this.tableParts
[j
])[0];
995 var rows
= tablePart
.rows
;
996 for (var i
= rows
.length
; --i
>= 0;) {
997 if(rows
[i
].cells
.length
> index
) {
998 cells
.push(rows
[i
].cells
[index
]);
1007 * Toggles the display of borders on tables and table cells
1009 * @param boolean forceBorders: if set, borders are displayed whatever the current state
1013 toggleBorders : function (forceBorders
) {
1014 var body
= this.editor
._doc
.body
;
1015 if (!HTMLArea
._hasClass(body
, 'htmlarea-showtableborders')) {
1016 HTMLArea
._addClass(body
,'htmlarea-showtableborders');
1017 } else if (!forceBorders
) {
1018 HTMLArea
._removeClass(body
,'htmlarea-showtableborders');
1023 * Applies to rows/cells the alternating and counting classes of an alternating or counting style scheme
1025 * @param object table: the table to be re-styled
1029 reStyleTable : function (table
) {
1031 if (this.classesUrl
&& (typeof(HTMLArea
.classesAlternating
) === "undefined" || typeof(HTMLArea
.classesCounting
) === "undefined")) {
1032 this.getJavascriptFile(this.classesUrl
);
1034 var classNames
= table
.className
.trim().split(" ");
1035 for (var i
= classNames
.length
; --i
>= 0;) {
1036 var classConfiguration
= HTMLArea
.classesAlternating
[classNames
[i
]];
1037 if (classConfiguration
&& classConfiguration
.rows
) {
1038 if (classConfiguration
.rows
.oddClass
&& classConfiguration
.rows
.evenClass
) {
1039 this.alternateRows(table
, classConfiguration
);
1042 if (classConfiguration
&& classConfiguration
.columns
) {
1043 if (classConfiguration
.columns
.oddClass
&& classConfiguration
.columns
.evenClass
) {
1044 this.alternateColumns(table
, classConfiguration
);
1047 classConfiguration
= HTMLArea
.classesCounting
[classNames
[i
]];
1048 if (classConfiguration
&& classConfiguration
.rows
) {
1049 if (classConfiguration
.rows
.rowClass
) {
1050 this.countRows(table
, classConfiguration
);
1053 if (classConfiguration
&& classConfiguration
.columns
) {
1054 if (classConfiguration
.columns
.columnClass
) {
1055 this.countColumns(table
, classConfiguration
);
1063 * Removes from rows/cells the alternating classes of an alternating style scheme
1065 * @param object table: the table to be re-styled
1066 * @param string removeClass: the name of the class that identifies the alternating style scheme
1070 removeAlternatingClasses : function (table
, removeClass
) {
1072 if (this.classesUrl
&& typeof(HTMLArea
.classesAlternating
) === "undefined") {
1073 this.getJavascriptFile(this.classesUrl
);
1075 var classConfiguration
= HTMLArea
.classesAlternating
[removeClass
];
1076 if (classConfiguration
) {
1077 if (classConfiguration
.rows
&& classConfiguration
.rows
.oddClass
&& classConfiguration
.rows
.evenClass
) {
1078 this.alternateRows(table
, classConfiguration
, true);
1080 if (classConfiguration
.columns
&& classConfiguration
.columns
.oddClass
&& classConfiguration
.columns
.evenClass
) {
1081 this.alternateColumns(table
, classConfiguration
, true);
1088 * Applies/removes the alternating classes of an alternating rows style scheme
1090 * @param object table: the table to be re-styled
1091 * @param object classConfifuration: the alternating sub-array of the configuration of the class
1092 * @param boolean remove: if true, the classes are removed
1096 alternateRows : function (table
, classConfiguration
, remove
) {
1097 var oddClass
= { tbody
: classConfiguration
.rows
.oddClass
, thead
: classConfiguration
.rows
.oddHeaderClass
};
1098 var evenClass
= { tbody
: classConfiguration
.rows
.evenClass
, thead
: classConfiguration
.rows
.evenHeaderClass
};
1099 var startAt
= parseInt(classConfiguration
.rows
.startAt
);
1100 startAt
= remove
? 1 : (startAt
? startAt
: 1);
1101 var rows
= table
.rows
, type
, odd
, even
;
1102 // Loop through the rows
1103 for (var i
= startAt
-1, n
= rows
.length
; i
< n
; i
++) {
1105 type
= (row
.parentNode
.nodeName
.toLowerCase() == "thead") ? "thead" : "tbody";
1106 odd
= oddClass
[type
];
1107 even
= evenClass
[type
];
1109 HTMLArea
._removeClass(row
, odd
);
1110 HTMLArea
._removeClass(row
, even
);
1111 // Check if i is even, and apply classes for both possible results
1112 } else if (odd
&& even
) {
1114 if (HTMLArea
._hasClass(row
, even
)) {
1115 HTMLArea
._removeClass(row
, even
);
1117 HTMLArea
._addClass(row
, odd
);
1119 if (HTMLArea
._hasClass(row
, odd
)) {
1120 HTMLArea
._removeClass(row
, odd
);
1122 HTMLArea
._addClass(row
, even
);
1129 * Applies/removes the alternating classes of an alternating columns style scheme
1131 * @param object table: the table to be re-styled
1132 * @param object classConfifuration: the alternating sub-array of the configuration of the class
1133 * @param boolean remove: if true, the classes are removed
1137 alternateColumns : function (table
, classConfiguration
, remove
) {
1138 var oddClass
= { td
: classConfiguration
.columns
.oddClass
, th
: classConfiguration
.columns
.oddHeaderClass
};
1139 var evenClass
= { td
: classConfiguration
.columns
.evenClass
, th
: classConfiguration
.columns
.evenHeaderClass
};
1140 var startAt
= parseInt(classConfiguration
.columns
.startAt
);
1141 startAt
= remove
? 1 : (startAt
? startAt
: 1);
1142 var rows
= table
.rows
, type
, odd
, even
;
1143 // Loop through the rows of the table
1144 for (var i
= rows
.length
; --i
>= 0;) {
1145 // Loop through the cells
1146 var cells
= rows
[i
].cells
;
1147 for (var j
= startAt
-1, n
= cells
.length
; j
< n
; j
++) {
1148 var cell
= cells
[j
];
1149 type
= cell
.nodeName
.toLowerCase();
1150 odd
= oddClass
[type
];
1151 even
= evenClass
[type
];
1153 if (odd
) HTMLArea
._removeClass(cell
, odd
);
1154 if (even
) HTMLArea
._removeClass(cell
, even
);
1155 } else if (odd
&& even
) {
1156 // Check if j+startAt is even, and apply classes for both possible results
1158 if (HTMLArea
._hasClass(cell
, even
)) {
1159 HTMLArea
._removeClass(cell
, even
);
1161 HTMLArea
._addClass(cell
, odd
);
1163 if (HTMLArea
._hasClass(cell
, odd
)) {
1164 HTMLArea
._removeClass(cell
, odd
);
1166 HTMLArea
._addClass(cell
, even
);
1174 * Removes from rows/cells the counting classes of an counting style scheme
1176 * @param object table: the table to be re-styled
1177 * @param string removeClass: the name of the class that identifies the counting style scheme
1181 removeCountingClasses : function (table
, removeClass
) {
1183 if (this.classesUrl
&& typeof(HTMLArea
.classesCounting
) === "undefined") {
1184 this.getJavascriptFile(this.classesUrl
);
1186 var classConfiguration
= HTMLArea
.classesCounting
[removeClass
];
1187 if (classConfiguration
) {
1188 if (classConfiguration
.rows
&& classConfiguration
.rows
.rowClass
) {
1189 this.countRows(table
, classConfiguration
, true);
1191 if (classConfiguration
.columns
&& classConfiguration
.columns
.columnClass
) {
1192 this.countColumns(table
, classConfiguration
, true);
1199 * Applies/removes the counting classes of an counting rows style scheme
1201 * @param object table: the table to be re-styled
1202 * @param object classConfifuration: the counting sub-array of the configuration of the class
1203 * @param boolean remove: if true, the classes are removed
1207 countRows : function (table
, classConfiguration
, remove
) {
1208 var rowClass
= { tbody
: classConfiguration
.rows
.rowClass
, thead
: classConfiguration
.rows
.rowHeaderClass
};
1209 var rowLastClass
= { tbody
: classConfiguration
.rows
.rowLastClass
, thead
: classConfiguration
.rows
.rowHeaderLastClass
};
1210 var startAt
= parseInt(classConfiguration
.rows
.startAt
);
1211 startAt
= remove
? 1 : (startAt
? startAt
: 1);
1212 var rows
= table
.rows
, type
, baseClassName
, rowClassName
, lastRowClassName
;
1213 // Loop through the rows
1214 for (var i
= startAt
-1, n
= rows
.length
; i
< n
; i
++) {
1216 type
= (row
.parentNode
.nodeName
.toLowerCase() == "thead") ? "thead" : "tbody";
1217 baseClassName
= rowClass
[type
];
1218 rowClassName
= baseClassName
+ (i
+1);
1219 lastRowClassName
= rowLastClass
[type
];
1221 if (baseClassName
) {
1222 HTMLArea
._removeClass(row
, rowClassName
);
1224 if (lastRowClassName
&& i
== n
-1) {
1225 HTMLArea
._removeClass(row
, lastRowClassName
);
1228 if (baseClassName
) {
1229 if (HTMLArea
._hasClass(row
, baseClassName
, true)) {
1230 HTMLArea
._removeClass(row
, baseClassName
, true);
1232 HTMLArea
._addClass(row
, rowClassName
);
1234 if (lastRowClassName
) {
1236 HTMLArea
._addClass(row
, lastRowClassName
);
1237 } else if (HTMLArea
._hasClass(row
, lastRowClassName
)) {
1238 HTMLArea
._removeClass(row
, lastRowClassName
);
1246 * Applies/removes the counting classes of a counting columns style scheme
1248 * @param object table: the table to be re-styled
1249 * @param object classConfifuration: the counting sub-array of the configuration of the class
1250 * @param boolean remove: if true, the classes are removed
1254 countColumns : function (table
, classConfiguration
, remove
) {
1255 var columnClass
= { td
: classConfiguration
.columns
.columnClass
, th
: classConfiguration
.columns
.columnHeaderClass
};
1256 var columnLastClass
= { td
: classConfiguration
.columns
.columnLastClass
, th
: classConfiguration
.columns
.columnHeaderLastClass
};
1257 var startAt
= parseInt(classConfiguration
.columns
.startAt
);
1258 startAt
= remove
? 1 : (startAt
? startAt
: 1);
1259 var rows
= table
.rows
, type
, baseClassName
, columnClassName
, lastColumnClassName
;
1260 // Loop through the rows of the table
1261 for (var i
= rows
.length
; --i
>= 0;) {
1262 // Loop through the cells
1263 var cells
= rows
[i
].cells
;
1264 for (var j
= startAt
-1, n
= cells
.length
; j
< n
; j
++) {
1265 var cell
= cells
[j
];
1266 type
= cell
.nodeName
.toLowerCase();
1267 baseClassName
= columnClass
[type
];
1268 columnClassName
= baseClassName
+ (j
+1);
1269 lastColumnClassName
= columnLastClass
[type
];
1271 if (baseClassName
) {
1272 HTMLArea
._removeClass(cell
, columnClassName
);
1274 if (lastColumnClassName
&& j
== n
-1) {
1275 HTMLArea
._removeClass(cell
, lastColumnClassName
);
1278 if (baseClassName
) {
1279 if (HTMLArea
._hasClass(cell
, baseClassName
, true)) {
1280 HTMLArea
._removeClass(cell
, baseClassName
, true);
1282 HTMLArea
._addClass(cell
, columnClassName
);
1284 if (lastColumnClassName
) {
1286 HTMLArea
._addClass(cell
, lastColumnClassName
);
1287 } else if (HTMLArea
._hasClass(cell
, lastColumnClassName
)) {
1288 HTMLArea
._removeClass(cell
, lastColumnClassName
);
1297 * This function sets the headers cells on the table (top, left, both or none)
1299 * @param object table: the table being edited
1300 * @param object params: the field values entered in the form
1302 * @return object the modified table
1304 setHeaders : function (table
, params
) {
1305 var headers
= params
.f_headers
;
1306 var doc
= this.editor
._doc
;
1307 var tbody
= table
.tBodies
[0];
1308 var thead
= table
.tHead
;
1309 if (thead
&& !thead
.rows
.length
&& !tbody
.rows
.length
) {
1310 // Table is degenerate
1313 if (headers
== "top") {
1315 var thead
= doc
.createElement("thead");
1316 thead
= table
.insertBefore(thead
, tbody
);
1318 if (!thead
.rows
.length
) {
1319 var firstRow
= thead
.appendChild(tbody
.rows
[0]);
1321 var firstRow
= thead
.rows
[0];
1323 HTMLArea
._removeClass(firstRow
, this.useHeaderClass
);
1326 var rows
= thead
.rows
;
1328 for (var i
= rows
.length
; --i
>= 0 ;) {
1329 this.remapRowCells(rows
[i
], "td");
1330 if (tbody
.rows
.length
) {
1331 tbody
.insertBefore(rows
[i
], tbody
.rows
[0]);
1333 tbody
.appendChild(rows
[i
]);
1337 table
.removeChild(thead
);
1340 if (headers
== "both") {
1341 var firstRow
= tbody
.rows
[0];
1342 HTMLArea
._addClass(firstRow
, this.useHeaderClass
);
1343 } else if (headers
!= "top") {
1344 var firstRow
= tbody
.rows
[0];
1345 HTMLArea
._removeClass(firstRow
, this.useHeaderClass
);
1346 this.remapRowCells(firstRow
, "td");
1348 if (headers
== "top" || headers
== "both") {
1349 this.remapRowCells(firstRow
, "th");
1351 if (headers
== "left") {
1352 var firstRow
= tbody
.rows
[0];
1354 if (headers
== "left" || headers
== "both") {
1355 var rows
= tbody
.rows
;
1356 for (var i
= rows
.length
; --i
>= 0 ;) {
1357 if (i
|| rows
[i
] == firstRow
) {
1358 if (rows
[i
].cells
[0].nodeName
.toLowerCase() != "th") {
1359 var th
= this.remapCell(rows
[i
].cells
[0], "th");
1365 var rows
= tbody
.rows
;
1366 for (var i
= rows
.length
; --i
>= 0 ;) {
1367 if (rows
[i
].cells
[0].nodeName
.toLowerCase() != "td") {
1368 rows
[i
].cells
[0].scope
= "";
1369 var td
= this.remapCell(rows
[i
].cells
[0], "td");
1373 this.reStyleTable(table
);
1378 * This function remaps the given cell to the specified node name
1380 remapCell : function(element
, nodeName
) {
1381 var newCell
= this.editor
.convertNode(element
, nodeName
);
1382 var attributes
= element
.attributes
, attributeName
, attributeValue
;
1383 for (var i
= attributes
.length
; --i
>= 0;) {
1384 attributeName
= attributes
.item(i
).nodeName
;
1385 attributeValue
= element
.getAttribute(attributeName
);
1386 if (attributeValue
) newCell
.setAttribute(attributeName
, attributeValue
);
1388 // In IE, the above fails to update the classname and style attributes.
1389 if (HTMLArea
.is_ie
) {
1390 if (element
.style
.cssText
) {
1391 newCell
.style
.cssText
= element
.style
.cssText
;
1393 if (element
.className
) {
1394 newCell
.setAttribute("className", element
.className
);
1396 newCell
.removeAttribute("className");
1400 if (this.tags
&& this.tags
[nodeName
] && this.tags
[nodeName
].allowedClasses
) {
1401 if (newCell
.className
&& /\S/.test(newCell
.className
)) {
1402 var allowedClasses
= this.tags
[nodeName
].allowedClasses
;
1403 var classNames
= newCell
.className
.trim().split(" ");
1404 for (var i
= classNames
.length
; --i
>= 0;) {
1405 if (!allowedClasses
.test(classNames
[i
])) {
1406 HTMLArea
._removeClass(newCell
, classNames
[i
]);
1414 remapRowCells : function (row
, toType
) {
1415 var cells
= row
.cells
;
1416 if (toType
=== "th") {
1417 for (var i
= cells
.length
; --i
>= 0 ;) {
1418 if (cells
[i
].nodeName
.toLowerCase() != "th") {
1419 var th
= this.remapCell(cells
[i
], "th");
1424 for (var i
= cells
.length
; --i
>= 0 ;) {
1425 if (cells
[i
].nodeName
.toLowerCase() != "td") {
1426 var td
= this.remapCell(cells
[i
], "td");
1434 * This function applies the style properties found in params to the given element
1436 * @param object element: the element
1437 * @param object params: the properties
1439 * @return object the modified element
1441 processStyle : function (element
, params
) {
1442 var style
= element
.style
;
1443 if (HTMLArea
.is_ie
) {
1444 style
.styleFloat
= "";
1446 style
.cssFloat
= "";
1448 style
.textAlign
= "";
1449 for (var i
in params
) {
1450 if (params
.hasOwnProperty(i
)) {
1451 var val
= params
[i
];
1453 case "f_st_backgroundColor":
1454 style
.backgroundColor
= val
;
1459 case "f_st_backgroundImage":
1460 if (/\S/.test(val
)) {
1461 style
.backgroundImage
= "url(" + val
+ ")";
1463 style
.backgroundImage
= "";
1466 case "f_st_borderWidth":
1467 if (/\S/.test(val
)) {
1468 style
.borderWidth
= val
+ "px";
1470 style
.borderWidth
= "";
1472 if (params
.f_st_borderStyle
== "none") style
.borderWidth
= "0px";
1473 if (params
.f_st_borderStyle
== "not set") style
.borderWidth
= "";
1475 case "f_st_borderStyle":
1476 style
.borderStyle
= (val
!= "not set") ? val
: "";
1478 case "f_st_borderColor":
1479 style
.borderColor
= val
;
1481 case "f_st_borderCollapse":
1482 style
.borderCollapse
= val
? "collapse" : "";
1485 if (/\S/.test(val
)) {
1486 style
.width
= val
+ params
.f_st_widthUnit
;
1492 if (/\S/.test(val
)) {
1493 style
.height
= val
+ params
.f_st_heightUnit
;
1498 case "f_st_textAlign":
1499 style
.textAlign
= (val
!= "not set") ? val
: "";
1501 case "f_st_vertAlign":
1502 style
.verticalAlign
= (val
!= "not set") ? val
: "";
1511 * This function creates a Size and Headers fieldset to be added to the form
1513 * @param object doc: the dialog document
1514 * @param object table: the table being edited
1515 * @param object content: the content div of the dialog window
1519 buildSizeAndHeadersFieldset : function (doc
, table
, content
, fieldsetClass
) {
1520 var fieldset
= doc
.createElement("fieldset");
1521 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1523 TableOperations
.insertLegend(doc
, fieldset
, "Size and Headers");
1524 TableOperations
.buildInput(doc
, fieldset
, "f_rows", "Rows:", "Number of rows", "", "5", ((this.properties
&& this.properties
.numberOfRows
&& this.properties
.numberOfRows
.defaultValue
) ? this.properties
.numberOfRows
.defaultValue
: "2"), "fr");
1525 TableOperations
.insertSpace(doc
, fieldset
);
1526 TableOperations
.buildInput(doc
, fieldset
, "f_cols", "Cols:", "Number of columns", "", "5", ((this.properties
&& this.properties
.numberOfColumns
&& this.properties
.numberOfColumns
.defaultValue
) ? this.properties
.numberOfColumns
.defaultValue
: "4"), "fr");
1528 TableOperations
.insertLegend(doc
, fieldset
, "Headers");
1530 if (this.removedProperties
.indexOf("headers") == -1) {
1531 var ul
= doc
.createElement("ul");
1532 fieldset
.appendChild(ul
);
1533 var li
= doc
.createElement("li");
1536 var selected
= (this.properties
&& this.properties
.headers
&& this.properties
.headers
.defaultValue
) ? this.properties
.headers
.defaultValue
: "top";
1538 var selected
= "none";
1539 var thead
= table
.getElementsByTagName("thead");
1540 var tbody
= table
.getElementsByTagName("tbody");
1541 if (thead
.length
&& thead
[0].rows
.length
) {
1543 } else if (tbody
.length
&& tbody
[0].rows
.length
) {
1544 if (HTMLArea
._hasClass(tbody
[0].rows
[0], this.useHeaderClass
)) {
1546 } else if (tbody
[0].rows
[0].cells
.length
&& tbody
[0].rows
[0].cells
[0].nodeName
.toLowerCase() == "th") {
1551 var selectHeaders
= TableOperations
.buildSelectField(doc
, li
, "f_headers", "Headers:", "fr", "floating", "Table headers", ["No header cells", "Header cells on top", "Header cells on left", "Header cells on top and left"], ["none", "top", "left", "both"], new RegExp((selected
? selected
: "top"), "i"));
1552 this.removeOptions(selectHeaders
, "headers");
1554 TableOperations
.insertSpace(doc
, fieldset
);
1555 content
.appendChild(fieldset
);
1558 buildLayoutFieldset : function(doc
, el
, content
, fieldsetClass
) {
1559 var select
, selected
;
1560 var fieldset
= doc
.createElement("fieldset");
1561 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1562 TableOperations
.insertLegend(doc
, fieldset
, "Layout");
1563 var f_st_width
= el
? TableOperations
.getLength(el
.style
.width
) : ((this.properties
&& this.properties
.width
&& this.properties
.width
.defaultValue
) ? this.properties
.width
.defaultValue
: "");
1564 var f_st_height
= el
? TableOperations
.getLength(el
.style
.height
) : ((this.properties
&& this.properties
.height
&& this.properties
.height
.defaultValue
) ? this.properties
.height
.defaultValue
: "");
1565 var selectedWidthUnit
= el
? (/%/.test(el.style.width) ? '%' : (/px/.test(el
.style
.width
) ? 'px' : 'em')) : ((this.properties
&& this.properties
.widthUnit
&&this.properties
.widthUnit
.defaultValue
) ? this.properties
.widthUnit
.defaultValue
: "%");
1566 var selectedHeightUnit
= el
? (/%/.test(el.style.height) ? '%' : (/px/.test(el
.style
.height
) ? 'px' : 'em')) : ((this.properties
&& this.properties
.heightUnit
&&this.properties
.heightUnit
.defaultValue
) ? this.properties
.heightUnit
.defaultValue
: "%");
1567 var nodeName
= el
? el
.nodeName
.toLowerCase() : "table";
1568 var ul
= doc
.createElement("ul");
1569 fieldset
.appendChild(ul
);
1572 var widthTitle
= "Table width";
1573 var heightTitle
= "Table height";
1576 var widthTitle
= "Row width";
1577 var heightTitle
= "Row height";
1581 var widthTitle
= "Cell width";
1582 var heightTitle
= "Cell height";
1584 if (this.removedProperties
.indexOf("width") == -1) {
1585 var li
= doc
.createElement("li");
1587 TableOperations
.buildInput(doc
, li
, "f_st_width", "Width:", widthTitle
, "", "5", f_st_width
, "fr");
1588 select
= TableOperations
.buildSelectField(doc
, li
, "f_st_widthUnit", "", "", "", "Width unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((selectedWidthUnit
? selectedWidthUnit
: "%"), "i"));
1589 this.removeOptions(select
, "widthUnit");
1591 if (this.removedProperties
.indexOf("height") == -1) {
1592 var li
= doc
.createElement("li");
1594 TableOperations
.buildInput(doc
, li
, "f_st_height", "Height:", heightTitle
, "", "5", f_st_height
, "fr");
1595 select
= TableOperations
.buildSelectField(doc
, li
, "f_st_heightUnit", "", "", "", "Height unit", ["percent", "pixels", "em"], ["%", "px", "em"], new RegExp((selectedHeightUnit
? selectedHeightUnit
: "%"), "i"));
1596 this.removeOptions(select
, "heightUnit");
1598 if (nodeName
== "table" && this.removedProperties
.indexOf("float") == -1) {
1599 selected
= el
? (HTMLArea
._hasClass(el
, this.floatLeft
) ? "left" : (HTMLArea
._hasClass(el
, this.floatRight
) ? "right" : "not set")) : this.floatDefault
;
1600 select
= TableOperations
.buildSelectField(doc
, li
, "f_st_float", "Float:", "", "", "Specifies where the table should float", ["Not set", "Left", "Right"], ["not set", "left", "right"], new RegExp((selected
? selected
: "not set"), "i"));
1601 this.removeOptions(select
, "float");
1603 content
.appendChild(fieldset
);
1606 setStyleOptions : function (doc
, dropDown
, el
, nodeName
, defaultClass
) {
1607 if (!dropDown
) return false;
1608 if (this.editor
.config
.customSelects
.BlockStyle
) {
1609 var blockStyle
= this.editor
.plugins
.BlockStyle
.instance
;
1610 if (!blockStyle
|| !blockStyle
.cssLoaded
) return false;
1612 var classNames
= new Array();
1613 classNames
.push(defaultClass
);
1615 var classNames
= blockStyle
.getClassNames(el
);
1617 blockStyle
.buildDropDownOptions(dropDown
, nodeName
);
1618 blockStyle
.setSelectedOption(dropDown
, classNames
, "noUnknown", defaultClass
);
1622 buildStylingFieldset : function (doc
, el
, content
, fieldsetClass
, buttonId
) {
1623 var nodeName
= el
? el
.nodeName
.toLowerCase() : "table";
1624 var table
= (nodeName
== "table");
1625 var fieldset
= doc
.createElement("fieldset");
1626 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1627 TableOperations
.insertLegend(doc
, fieldset
, "CSS Style");
1628 TableOperations
.insertSpace(doc
, fieldset
);
1629 var ul
= doc
.createElement("ul");
1630 ul
.className
= "floating";
1631 fieldset
.appendChild(ul
);
1632 var li
= doc
.createElement("li");
1634 var select
= TableOperations
.buildSelectField(doc
, li
, "f_class", (table
? "Table class:" : "Class:"), "fr", "", (table
? "Table class selector" : "Class selector"), new Array("undefined"), new Array("none"), new RegExp("none", "i"), "", false);
1635 this.setStyleOptions(doc
, select
, el
, nodeName
, (buttonId
=== "InsertTable") ? this.defaultClass
: null);
1637 var tbody
= el
.getElementsByTagName("tbody")[0];
1639 var li
= doc
.createElement("li");
1641 var select
= TableOperations
.buildSelectField(doc
, li
, "f_class_tbody", "Table body class:", "fr", "", "Table body class selector", new Array("undefined"), new Array("none"), new RegExp("none", "i"), "", false);
1642 this.setStyleOptions(doc
, select
, tbody
, "tbody");
1644 var thead
= el
.getElementsByTagName("thead")[0];
1646 var li
= doc
.createElement("li");
1648 var select
= TableOperations
.buildSelectField(doc
, li
, "f_class_thead", "Table header class:", "fr", "", "Table header class selector", new Array("undefined"), new Array("none"), new RegExp("none", "i"), "", false);
1649 this.setStyleOptions(doc
, select
, thead
, "thead");
1651 var tfoot
= el
.getElementsByTagName("tfoot")[0];
1653 var li
= doc
.createElement("li");
1655 var select
= TableOperations
.buildSelectField(doc
, li
, "f_class_tfoot", "Table footer class:", "fr", "", "Table footer class selector", new Array("undefined"), new Array("none"), new RegExp("none", "i"), "", false);
1656 this.setStyleOptions(doc
, select
, tfoot
, "tfoot");
1659 TableOperations
.insertSpace(doc
, fieldset
);
1660 content
.appendChild(fieldset
);
1663 buildLanguageFieldset : function (doc
, el
, content
, fieldsetClass
) {
1664 if (this.removedFieldsets
.indexOf("language") == -1 && (this.removedProperties
.indexOf("language") == -1 || this.removedProperties
.indexOf("direction") == -1) && this.getPluginInstance("Language") && (this.isButtonInToolbar("Language") || this.isButtonInToolbar("LeftToRight") || this.isButtonInToolbar("RightToLeft"))) {
1665 var languageObject
= this.getPluginInstance("Language");
1666 var fieldset
= doc
.createElement("fieldset");
1667 if (fieldsetClass
) {
1668 fieldset
.className
= fieldsetClass
;
1670 TableOperations
.insertLegend(doc
, fieldset
, "Language");
1671 var ul
= doc
.createElement("ul");
1672 fieldset
.appendChild(ul
);
1673 if (this.removedProperties
.indexOf("language") == -1 && this.isButtonInToolbar("Language")) {
1674 var languageOptions
= this.getDropDownConfiguration("Language").options
;
1677 options
= new Array(),
1678 values
= new Array();
1679 for (var option
in languageOptions
) {
1680 if (languageOptions
.hasOwnProperty(option
)) {
1681 options
.push(option
);
1682 values
.push(languageOptions
[option
]);
1685 selected
= el
? languageObject
.getLanguageAttribute(el
) : "none";
1686 if (selected
!= "none") {
1687 options
[0] = languageObject
.localize("Remove language mark");
1689 (selected
.match(/([^\s]*)\s/)) && (selected
= RegExp
.$1);
1690 var li
= doc
.createElement("li");
1692 select
= TableOperations
.buildSelectField(doc
, li
, "f_lang", "Language:", "fr", "", "Language", options
, values
, new RegExp((selected
? selected
: "none"), "i"));
1694 if (this.removedProperties
.indexOf("direction") == -1 && (this.isButtonInToolbar("LeftToRight") || this.isButtonInToolbar("RightToLeft"))) {
1695 var li
= doc
.createElement("li");
1697 selected
= el
? el
.dir
: "";
1698 (selected
.match(/([^\s]*)\s/)) && (selected
= RegExp
.$1);
1699 select
= TableOperations
.buildSelectField(doc
, li
, "f_dir", "Text direction:", "fr", "", "Text direction", ["Not set", "Right to left", "Left to right"], ["not set", "rtl", "ltr"], new RegExp((selected
? selected
: "not set"), "i"));
1701 content
.appendChild(fieldset
);
1705 buildCellTypeFieldset : function (doc
, el
, content
, column
, fieldsetClass
) {
1706 var fieldset
= doc
.createElement("fieldset");
1707 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1708 TableOperations
.insertLegend(doc
, fieldset
, column
? "Type of cells" : "Cell Type and Scope");
1709 TableOperations
.insertSpace(doc
, fieldset
);
1710 var ul
= doc
.createElement("ul");
1711 fieldset
.appendChild(ul
);
1712 var li
= doc
.createElement("li");
1715 var selectType
= TableOperations
.buildSelectField(doc
, li
, "f_cell_type", "Type of cells of the column", "fl", "", "Specifies the type of cells", ["Data cells", "Headers for rows", "Headers for row groups"], ["td", "throw", "throwgroup"], new RegExp(el
.nodeName
.toLowerCase()+el
.scope
.toLowerCase()+"$", "i"));
1717 var selectType
= TableOperations
.buildSelectField(doc
, li
, "f_cell_type", "Type of cell", "fr", "", "Specifies the type of cell", ["Normal", "Header for column", "Header for row", "Header for row group"], ["td", "thcol", "throw", "throwgroup"], new RegExp(el
.nodeName
.toLowerCase()+el
.scope
.toLowerCase()+"$", "i"));
1720 selectType
.onchange = function() { self
.setStyleOptions(doc
, doc
.getElementById("f_class"), el
, this.value
.substring(0,2)); };
1721 TableOperations
.insertSpace(doc
, fieldset
);
1722 content
.appendChild(fieldset
);
1725 buildAlignmentFieldset : function (doc
, el
, content
, fieldsetClass
) {
1727 var nodeName
= el
? el
.nodeName
.toLowerCase() : "table";
1728 var fieldset
= doc
.createElement("fieldset");
1729 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1730 TableOperations
.insertLegend(doc
, fieldset
, "Alignment");
1731 var options
= ["Not set", "Left", "Center", "Right", "Justify"];
1732 var values
= ["not set", "left", "center", "right", "justify"];
1734 if (el
&& this.editor
.plugins
.BlockElements
) {
1735 var blockElements
= this.editor
.plugins
.BlockElements
.instance
;
1736 for (var value
in this.convertAlignment
) {
1737 if (this.convertAlignment
.hasOwnProperty(value
) && HTMLArea
._hasClass(el
, blockElements
.useClass
[this.convertAlignment
[value
]])) {
1743 selected
= el
? el
.style
.verticalAlign
: "";
1745 (selected
.match(/([^\s]*)\s/)) && (selected
= RegExp
.$1);
1746 var ul
= doc
.createElement("ul");
1747 fieldset
.appendChild(ul
);
1748 var li
= doc
.createElement("li");
1750 select
= TableOperations
.buildSelectField(doc
, li
, "f_st_textAlign", "Text alignment:", "fr", "", "Horizontal alignment of text within cell", options
, values
, new RegExp((selected
? selected
: "not set"), "i"));
1752 var li
= doc
.createElement("li");
1754 selected
= el
? el
.style
.verticalAlign
: "";
1755 (selected
.match(/([^\s]*)\s/)) && (selected
= RegExp
.$1);
1756 select
= TableOperations
.buildSelectField(doc
, li
, "f_st_vertAlign", "Vertical alignment:", "fr", "", "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"));
1757 content
.appendChild(fieldset
);
1760 buildBordersFieldset : function (w
, doc
, editor
, el
, content
, fieldsetClass
) {
1761 var nodeName
= el
? el
.nodeName
.toLowerCase() : "table";
1764 var borderFields
= [];
1765 function setBorderFieldsVisibility(value
) {
1766 for (var i
= 0; i
< borderFields
.length
; ++i
) {
1767 var borderFieldElement
= borderFields
[i
];
1768 borderFieldElement
.style
.visibility
= value
? "hidden" : "visible";
1769 if (!value
&& (borderFieldElement
.nodeName
.toLowerCase() == "input")) {
1770 borderFieldElement
.focus();
1771 borderFieldElement
.select();
1775 var fieldset
= doc
.createElement("fieldset");
1776 fieldset
.className
= fieldsetClass
;
1777 TableOperations
.insertLegend(doc
, fieldset
, "Frame and borders");
1778 TableOperations
.insertSpace(doc
, fieldset
);
1779 // Gecko reports "solid solid solid solid" for "border-style: solid".
1780 // That is, "top right bottom left" -- we only consider the first value.
1781 var f_st_borderWidth
= el
? TableOperations
.getLength(el
.style
.borderWidth
) : ((this.properties
&& this.properties
.borderWidth
&& this.properties
.borderWidth
.defaultValue
) ? this.properties
.borderWidth
.defaultValue
: "");
1782 selected
= el
? el
.style
.borderStyle
: ((this.properties
&& this.properties
.borderWidth
) ? ((this.properties
.borderStyle
&& this.properties
.borderStyle
.defaultValue
) ? this.properties
.borderStyle
.defaultValue
: "solid") : "");
1783 (selected
.match(/([^\s]*)\s/)) && (selected
= RegExp
.$1);
1784 selectBorderStyle
= TableOperations
.buildSelectField(doc
, 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"));
1785 selectBorderStyle
.onchange = function() { setBorderFieldsVisibility(this.value
== "none"); };
1786 this.removeOptions(selectBorderStyle
, "borderStyle");
1787 TableOperations
.buildInput(doc
, fieldset
, "f_st_borderWidth", "Border width:", "Border width", "pixels", "5", f_st_borderWidth
, "fr", "floating", "postlabel", borderFields
);
1788 TableOperations
.insertSpace(doc
, fieldset
, borderFields
);
1790 if (nodeName
== "table") {
1791 TableOperations
.buildColorField(w
, doc
, editor
, fieldset
, "", "Color:", "fr", "colorButton", (el
? el
.style
.borderColor
: ""), "borderColor", borderFields
);
1792 var label
= doc
.createElement("label");
1793 label
.className
= "fl-borderCollapse";
1794 label
.htmlFor
= "f_st_borderCollapse";
1795 label
.innerHTML
= "Collapsed borders";
1796 fieldset
.appendChild(label
);
1797 borderFields
.push(label
);
1798 var input
= doc
.createElement("input");
1799 input
.className
= "checkbox";
1800 input
.type
= "checkbox";
1801 input
.name
= "f_st_borderCollapse";
1802 input
.id
= "f_st_borderCollapse";
1803 input
.defaultChecked
= el
? /collapse/i.test(el
.style
.borderCollapse
) : false;
1804 input
.checked
= input
.defaultChecked
;
1805 fieldset
.appendChild(input
);
1806 borderFields
.push(input
);
1807 TableOperations
.insertSpace(doc
, fieldset
, borderFields
);
1808 select
= TableOperations
.buildSelectField(doc
, 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
&& el
.frame
) ? el
.frame
: "not set"), "i"), borderFields
);
1809 TableOperations
.insertSpace(doc
, fieldset
, borderFields
);
1810 select
= TableOperations
.buildSelectField(doc
, 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
&& el
.rules
) ? el
.rules
: "not set"), "i"), borderFields
);
1812 TableOperations
.insertSpace(doc
, fieldset
, borderFields
);
1813 TableOperations
.buildColorField(w
, doc
, editor
, fieldset
, "", "Color:", "fr", "colorButton", (el
? el
.style
.borderColor
: ""), "borderColor", borderFields
);
1815 setBorderFieldsVisibility(selectBorderStyle
.value
== "none");
1816 TableOperations
.insertSpace(doc
, fieldset
);
1817 content
.appendChild(fieldset
);
1820 removeOptions : function(select
, property
) {
1821 if (this.properties
&& this.properties
[property
] && this.properties
[property
].removeItems
) {
1822 for (var i
= select
.options
.length
; --i
>= 0;) {
1823 if (this.properties
[property
].removeItems
.indexOf(select
.options
[i
].value
) != -1) {
1824 if (select
.options
[i
].value
!= select
.value
) {
1825 select
.options
[i
] = null;
1833 * This function gets called by the main editor event handler when a key was pressed.
1834 * It will process the enter key for IE when buttons.table.disableEnterParagraphs is set in the editor configuration
1836 onKeyPress : function (ev
) {
1837 if (HTMLArea
.is_ie
&& ev
.keyCode
== 13 && !ev
.shiftKey
&& this.disableEnterParagraphs
) {
1838 var selection
= this.editor
._getSelection();
1839 var range
= this.editor
._createRange(selection
);
1840 var parentElement
= this.editor
.getParentElement(selection
, range
);
1841 while (parentElement
&& !HTMLArea
.isBlockElement(parentElement
)) {
1842 parentElement
= parentElement
.parentNode
;
1844 if (/^(td|th)$/i.test(parentElement
.nodeName
)) {
1845 range
.pasteHTML("<br />");
1853 TableOperations
.getLength = function(value
) {
1854 var len
= parseInt(value
);
1855 if (isNaN(len
)) len
= "";
1859 // Returns an HTML element for a widget that allows color selection. That is,
1860 // a button that contains the given color, if any, and when pressed will popup
1861 // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
1862 // to select some color. If a color is selected, an input field with the name
1863 // "f_st_"+name will be updated with the color value in #123456 format.
1864 TableOperations
.createColorButton = function(w
, doc
, editor
, color
, name
) {
1867 } else if (!/#/.test(color
)) {
1868 color
= HTMLArea
._colorToRgb(color
);
1871 var df
= doc
.createElement("span");
1872 var field
= doc
.createElement("input");
1873 field
.type
= "hidden";
1874 df
.appendChild(field
);
1875 field
.name
= "f_st_" + name
;
1876 field
.id
= "f_st_" + name
;
1877 field
.value
= color
;
1878 var button
= doc
.createElement("span");
1879 button
.className
= "buttonColor";
1880 df
.appendChild(button
);
1881 var span
= doc
.createElement("span");
1882 span
.className
= "chooser";
1883 span
.style
.backgroundColor
= color
;
1884 var space
= doc
.createTextNode("\xA0");
1885 span
.appendChild(space
);
1886 button
.appendChild(span
);
1887 button
.onmouseover = function() { if (!this.disabled
) this.className
+= " buttonColor-hilite"; };
1888 button
.onmouseout = function() { if (!this.disabled
) this.className
= "buttonColor"; };
1889 span
.onclick = function() {
1890 if (this.parentNode
.disabled
) return false;
1891 var colorPlugin
= editor
.plugins
.TYPO3Color
;
1893 colorPlugin
.instance
.dialogSelectColor("color", span
, field
, w
);
1895 colorPlugin
= editor
.plugins
.DefaultColor
;
1897 w
.insertColor = function (color
) {
1899 span
.style
.backgroundColor
= color
;
1900 field
.value
= color
;
1903 colorPlugin
.instance
.onButtonPress(editor
, "TableOperations");
1907 var span2
= doc
.createElement("span");
1908 span2
.innerHTML
= "×";
1909 span2
.className
= "nocolor";
1910 span2
.title
= "Unset color";
1911 button
.appendChild(span2
);
1912 span2
.onmouseover = function() { if (!this.parentNode
.disabled
) this.className
+= " nocolor-hilite"; };
1913 span2
.onmouseout = function() { if (!this.parentNode
.disabled
) this.className
= "nocolor"; };
1914 span2
.onclick = function() {
1915 span
.style
.backgroundColor
= "";
1920 TableOperations
.buildTitle = function(doc
, content
, title
) {
1921 var div
= doc
.createElement("div");
1922 div
.className
= "title";
1923 div
.innerHTML
= title
;
1924 content
.appendChild(div
);
1927 TableOperations
.buildDescriptionFieldset = function(doc
, el
, content
, fieldsetClass
) {
1928 var fieldset
= doc
.createElement("fieldset");
1929 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1930 TableOperations
.insertLegend(doc
, fieldset
, "Description");
1931 TableOperations
.insertSpace(doc
, fieldset
);
1934 var capel
= el
.getElementsByTagName("caption")[0];
1935 if (capel
) f_caption
= capel
.innerHTML
;
1937 TableOperations
.buildInput(doc
, fieldset
, "f_caption", "Caption:", "Description of the nature of the table", "", "", f_caption
, "fr", "value", "");
1938 TableOperations
.insertSpace(doc
, fieldset
);
1939 TableOperations
.buildInput(doc
, fieldset
, "f_summary", "Summary:", "Summary of the table purpose and structure", "", "", (el
? el
.summary
: ""), "fr", "value", "");
1940 TableOperations
.insertSpace(doc
, fieldset
);
1941 content
.appendChild(fieldset
);
1943 TableOperations
.buildRowGroupFieldset = function(w
, doc
, editor
, el
, content
, fieldsetClass
) {
1944 var fieldset
= doc
.createElement("fieldset");
1945 if (fieldsetClass
) fieldset
.className
= fieldsetClass
;
1946 TableOperations
.insertLegend(doc
, fieldset
, "Row group");
1947 TableOperations
.insertSpace(doc
, fieldset
);
1948 TableOperations
.insertSpace(doc
, fieldset
);
1949 selected
= el
.parentNode
.nodeName
.toLowerCase();
1950 var selectScope
= TableOperations
.buildSelectField(doc
, fieldset
, "f_rowgroup", "Row group:", "fr", "floating", "Table section", ["Table body", "Table header", "Table footer"], ["tbody", "thead", "tfoot"], new RegExp((selected
? selected
: "tbody"), "i"));
1951 function displayCheckbox(current
, value
) {
1952 if (current
!== "thead" && value
=== "thead") {
1953 label1
.style
.display
= "inline";
1954 label2
.style
.display
= "none";
1955 input
.style
.display
= "inline";
1956 input
.checked
= true;
1957 } else if (current
=== "thead" && value
!== "thead") {
1958 label1
.style
.display
= "none";
1959 label2
.style
.display
= "inline";
1960 input
.style
.display
= "inline";
1961 input
.checked
= true;
1963 label1
.style
.display
= "none";
1964 label2
.style
.display
= "none";
1965 input
.style
.display
= "none";
1966 input
.checked
= false;
1969 selectScope
.onchange = function() { displayCheckbox(selected
, this.value
); };
1970 var label1
= doc
.createElement("label");
1971 label1
.className
= "fl";
1972 label1
.htmlFor
= "f_convertCells";
1973 label1
.innerHTML
= "Make cells header cells";
1974 label1
.style
.display
= "none";
1975 fieldset
.appendChild(label1
);
1976 var label2
= doc
.createElement("label");
1977 label2
.className
= "fl";
1978 label2
.htmlFor
= "f_convertCells";
1979 label2
.innerHTML
= "Make cells data cells";
1980 label2
.style
.display
= "none";
1981 fieldset
.appendChild(label2
);
1982 var input
= doc
.createElement("input");
1983 input
.className
= "checkbox";
1984 input
.type
= "checkbox";
1985 input
.name
= "f_convertCells";
1986 input
.id
= "f_convertCells";
1987 input
.checked
= false;
1988 input
.style
.display
= "none";
1989 fieldset
.appendChild(input
);
1990 TableOperations
.insertSpace(doc
, fieldset
);
1991 content
.appendChild(fieldset
);
1993 TableOperations
.buildSpacingFieldset = function(doc
, el
, content
) {
1994 var fieldset
= doc
.createElement("fieldset");
1995 TableOperations
.insertLegend(doc
, fieldset
, "Spacing and padding");
1996 TableOperations
.buildInput(doc
, fieldset
, "f_spacing", "Cell spacing:", "Space between adjacent cells", "pixels", "5", (el
? el
.cellSpacing
: ""), "fr", "", "postlabel");
1997 TableOperations
.insertSpace(doc
, fieldset
);
1998 TableOperations
.buildInput(doc
, fieldset
, "f_padding", "Cell padding:", "Space between content and border in cell", "pixels", "5", (el
? el
.cellPadding
: ""), "fr", "", "postlabel");
1999 content
.appendChild(fieldset
);
2001 TableOperations
.buildColorsFieldset = function(w
, doc
, editor
, el
, content
) {
2002 var fieldset
= doc
.createElement("fieldset");
2003 TableOperations
.insertLegend(doc
, fieldset
, "Background and colors");
2004 var ul
= doc
.createElement("ul");
2005 fieldset
.appendChild(ul
);
2006 var li
= doc
.createElement("li");
2008 TableOperations
.buildColorField(w
, doc
, editor
, li
, "", "FG Color:", "fr", "colorButtonNoFloat", (el
? el
.style
.color
: ""), "color");
2009 var li
= doc
.createElement("li");
2011 TableOperations
.buildColorField(w
, doc
, editor
, li
, "", "Background:", "fr", "colorButtonNoFloat", (el
? el
.style
.backgroundColor
: ""), "backgroundColor");
2013 if (el
&& el
.style
.backgroundImage
.match(/url\(\s*(.*?)\s*\)/)) url
= RegExp
.$1;
2014 TableOperations
.buildInput(doc
, li
, "f_st_backgroundImage", "Image URL:", "URL of the background image", "", "", url
, "", "shorter-value");
2015 content
.appendChild(fieldset
);
2017 TableOperations
.insertLegend = function(doc
, fieldset
, legend
) {
2018 var legendNode
= doc
.createElement("legend");
2019 legendNode
.innerHTML
= legend
;
2020 fieldset
.appendChild(legendNode
);
2022 TableOperations
.insertSpace = function(doc
,fieldset
,fields
) {
2023 var space
= doc
.createElement("div");
2024 space
.className
= "space";
2025 fieldset
.appendChild(space
);
2026 if(fields
) fields
.push(space
);
2028 TableOperations
.buildInput = function(doc
, fieldset
,fieldName
,fieldLabel
,fieldTitle
,postLabel
,fieldSize
,fieldValue
,labelClass
,inputClass
,postClass
,fields
) {
2032 label
= doc
.createElement("label");
2033 if(labelClass
) label
.className
= labelClass
;
2034 label
.innerHTML
= fieldLabel
;
2035 label
.htmlFor
= fieldName
;
2036 fieldset
.appendChild(label
);
2037 if(fields
) fields
.push(label
);
2040 var input
= doc
.createElement("input");
2041 input
.type
= "text";
2042 input
.id
= fieldName
;
2043 input
.name
= fieldName
;
2044 if(inputClass
) input
.className
= inputClass
;
2045 if(fieldTitle
) input
.title
= fieldTitle
;
2046 if(fieldSize
) input
.size
= fieldSize
;
2047 if(fieldValue
) input
.value
= fieldValue
;
2048 fieldset
.appendChild(input
);
2049 if(fields
) fields
.push(input
);
2052 label
= doc
.createElement("span");
2053 if(postClass
) label
.className
= postClass
;
2054 label
.innerHTML
= postLabel
;
2055 fieldset
.appendChild(label
);
2056 if(fields
) fields
.push(label
);
2060 TableOperations
.buildSelectField = function(doc
, fieldset
,fieldName
,fieldLabel
,labelClass
,selectClass
,fieldTitle
,options
,values
,selected
,fields
,translateOptions
) {
2061 if(typeof(translateOptions
) == "undefined") var translateOptions
= true;
2064 var label
= doc
.createElement("label");
2065 if(labelClass
) label
.className
= labelClass
;
2066 label
.innerHTML
= fieldLabel
;
2067 label
.htmlFor
= fieldName
;
2068 fieldset
.appendChild(label
);
2069 if(fields
) fields
.push(label
);
2071 // Text Alignment Select Box
2072 var select
= doc
.createElement("select");
2073 if (selectClass
) select
.className
= selectClass
;
2074 select
.id
= fieldName
;
2075 select
.name
= fieldName
;
2076 select
.title
= fieldTitle
;
2077 select
.selectedIndex
= 0;
2079 for (var i
= 0; i
< options
.length
; ++i
) {
2080 option
= doc
.createElement("option");
2081 select
.appendChild(option
);
2082 option
.value
= values
[i
];
2083 option
.innerHTML
= options
[i
];
2084 option
.selected
= selected
.test(option
.value
);
2086 if (select
.options
.length
>1) select
.disabled
= false;
2087 else select
.disabled
= true;
2088 fieldset
.appendChild(select
);
2089 if(fields
) fields
.push(select
);
2092 TableOperations
.buildColorField = function(w
, doc
, editor
, fieldset
,fieldName
,fieldLabel
,labelClass
, buttonClass
, fieldValue
,fieldType
,fields
) {
2095 var label
= doc
.createElement("label");
2096 if(labelClass
) label
.className
= labelClass
;
2097 label
.innerHTML
= fieldLabel
;
2098 fieldset
.appendChild(label
);
2099 if(fields
) fields
.push(label
);
2101 var colorButton
= TableOperations
.createColorButton(w
, doc
, editor
, fieldValue
, fieldType
);
2102 colorButton
.className
= buttonClass
;
2103 fieldset
.appendChild(colorButton
);
2104 if(fields
) fields
.push(colorButton
);