Annotation of loncom/html/htmlarea/plugins/TableOperations/table-operations.js, revision 1.2
1.1 www 1: // Table Operations Plugin for HTMLArea-3.0
2: // Implementation by Mihai Bazon. Sponsored by http://www.bloki.com
3: //
4: // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
5: // This notice MUST stay intact for use (see license.txt).
6: //
7: // A free WYSIWYG editor replacement for <textarea> fields.
8: // For full source code and docs, visit http://www.interactivetools.com/
9: //
10: // Version 3.0 developed by Mihai Bazon for InteractiveTools.
1.2 ! www 11: // http://dynarch.com/mishoo
1.1 www 12: //
1.2 ! www 13: // $Id: table-operations.js,v 1.5 2003/10/06 11:47:42 mishoo Exp $
1.1 www 14:
15: // Object that will encapsulate all the table operations provided by
16: // HTMLArea-3.0 (except "insert table" which is included in the main file)
17: function TableOperations(editor) {
18: this.editor = editor;
19:
20: var cfg = editor.config;
21: var tt = TableOperations.I18N;
22: var bl = TableOperations.btnList;
23: var self = this;
24:
25: // register the toolbar buttons provided by this plugin
26: var toolbar = ["linebreak"];
27: for (var i in bl) {
28: var btn = bl[i];
29: if (!btn) {
30: toolbar.push("separator");
31: } else {
32: var id = "TO-" + btn[0];
1.2 ! www 33: cfg.registerButton(id, tt[id], editor.imgURL(btn[0] + ".gif", "TableOperations"), false,
1.1 www 34: function(editor, id) {
35: // dispatch button press event
36: self.buttonPress(editor, id);
37: }, btn[1]);
38: toolbar.push(id);
39: }
40: }
41:
42: // add a new line in the toolbar
43: cfg.toolbar.push(toolbar);
44: };
45:
1.2 ! www 46: TableOperations._pluginInfo = {
! 47: name : "TableOperations",
! 48: version : "1.0",
! 49: developer : "Mihai Bazon",
! 50: developer_url : "http://dynarch.com/mishoo/",
! 51: c_owner : "Mihai Bazon",
! 52: sponsor : "Zapatec Inc.",
! 53: sponsor_url : "http://www.bloki.com",
! 54: license : "htmlArea"
! 55: };
! 56:
1.1 www 57: /************************
58: * UTILITIES
59: ************************/
60:
61: // retrieves the closest element having the specified tagName in the list of
62: // ancestors of the current selection/caret.
63: TableOperations.prototype.getClosest = function(tagName) {
64: var editor = this.editor;
65: var ancestors = editor.getAllAncestors();
66: var ret = null;
67: tagName = ("" + tagName).toLowerCase();
68: for (var i in ancestors) {
69: var el = ancestors[i];
70: if (el.tagName.toLowerCase() == tagName) {
71: ret = el;
72: break;
73: }
74: }
75: return ret;
76: };
77:
78: // this function requires the file PopupDiv/PopupWin to be loaded from browser
79: TableOperations.prototype.dialogTableProperties = function() {
80: var i18n = TableOperations.I18N;
81: // retrieve existing values
82: var table = this.getClosest("table");
83: // this.editor.selectNodeContents(table);
84: // this.editor.updateToolbar();
85:
86: var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {
87: TableOperations.processStyle(params, table);
88: for (var i in params) {
89: var val = params[i];
90: switch (i) {
91: case "f_caption":
92: if (/\S/.test(val)) {
93: // contains non white-space characters
94: var caption = table.getElementsByTagName("caption")[0];
95: if (!caption) {
96: caption = dialog.editor._doc.createElement("caption");
97: table.insertBefore(caption, table.firstChild);
98: }
99: caption.innerHTML = val;
100: } else {
101: // search for caption and delete it if found
102: var caption = table.getElementsByTagName("caption")[0];
103: if (caption) {
104: caption.parentNode.removeChild(caption);
105: }
106: }
107: break;
108: case "f_summary":
109: table.summary = val;
110: break;
111: case "f_width":
112: table.style.width = ("" + val) + params.f_unit;
113: break;
114: case "f_align":
115: table.align = val;
116: break;
117: case "f_spacing":
118: table.cellSpacing = val;
119: break;
120: case "f_padding":
121: table.cellPadding = val;
122: break;
123: case "f_borders":
124: table.border = val;
125: break;
126: case "f_frames":
127: table.frame = val;
128: break;
129: case "f_rules":
130: table.rules = val;
131: break;
132: }
133: }
134: // various workarounds to refresh the table display (Gecko,
135: // what's going on?! do not disappoint me!)
136: dialog.editor.forceRedraw();
137: dialog.editor.focusEditor();
138: dialog.editor.updateToolbar();
139: var save_collapse = table.style.borderCollapse;
140: table.style.borderCollapse = "collapse";
141: table.style.borderCollapse = "separate";
142: table.style.borderCollapse = save_collapse;
143: },
144:
145: // this function gets called when the dialog needs to be initialized
146: function (dialog) {
147:
148: var f_caption = "";
149: var capel = table.getElementsByTagName("caption")[0];
150: if (capel) {
151: f_caption = capel.innerHTML;
152: }
153: var f_summary = table.summary;
154: var f_width = parseInt(table.style.width);
155: isNaN(f_width) && (f_width = "");
156: var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
157: var f_align = table.align;
158: var f_spacing = table.cellSpacing;
159: var f_padding = table.cellPadding;
160: var f_borders = table.border;
161: var f_frames = table.frame;
162: var f_rules = table.rules;
163:
164: function selected(val) {
165: return val ? " selected" : "";
166: };
167:
168: // dialog contents
169: dialog.content.style.width = "400px";
170: dialog.content.innerHTML = " \
171: <div class='title'\
172: style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\
173: </div> \
174: <table style='width:100%'> \
175: <tr> \
176: <td> \
177: <fieldset><legend>" + i18n["Description"] + "</legend> \
178: <table style='width:100%'> \
179: <tr> \
180: <td class='label'>" + i18n["Caption"] + ":</td> \
181: <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
182: </tr><tr> \
183: <td class='label'>" + i18n["Summary"] + ":</td> \
184: <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
185: </tr> \
186: </table> \
187: </fieldset> \
188: </td> \
189: </tr> \
190: <tr><td id='--HA-layout'></td></tr> \
191: <tr> \
192: <td> \
193: <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \
194: <table style='width:100%'> \
195: "+// <tr> \
196: // <td class='label'>" + i18n["Width"] + ":</td> \
197: // <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
198: // <select name='f_unit'> \
199: // <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \
200: // <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \
201: // </select> " + i18n["Align"] + ": \
202: // <select name='f_align'> \
203: // <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
204: // <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
205: // <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
206: // </select> \
207: // </td> \
208: // </tr> \
209: " <tr> \
210: <td class='label'>" + i18n["Spacing"] + ":</td> \
211: <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> " + i18n["Padding"] + ":\
212: <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> " + i18n["pixels"] + "\
213: </td> \
214: </tr> \
215: </table> \
216: </fieldset> \
217: </td> \
218: </tr> \
219: <tr> \
220: <td> \
221: <fieldset><legend>Frame and borders</legend> \
222: <table width='100%'> \
223: <tr> \
224: <td class='label'>" + i18n["Borders"] + ":</td> \
225: <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> " + i18n["pixels"] + "</td> \
226: </tr> \
227: <tr> \
228: <td class='label'>" + i18n["Frames"] + ":</td> \
229: <td> \
230: <select name='f_frames'> \
231: <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \
232: <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \
233: <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \
234: <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \
235: <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \
236: <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \
237: <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \
238: <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \
239: </select> \
240: </td> \
241: </tr> \
242: <tr> \
243: <td class='label'>" + i18n["Rules"] + ":</td> \
244: <td> \
245: <select name='f_rules'> \
246: <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \
247: <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \
248: <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \
249: <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \
250: </select> \
251: </td> \
252: </tr> \
253: </table> \
254: </fieldset> \
255: </td> \
256: </tr> \
257: <tr> \
258: <td id='--HA-style'></td> \
259: </tr> \
260: </table> \
261: ";
262: var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
263: var p = dialog.doc.getElementById("--HA-style");
264: p.appendChild(st_prop);
265: var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
266: p = dialog.doc.getElementById("--HA-layout");
267: p.appendChild(st_layout);
268: dialog.modal = true;
269: dialog.addButtons("ok", "cancel");
270: dialog.showAtElement(dialog.editor._iframe, "c");
271: });
272: };
273:
274: // this function requires the file PopupDiv/PopupWin to be loaded from browser
275: TableOperations.prototype.dialogRowCellProperties = function(cell) {
276: var i18n = TableOperations.I18N;
277: // retrieve existing values
278: var element = this.getClosest(cell ? "td" : "tr");
279: var table = this.getClosest("table");
280: // this.editor.selectNodeContents(element);
281: // this.editor.updateToolbar();
282:
283: var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
284: TableOperations.processStyle(params, element);
285: for (var i in params) {
286: var val = params[i];
287: switch (i) {
288: case "f_align":
289: element.align = val;
290: break;
291: case "f_char":
292: element.ch = val;
293: break;
294: case "f_valign":
295: element.vAlign = val;
296: break;
297: }
298: }
299: // various workarounds to refresh the table display (Gecko,
300: // what's going on?! do not disappoint me!)
301: dialog.editor.forceRedraw();
302: dialog.editor.focusEditor();
303: dialog.editor.updateToolbar();
304: var save_collapse = table.style.borderCollapse;
305: table.style.borderCollapse = "collapse";
306: table.style.borderCollapse = "separate";
307: table.style.borderCollapse = save_collapse;
308: },
309:
310: // this function gets called when the dialog needs to be initialized
311: function (dialog) {
312:
313: var f_align = element.align;
314: var f_valign = element.vAlign;
315: var f_char = element.ch;
316:
317: function selected(val) {
318: return val ? " selected" : "";
319: };
320:
321: // dialog contents
322: dialog.content.style.width = "400px";
323: dialog.content.innerHTML = " \
324: <div class='title'\
325: style='background: url(" + dialog.baseURL + dialog.editor.imgURL(cell ? "cell-prop.gif" : "row-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n[cell ? "Cell Properties" : "Row Properties"] + "</div> \
326: <table style='width:100%'> \
327: <tr> \
328: <td id='--HA-layout'> \
329: "+// <fieldset><legend>" + i18n["Layout"] + "</legend> \
330: // <table style='width:100%'> \
331: // <tr> \
332: // <td class='label'>" + i18n["Align"] + ":</td> \
333: // <td> \
334: // <select name='f_align'> \
335: // <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
336: // <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
337: // <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
338: // <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
339: // </select> \
340: // " + i18n["Char"] + ": \
341: // <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
342: // </td> \
343: // </tr><tr> \
344: // <td class='label'>" + i18n["Vertical align"] + ":</td> \
345: // <td> \
346: // <select name='f_valign'> \
347: // <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
348: // <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
349: // <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
350: // <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
351: // </select> \
352: // </td> \
353: // </tr> \
354: // </table> \
355: // </fieldset> \
356: " </td> \
357: </tr> \
358: <tr> \
359: <td id='--HA-style'></td> \
360: </tr> \
361: </table> \
362: ";
363: var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
364: var p = dialog.doc.getElementById("--HA-style");
365: p.appendChild(st_prop);
366: var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
367: p = dialog.doc.getElementById("--HA-layout");
368: p.appendChild(st_layout);
369: dialog.modal = true;
370: dialog.addButtons("ok", "cancel");
371: dialog.showAtElement(dialog.editor._iframe, "c");
372: });
373: };
374:
375: // this function gets called when some button from the TableOperations toolbar
376: // was pressed.
377: TableOperations.prototype.buttonPress = function(editor, button_id) {
378: this.editor = editor;
379: var mozbr = HTMLArea.is_gecko ? "<br />" : "";
380: var i18n = TableOperations.I18N;
381:
382: // helper function that clears the content in a table row
383: function clearRow(tr) {
384: var tds = tr.getElementsByTagName("td");
385: for (var i = tds.length; --i >= 0;) {
386: var td = tds[i];
387: td.rowSpan = 1;
388: td.innerHTML = mozbr;
389: }
390: };
391:
392: function splitRow(td) {
393: var n = parseInt("" + td.rowSpan);
394: var nc = parseInt("" + td.colSpan);
395: td.rowSpan = 1;
396: tr = td.parentNode;
397: var itr = tr.rowIndex;
398: var trs = tr.parentNode.rows;
399: var index = td.cellIndex;
400: while (--n > 0) {
401: tr = trs[++itr];
402: var otd = editor._doc.createElement("td");
403: otd.colSpan = td.colSpan;
404: otd.innerHTML = mozbr;
405: tr.insertBefore(otd, tr.cells[index]);
406: }
407: editor.forceRedraw();
408: editor.updateToolbar();
409: };
410:
411: function splitCol(td) {
412: var nc = parseInt("" + td.colSpan);
413: td.colSpan = 1;
414: tr = td.parentNode;
415: var ref = td.nextSibling;
416: while (--nc > 0) {
417: var otd = editor._doc.createElement("td");
418: otd.rowSpan = td.rowSpan;
419: otd.innerHTML = mozbr;
420: tr.insertBefore(otd, ref);
421: }
422: editor.forceRedraw();
423: editor.updateToolbar();
424: };
425:
426: function splitCell(td) {
427: var nc = parseInt("" + td.colSpan);
428: splitCol(td);
429: var items = td.parentNode.cells;
430: var index = td.cellIndex;
431: while (nc-- > 0) {
432: splitRow(items[index++]);
433: }
434: };
435:
436: function selectNextNode(el) {
437: var node = el.nextSibling;
438: while (node && node.nodeType != 1) {
439: node = node.nextSibling;
440: }
441: if (!node) {
442: node = el.previousSibling;
443: while (node && node.nodeType != 1) {
444: node = node.previousSibling;
445: }
446: }
447: if (!node) {
448: node = el.parentNode;
449: }
450: editor.selectNodeContents(node);
451: };
452:
453: switch (button_id) {
454: // ROWS
455:
456: case "TO-row-insert-above":
457: case "TO-row-insert-under":
458: var tr = this.getClosest("tr");
459: if (!tr) {
460: break;
461: }
462: var otr = tr.cloneNode(true);
463: clearRow(otr);
464: tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
465: editor.forceRedraw();
466: editor.focusEditor();
467: break;
468: case "TO-row-delete":
469: var tr = this.getClosest("tr");
470: if (!tr) {
471: break;
472: }
473: var par = tr.parentNode;
474: if (par.rows.length == 1) {
475: alert(i18n["not-del-last-row"]);
476: break;
477: }
478: // set the caret first to a position that doesn't
479: // disappear.
480: selectNextNode(tr);
481: par.removeChild(tr);
482: editor.forceRedraw();
483: editor.focusEditor();
484: editor.updateToolbar();
485: break;
486: case "TO-row-split":
487: var td = this.getClosest("td");
488: if (!td) {
489: break;
490: }
491: splitRow(td);
492: break;
493:
494: // COLUMNS
495:
496: case "TO-col-insert-before":
497: case "TO-col-insert-after":
498: var td = this.getClosest("td");
499: if (!td) {
500: break;
501: }
502: var rows = td.parentNode.parentNode.rows;
503: var index = td.cellIndex;
504: for (var i = rows.length; --i >= 0;) {
505: var tr = rows[i];
506: var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
507: var otd = editor._doc.createElement("td");
508: otd.innerHTML = mozbr;
509: tr.insertBefore(otd, ref);
510: }
511: editor.focusEditor();
512: break;
513: case "TO-col-split":
514: var td = this.getClosest("td");
515: if (!td) {
516: break;
517: }
518: splitCol(td);
519: break;
520: case "TO-col-delete":
521: var td = this.getClosest("td");
522: if (!td) {
523: break;
524: }
525: var index = td.cellIndex;
526: if (td.parentNode.cells.length == 1) {
527: alert(i18n["not-del-last-col"]);
528: break;
529: }
530: // set the caret first to a position that doesn't disappear
531: selectNextNode(td);
532: var rows = td.parentNode.parentNode.rows;
533: for (var i = rows.length; --i >= 0;) {
534: var tr = rows[i];
535: tr.removeChild(tr.cells[index]);
536: }
537: editor.forceRedraw();
538: editor.focusEditor();
539: editor.updateToolbar();
540: break;
541:
542: // CELLS
543:
544: case "TO-cell-split":
545: var td = this.getClosest("td");
546: if (!td) {
547: break;
548: }
549: splitCell(td);
550: break;
551: case "TO-cell-insert-before":
552: case "TO-cell-insert-after":
553: var td = this.getClosest("td");
554: if (!td) {
555: break;
556: }
557: var tr = td.parentNode;
558: var otd = editor._doc.createElement("td");
559: otd.innerHTML = mozbr;
560: tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
561: editor.forceRedraw();
562: editor.focusEditor();
563: break;
564: case "TO-cell-delete":
565: var td = this.getClosest("td");
566: if (!td) {
567: break;
568: }
569: if (td.parentNode.cells.length == 1) {
570: alert(i18n["not-del-last-cell"]);
571: break;
572: }
573: // set the caret first to a position that doesn't disappear
574: selectNextNode(td);
575: td.parentNode.removeChild(td);
576: editor.forceRedraw();
577: editor.updateToolbar();
578: break;
579: case "TO-cell-merge":
580: // !! FIXME: Mozilla specific !!
581: var sel = editor._getSelection();
582: var range, i = 0;
583: var rows = [];
584: var row = null;
585: var cells = null;
586: if (!HTMLArea.is_ie) {
587: try {
588: while (range = sel.getRangeAt(i++)) {
589: var td = range.startContainer.childNodes[range.startOffset];
590: if (td.parentNode != row) {
591: row = td.parentNode;
592: (cells) && rows.push(cells);
593: cells = [];
594: }
595: cells.push(td);
596: }
597: } catch(e) {/* finished walking through selection */}
598: rows.push(cells);
599: } else {
600: // Internet Explorer "browser"
601: var td = this.getClosest("td");
602: if (!td) {
603: alert(i18n["Please click into some cell"]);
604: break;
605: }
606: var tr = td.parentElement;
607: var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
608: if (!no_cols) {
609: // cancelled
610: break;
611: }
612: var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
613: if (!no_rows) {
614: // cancelled
615: break;
616: }
617: var cell_index = td.cellIndex;
618: while (no_rows-- > 0) {
619: td = tr.cells[cell_index];
620: cells = [td];
621: for (var i = 1; i < no_cols; ++i) {
622: td = td.nextSibling;
623: if (!td) {
624: break;
625: }
626: cells.push(td);
627: }
628: rows.push(cells);
629: tr = tr.nextSibling;
630: if (!tr) {
631: break;
632: }
633: }
634: }
635: var HTML = "";
636: for (i = 0; i < rows.length; ++i) {
637: // i && (HTML += "<br />");
638: var cells = rows[i];
639: for (var j = 0; j < cells.length; ++j) {
640: // j && (HTML += " ");
641: var cell = cells[j];
642: HTML += cell.innerHTML;
643: (i || j) && (cell.parentNode.removeChild(cell));
644: }
645: }
646: var td = rows[0][0];
647: td.innerHTML = HTML;
648: td.rowSpan = rows.length;
649: td.colSpan = rows[0].length;
650: editor.selectNodeContents(td);
651: editor.forceRedraw();
652: editor.focusEditor();
653: break;
654:
655: // PROPERTIES
656:
657: case "TO-table-prop":
658: this.dialogTableProperties();
659: break;
660:
661: case "TO-row-prop":
662: this.dialogRowCellProperties(false);
663: break;
664:
665: case "TO-cell-prop":
666: this.dialogRowCellProperties(true);
667: break;
668:
669: default:
670: alert("Button [" + button_id + "] not yet implemented");
671: }
672: };
673:
674: // the list of buttons added by this plugin
675: TableOperations.btnList = [
676: // table properties button
677: ["table-prop", "table"],
678: null, // separator
679:
680: // ROWS
681: ["row-prop", "tr"],
682: ["row-insert-above", "tr"],
683: ["row-insert-under", "tr"],
684: ["row-delete", "tr"],
685: ["row-split", "td[rowSpan!=1]"],
686: null,
687:
688: // COLS
689: ["col-insert-before", "td"],
690: ["col-insert-after", "td"],
691: ["col-delete", "td"],
692: ["col-split", "td[colSpan!=1]"],
693: null,
694:
695: // CELLS
696: ["cell-prop", "td"],
697: ["cell-insert-before", "td"],
698: ["cell-insert-after", "td"],
699: ["cell-delete", "td"],
700: ["cell-merge", "tr"],
701: ["cell-split", "td[colSpan!=1,rowSpan!=1]"]
702: ];
703:
704:
705:
706: //// GENERIC CODE [style of any element; this should be moved into a separate
707: //// file as it'll be very useful]
708: //// BEGIN GENERIC CODE -----------------------------------------------------
709:
710: TableOperations.getLength = function(value) {
711: var len = parseInt(value);
712: if (isNaN(len)) {
713: len = "";
714: }
715: return len;
716: };
717:
718: // Applies the style found in "params" to the given element.
719: TableOperations.processStyle = function(params, element) {
720: var style = element.style;
721: for (var i in params) {
722: var val = params[i];
723: switch (i) {
724: case "f_st_backgroundColor":
725: style.backgroundColor = val;
726: break;
727: case "f_st_color":
728: style.color = val;
729: break;
730: case "f_st_backgroundImage":
731: if (/\S/.test(val)) {
732: style.backgroundImage = "url(" + val + ")";
733: } else {
734: style.backgroundImage = "none";
735: }
736: break;
737: case "f_st_borderWidth":
738: style.borderWidth = val;
739: break;
740: case "f_st_borderStyle":
741: style.borderStyle = val;
742: break;
743: case "f_st_borderColor":
744: style.borderColor = val;
745: break;
746: case "f_st_borderCollapse":
747: style.borderCollapse = val ? "collapse" : "";
748: break;
749: case "f_st_width":
750: if (/\S/.test(val)) {
751: style.width = val + params["f_st_widthUnit"];
752: } else {
753: style.width = "";
754: }
755: break;
756: case "f_st_height":
757: if (/\S/.test(val)) {
758: style.height = val + params["f_st_heightUnit"];
759: } else {
760: style.height = "";
761: }
762: break;
763: case "f_st_textAlign":
764: if (val == "char") {
765: var ch = params["f_st_textAlignChar"];
766: if (ch == '"') {
767: ch = '\\"';
768: }
769: style.textAlign = '"' + ch + '"';
770: } else {
771: style.textAlign = val;
772: }
773: break;
774: case "f_st_verticalAlign":
775: style.verticalAlign = val;
776: break;
777: case "f_st_float":
778: style.cssFloat = val;
779: break;
780: // case "f_st_margin":
781: // style.margin = val + "px";
782: // break;
783: // case "f_st_padding":
784: // style.padding = val + "px";
785: // break;
786: }
787: }
788: };
789:
790: // Returns an HTML element for a widget that allows color selection. That is,
791: // a button that contains the given color, if any, and when pressed will popup
792: // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
793: // to select some color. If a color is selected, an input field with the name
794: // "f_st_"+name will be updated with the color value in #123456 format.
795: TableOperations.createColorButton = function(doc, editor, color, name) {
796: if (!color) {
797: color = "";
798: } else if (!/#/.test(color)) {
799: color = HTMLArea._colorToRgb(color);
800: }
801:
802: var df = doc.createElement("span");
803: var field = doc.createElement("input");
804: field.type = "hidden";
805: df.appendChild(field);
806: field.name = "f_st_" + name;
807: field.value = color;
808: var button = doc.createElement("span");
809: button.className = "buttonColor";
810: df.appendChild(button);
811: var span = doc.createElement("span");
812: span.className = "chooser";
813: // span.innerHTML = " ";
814: span.style.backgroundColor = color;
815: button.appendChild(span);
816: button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
817: button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
818: span.onclick = function() {
819: if (this.parentNode.disabled) {
820: return false;
821: }
822: editor._popupDialog("select_color.html", function(color) {
823: if (color) {
824: span.style.backgroundColor = "#" + color;
825: field.value = "#" + color;
826: }
827: }, color);
828: };
829: var span2 = doc.createElement("span");
830: span2.innerHTML = "×";
831: span2.className = "nocolor";
832: span2.title = TableOperations.I18N["Unset color"];
833: button.appendChild(span2);
834: span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
835: span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
836: span2.onclick = function() {
837: span.style.backgroundColor = "";
838: field.value = "";
839: };
840: return df;
841: };
842:
843: TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
844: var i18n = TableOperations.I18N;
845: var fieldset = doc.createElement("fieldset");
846: var legend = doc.createElement("legend");
847: fieldset.appendChild(legend);
848: legend.innerHTML = i18n["Layout"];
849: var table = doc.createElement("table");
850: fieldset.appendChild(table);
851: table.style.width = "100%";
852: var tbody = doc.createElement("tbody");
853: table.appendChild(tbody);
854:
855: var tagname = el.tagName.toLowerCase();
856: var tr, td, input, select, option, options, i;
857:
858: if (tagname != "td" && tagname != "tr" && tagname != "th") {
859: tr = doc.createElement("tr");
860: tbody.appendChild(tr);
861: td = doc.createElement("td");
862: td.className = "label";
863: tr.appendChild(td);
864: td.innerHTML = i18n["Float"] + ":";
865: td = doc.createElement("td");
866: tr.appendChild(td);
867: select = doc.createElement("select");
868: td.appendChild(select);
869: select.name = "f_st_float";
870: options = ["None", "Left", "Right"];
871: for (i in options) {
872: var Val = options[i];
873: var val = options[i].toLowerCase();
874: option = doc.createElement("option");
875: option.innerHTML = i18n[Val];
876: option.value = val;
877: option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
878: select.appendChild(option);
879: }
880: }
881:
882: tr = doc.createElement("tr");
883: tbody.appendChild(tr);
884: td = doc.createElement("td");
885: td.className = "label";
886: tr.appendChild(td);
887: td.innerHTML = i18n["Width"] + ":";
888: td = doc.createElement("td");
889: tr.appendChild(td);
890: input = doc.createElement("input");
891: input.type = "text";
892: input.value = TableOperations.getLength(el.style.width);
893: input.size = "5";
894: input.name = "f_st_width";
895: input.style.marginRight = "0.5em";
896: td.appendChild(input);
897: select = doc.createElement("select");
898: select.name = "f_st_widthUnit";
899: option = doc.createElement("option");
900: option.innerHTML = i18n["percent"];
901: option.value = "%";
902: option.selected = /%/.test(el.style.width);
903: select.appendChild(option);
904: option = doc.createElement("option");
905: option.innerHTML = i18n["pixels"];
906: option.value = "px";
907: option.selected = /px/.test(el.style.width);
908: select.appendChild(option);
909: td.appendChild(select);
910:
911: select.style.marginRight = "0.5em";
912: td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
913: select = doc.createElement("select");
914: select.style.marginLeft = select.style.marginRight = "0.5em";
915: td.appendChild(select);
916: select.name = "f_st_textAlign";
917: options = ["Left", "Center", "Right", "Justify"];
918: if (tagname == "td") {
919: options.push("Char");
920: }
921: input = doc.createElement("input");
922: input.name = "f_st_textAlignChar";
923: input.size = "1";
924: input.style.fontFamily = "monospace";
925: td.appendChild(input);
926: for (i in options) {
927: var Val = options[i];
928: var val = Val.toLowerCase();
929: option = doc.createElement("option");
930: option.value = val;
931: option.innerHTML = i18n[Val];
932: option.selected = (el.style.textAlign.toLowerCase() == val);
933: select.appendChild(option);
934: }
935: function setCharVisibility(value) {
936: input.style.visibility = value ? "visible" : "hidden";
937: if (value) {
938: input.focus();
939: input.select();
940: }
941: };
942: select.onchange = function() { setCharVisibility(this.value == "char"); };
943: setCharVisibility(select.value == "char");
944:
945: tr = doc.createElement("tr");
946: tbody.appendChild(tr);
947: td = doc.createElement("td");
948: td.className = "label";
949: tr.appendChild(td);
950: td.innerHTML = i18n["Height"] + ":";
951: td = doc.createElement("td");
952: tr.appendChild(td);
953: input = doc.createElement("input");
954: input.type = "text";
955: input.value = TableOperations.getLength(el.style.height);
956: input.size = "5";
957: input.name = "f_st_height";
958: input.style.marginRight = "0.5em";
959: td.appendChild(input);
960: select = doc.createElement("select");
961: select.name = "f_st_heightUnit";
962: option = doc.createElement("option");
963: option.innerHTML = i18n["percent"];
964: option.value = "%";
965: option.selected = /%/.test(el.style.height);
966: select.appendChild(option);
967: option = doc.createElement("option");
968: option.innerHTML = i18n["pixels"];
969: option.value = "px";
970: option.selected = /px/.test(el.style.height);
971: select.appendChild(option);
972: td.appendChild(select);
973:
974: select.style.marginRight = "0.5em";
975: td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
976: select = doc.createElement("select");
977: select.name = "f_st_verticalAlign";
978: select.style.marginLeft = "0.5em";
979: td.appendChild(select);
980: options = ["Top", "Middle", "Bottom", "Baseline"];
981: for (i in options) {
982: var Val = options[i];
983: var val = Val.toLowerCase();
984: option = doc.createElement("option");
985: option.value = val;
986: option.innerHTML = i18n[Val];
987: option.selected = (el.style.verticalAlign.toLowerCase() == val);
988: select.appendChild(option);
989: }
990:
991: return fieldset;
992: };
993:
994: // Returns an HTML element containing the style attributes for the given
995: // element. This can be easily embedded into any dialog; the functionality is
996: // also provided.
997: TableOperations.createStyleFieldset = function(doc, editor, el) {
998: var i18n = TableOperations.I18N;
999: var fieldset = doc.createElement("fieldset");
1000: var legend = doc.createElement("legend");
1001: fieldset.appendChild(legend);
1002: legend.innerHTML = i18n["CSS Style"];
1003: var table = doc.createElement("table");
1004: fieldset.appendChild(table);
1005: table.style.width = "100%";
1006: var tbody = doc.createElement("tbody");
1007: table.appendChild(tbody);
1008:
1009: var tr, td, input, select, option, options, i;
1010:
1011: tr = doc.createElement("tr");
1012: tbody.appendChild(tr);
1013: td = doc.createElement("td");
1014: tr.appendChild(td);
1015: td.className = "label";
1016: td.innerHTML = i18n["Background"] + ":";
1017: td = doc.createElement("td");
1018: tr.appendChild(td);
1019: var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
1020: df.firstChild.nextSibling.style.marginRight = "0.5em";
1021: td.appendChild(df);
1022: td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
1023: input = doc.createElement("input");
1024: input.type = "text";
1025: input.name = "f_st_backgroundImage";
1026: if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
1027: input.value = RegExp.$1;
1028: }
1029: // input.style.width = "100%";
1030: td.appendChild(input);
1031:
1032: tr = doc.createElement("tr");
1033: tbody.appendChild(tr);
1034: td = doc.createElement("td");
1035: tr.appendChild(td);
1036: td.className = "label";
1037: td.innerHTML = i18n["FG Color"] + ":";
1038: td = doc.createElement("td");
1039: tr.appendChild(td);
1040: td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
1041:
1042: // for better alignment we include an invisible field.
1043: input = doc.createElement("input");
1044: input.style.visibility = "hidden";
1045: input.type = "text";
1046: td.appendChild(input);
1047:
1048: tr = doc.createElement("tr");
1049: tbody.appendChild(tr);
1050: td = doc.createElement("td");
1051: tr.appendChild(td);
1052: td.className = "label";
1053: td.innerHTML = i18n["Border"] + ":";
1054: td = doc.createElement("td");
1055: tr.appendChild(td);
1056:
1057: var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
1058: var btn = colorButton.firstChild.nextSibling;
1059: td.appendChild(colorButton);
1060: // borderFields.push(btn);
1061: btn.style.marginRight = "0.5em";
1062:
1063: select = doc.createElement("select");
1064: var borderFields = [];
1065: td.appendChild(select);
1066: select.name = "f_st_borderStyle";
1067: options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
1068: var currentBorderStyle = el.style.borderStyle;
1069: // Gecko reports "solid solid solid solid" for "border-style: solid".
1070: // That is, "top right bottom left" -- we only consider the first
1071: // value.
1072: (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
1073: for (i in options) {
1074: var val = options[i];
1075: option = doc.createElement("option");
1076: option.value = val;
1077: option.innerHTML = val;
1078: (val == currentBorderStyle) && (option.selected = true);
1079: select.appendChild(option);
1080: }
1081: select.style.marginRight = "0.5em";
1082: function setBorderFieldsStatus(value) {
1083: for (i in borderFields) {
1084: var el = borderFields[i];
1085: el.style.visibility = value ? "hidden" : "visible";
1086: if (!value && (el.tagName.toLowerCase() == "input")) {
1087: el.focus();
1088: el.select();
1089: }
1090: }
1091: };
1092: select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
1093:
1094: input = doc.createElement("input");
1095: borderFields.push(input);
1096: input.type = "text";
1097: input.name = "f_st_borderWidth";
1098: input.value = TableOperations.getLength(el.style.borderWidth);
1099: input.size = "5";
1100: td.appendChild(input);
1101: input.style.marginRight = "0.5em";
1102: var span = doc.createElement("span");
1103: span.innerHTML = i18n["pixels"];
1104: td.appendChild(span);
1105: borderFields.push(span);
1106:
1107: setBorderFieldsStatus(select.value == "none");
1108:
1109: if (el.tagName.toLowerCase() == "table") {
1110: // the border-collapse style is only for tables
1111: tr = doc.createElement("tr");
1112: tbody.appendChild(tr);
1113: td = doc.createElement("td");
1114: td.className = "label";
1115: tr.appendChild(td);
1116: input = doc.createElement("input");
1117: input.type = "checkbox";
1118: input.name = "f_st_borderCollapse";
1119: input.id = "f_st_borderCollapse";
1120: var val = (/collapse/i.test(el.style.borderCollapse));
1121: input.checked = val ? 1 : 0;
1122: td.appendChild(input);
1123:
1124: td = doc.createElement("td");
1125: tr.appendChild(td);
1126: var label = doc.createElement("label");
1127: label.htmlFor = "f_st_borderCollapse";
1128: label.innerHTML = i18n["Collapsed borders"];
1129: td.appendChild(label);
1130: }
1131:
1132: // tr = doc.createElement("tr");
1133: // tbody.appendChild(tr);
1134: // td = doc.createElement("td");
1135: // td.className = "label";
1136: // tr.appendChild(td);
1137: // td.innerHTML = i18n["Margin"] + ":";
1138: // td = doc.createElement("td");
1139: // tr.appendChild(td);
1140: // input = doc.createElement("input");
1141: // input.type = "text";
1142: // input.size = "5";
1143: // input.name = "f_st_margin";
1144: // td.appendChild(input);
1145: // input.style.marginRight = "0.5em";
1146: // td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
1147:
1148: // input = doc.createElement("input");
1149: // input.type = "text";
1150: // input.size = "5";
1151: // input.name = "f_st_padding";
1152: // td.appendChild(input);
1153: // input.style.marginLeft = "0.5em";
1154: // input.style.marginRight = "0.5em";
1155: // td.appendChild(doc.createTextNode(i18n["pixels"]));
1156:
1157: return fieldset;
1158: };
1159:
1160: //// END GENERIC CODE -------------------------------------------------------
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>