| // Table Operations Plugin for HTMLArea-3.0 |
| // Implementation by Mihai Bazon. Sponsored by http://www.bloki.com |
| // |
| // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc. |
| // This notice MUST stay intact for use (see license.txt). |
| // |
| // A free WYSIWYG editor replacement for <textarea> fields. |
| // For full source code and docs, visit http://www.interactivetools.com/ |
| // |
| // Version 3.0 developed by Mihai Bazon for InteractiveTools. |
| // http://dynarch.com/mishoo |
| // |
| // $Id: table-operations.js,v 1.6 2004/05/11 22:24:38 joerg Exp $ |
| |
| // Object that will encapsulate all the table operations provided by |
| // HTMLArea-3.0 (except "insert table" which is included in the main file) |
| function TableOperations(editor) { |
| this.editor = editor; |
| |
| var cfg = editor.config; |
| var tt = TableOperations.I18N; |
| var bl = TableOperations.btnList; |
| var self = this; |
| |
| // register the toolbar buttons provided by this plugin |
| var toolbar = ["linebreak"]; |
| for (var i in bl) { |
| var btn = bl[i]; |
| if (!btn) { |
| toolbar.push("separator"); |
| } else { |
| var id = "TO-" + btn[0]; |
| cfg.registerButton(id, tt[id], editor.imgURL(btn[0] + ".gif", "TableOperations"), false, |
| function(editor, id) { |
| // dispatch button press event |
| self.buttonPress(editor, id); |
| }, btn[1]); |
| toolbar.push(id); |
| } |
| } |
| |
| // add a new line in the toolbar |
| cfg.toolbar.push(toolbar); |
| }; |
| |
| TableOperations._pluginInfo = { |
| name : "TableOperations", |
| version : "1.0", |
| developer : "Mihai Bazon", |
| developer_url : "http://dynarch.com/mishoo/", |
| c_owner : "Mihai Bazon", |
| sponsor : "Zapatec Inc.", |
| sponsor_url : "http://www.bloki.com", |
| license : "htmlArea" |
| }; |
| |
| /************************ |
| * UTILITIES |
| ************************/ |
| |
| // retrieves the closest element having the specified tagName in the list of |
| // ancestors of the current selection/caret. |
| TableOperations.prototype.getClosest = function(tagName) { |
| var editor = this.editor; |
| var ancestors = editor.getAllAncestors(); |
| var ret = null; |
| tagName = ("" + tagName).toLowerCase(); |
| for (var i in ancestors) { |
| var el = ancestors[i]; |
| if (el.tagName.toLowerCase() == tagName) { |
| ret = el; |
| break; |
| } |
| } |
| return ret; |
| }; |
| |
| // this function requires the file PopupDiv/PopupWin to be loaded from browser |
| TableOperations.prototype.dialogTableProperties = function() { |
| var i18n = TableOperations.I18N; |
| // retrieve existing values |
| var table = this.getClosest("table"); |
| // this.editor.selectNodeContents(table); |
| // this.editor.updateToolbar(); |
| |
| var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) { |
| TableOperations.processStyle(params, table); |
| for (var i in params) { |
| var val = params[i]; |
| switch (i) { |
| case "f_caption": |
| if (/\S/.test(val)) { |
| // contains non white-space characters |
| var caption = table.getElementsByTagName("caption")[0]; |
| if (!caption) { |
| caption = dialog.editor._doc.createElement("caption"); |
| table.insertBefore(caption, table.firstChild); |
| } |
| caption.innerHTML = val; |
| } else { |
| // search for caption and delete it if found |
| var caption = table.getElementsByTagName("caption")[0]; |
| if (caption) { |
| caption.parentNode.removeChild(caption); |
| } |
| } |
| break; |
| case "f_summary": |
| table.summary = val; |
| break; |
| case "f_width": |
| table.style.width = ("" + val) + params.f_unit; |
| break; |
| case "f_align": |
| table.align = val; |
| break; |
| case "f_spacing": |
| table.cellSpacing = val; |
| break; |
| case "f_padding": |
| table.cellPadding = val; |
| break; |
| case "f_borders": |
| table.border = val; |
| break; |
| case "f_frames": |
| table.frame = val; |
| break; |
| case "f_rules": |
| table.rules = val; |
| break; |
| } |
| } |
| // various workarounds to refresh the table display (Gecko, |
| // what's going on?! do not disappoint me!) |
| dialog.editor.forceRedraw(); |
| dialog.editor.focusEditor(); |
| dialog.editor.updateToolbar(); |
| var save_collapse = table.style.borderCollapse; |
| table.style.borderCollapse = "collapse"; |
| table.style.borderCollapse = "separate"; |
| table.style.borderCollapse = save_collapse; |
| }, |
| |
| // this function gets called when the dialog needs to be initialized |
| function (dialog) { |
| |
| var f_caption = ""; |
| var capel = table.getElementsByTagName("caption")[0]; |
| if (capel) { |
| f_caption = capel.innerHTML; |
| } |
| var f_summary = table.summary; |
| var f_width = parseInt(table.style.width); |
| isNaN(f_width) && (f_width = ""); |
| var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels'; |
| var f_align = table.align; |
| var f_spacing = table.cellSpacing; |
| var f_padding = table.cellPadding; |
| var f_borders = table.border; |
| var f_frames = table.frame; |
| var f_rules = table.rules; |
| |
| function selected(val) { |
| return val ? " selected" : ""; |
| }; |
| |
| // dialog contents |
| dialog.content.style.width = "400px"; |
| dialog.content.innerHTML = " \ |
| <div class='title'\ |
| style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\ |
| </div> \ |
| <table style='width:100%'> \ |
| <tr> \ |
| <td> \ |
| <fieldset><legend>" + i18n["Description"] + "</legend> \ |
| <table style='width:100%'> \ |
| <tr> \ |
| <td class='label'>" + i18n["Caption"] + ":</td> \ |
| <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \ |
| </tr><tr> \ |
| <td class='label'>" + i18n["Summary"] + ":</td> \ |
| <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \ |
| </tr> \ |
| </table> \ |
| </fieldset> \ |
| </td> \ |
| </tr> \ |
| <tr><td id='--HA-layout'></td></tr> \ |
| <tr> \ |
| <td> \ |
| <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \ |
| <table style='width:100%'> \ |
| "+// <tr> \ |
| // <td class='label'>" + i18n["Width"] + ":</td> \ |
| // <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \ |
| // <select name='f_unit'> \ |
| // <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \ |
| // <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \ |
| // </select> " + i18n["Align"] + ": \ |
| // <select name='f_align'> \ |
| // <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \ |
| // <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \ |
| // <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \ |
| // </select> \ |
| // </td> \ |
| // </tr> \ |
| " <tr> \ |
| <td class='label'>" + i18n["Spacing"] + ":</td> \ |
| <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> " + i18n["Padding"] + ":\ |
| <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> " + i18n["pixels"] + "\ |
| </td> \ |
| </tr> \ |
| </table> \ |
| </fieldset> \ |
| </td> \ |
| </tr> \ |
| <tr> \ |
| <td> \ |
| <fieldset><legend>Frame and borders</legend> \ |
| <table width='100%'> \ |
| <tr> \ |
| <td class='label'>" + i18n["Borders"] + ":</td> \ |
| <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> " + i18n["pixels"] + "</td> \ |
| </tr> \ |
| <tr> \ |
| <td class='label'>" + i18n["Frames"] + ":</td> \ |
| <td> \ |
| <select name='f_frames'> \ |
| <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \ |
| <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \ |
| <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \ |
| <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \ |
| <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \ |
| <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \ |
| <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \ |
| <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \ |
| </select> \ |
| </td> \ |
| </tr> \ |
| <tr> \ |
| <td class='label'>" + i18n["Rules"] + ":</td> \ |
| <td> \ |
| <select name='f_rules'> \ |
| <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \ |
| <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \ |
| <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \ |
| <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \ |
| </select> \ |
| </td> \ |
| </tr> \ |
| </table> \ |
| </fieldset> \ |
| </td> \ |
| </tr> \ |
| <tr> \ |
| <td id='--HA-style'></td> \ |
| </tr> \ |
| </table> \ |
| "; |
| var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table); |
| var p = dialog.doc.getElementById("--HA-style"); |
| p.appendChild(st_prop); |
| var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table); |
| p = dialog.doc.getElementById("--HA-layout"); |
| p.appendChild(st_layout); |
| dialog.modal = true; |
| dialog.addButtons("ok", "cancel"); |
| dialog.showAtElement(dialog.editor._iframe, "c"); |
| }); |
| }; |
| |
| // this function requires the file PopupDiv/PopupWin to be loaded from browser |
| TableOperations.prototype.dialogRowCellProperties = function(cell) { |
| var i18n = TableOperations.I18N; |
| // retrieve existing values |
| var element = this.getClosest(cell ? "td" : "tr"); |
| var table = this.getClosest("table"); |
| // this.editor.selectNodeContents(element); |
| // this.editor.updateToolbar(); |
| |
| var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) { |
| TableOperations.processStyle(params, element); |
| for (var i in params) { |
| var val = params[i]; |
| switch (i) { |
| case "f_align": |
| element.align = val; |
| break; |
| case "f_char": |
| element.ch = val; |
| break; |
| case "f_valign": |
| element.vAlign = val; |
| break; |
| } |
| } |
| // various workarounds to refresh the table display (Gecko, |
| // what's going on?! do not disappoint me!) |
| dialog.editor.forceRedraw(); |
| dialog.editor.focusEditor(); |
| dialog.editor.updateToolbar(); |
| var save_collapse = table.style.borderCollapse; |
| table.style.borderCollapse = "collapse"; |
| table.style.borderCollapse = "separate"; |
| table.style.borderCollapse = save_collapse; |
| }, |
| |
| // this function gets called when the dialog needs to be initialized |
| function (dialog) { |
| |
| var f_align = element.align; |
| var f_valign = element.vAlign; |
| var f_char = element.ch; |
| |
| function selected(val) { |
| return val ? " selected" : ""; |
| }; |
| |
| // dialog contents |
| dialog.content.style.width = "400px"; |
| dialog.content.innerHTML = " \ |
| <div class='title'\ |
| 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> \ |
| <table style='width:100%'> \ |
| <tr> \ |
| <td id='--HA-layout'> \ |
| "+// <fieldset><legend>" + i18n["Layout"] + "</legend> \ |
| // <table style='width:100%'> \ |
| // <tr> \ |
| // <td class='label'>" + i18n["Align"] + ":</td> \ |
| // <td> \ |
| // <select name='f_align'> \ |
| // <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \ |
| // <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \ |
| // <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \ |
| // <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \ |
| // </select> \ |
| // " + i18n["Char"] + ": \ |
| // <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \ |
| // </td> \ |
| // </tr><tr> \ |
| // <td class='label'>" + i18n["Vertical align"] + ":</td> \ |
| // <td> \ |
| // <select name='f_valign'> \ |
| // <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \ |
| // <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \ |
| // <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \ |
| // <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \ |
| // </select> \ |
| // </td> \ |
| // </tr> \ |
| // </table> \ |
| // </fieldset> \ |
| " </td> \ |
| </tr> \ |
| <tr> \ |
| <td id='--HA-style'></td> \ |
| </tr> \ |
| </table> \ |
| "; |
| var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element); |
| var p = dialog.doc.getElementById("--HA-style"); |
| p.appendChild(st_prop); |
| var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element); |
| p = dialog.doc.getElementById("--HA-layout"); |
| p.appendChild(st_layout); |
| dialog.modal = true; |
| dialog.addButtons("ok", "cancel"); |
| dialog.showAtElement(dialog.editor._iframe, "c"); |
| }); |
| }; |
| |
| // this function gets called when some button from the TableOperations toolbar |
| // was pressed. |
| TableOperations.prototype.buttonPress = function(editor, button_id) { |
| this.editor = editor; |
| var mozbr = HTMLArea.is_gecko ? "<br />" : ""; |
| var i18n = TableOperations.I18N; |
| |
| // helper function that clears the content in a table row |
| function clearRow(tr) { |
| var tds = tr.getElementsByTagName("td"); |
| for (var i = tds.length; --i >= 0;) { |
| var td = tds[i]; |
| td.rowSpan = 1; |
| td.innerHTML = mozbr; |
| } |
| }; |
| |
| function splitRow(td) { |
| var n = parseInt("" + td.rowSpan); |
| var nc = parseInt("" + td.colSpan); |
| td.rowSpan = 1; |
| tr = td.parentNode; |
| var itr = tr.rowIndex; |
| var trs = tr.parentNode.rows; |
| var index = td.cellIndex; |
| while (--n > 0) { |
| tr = trs[++itr]; |
| var otd = editor._doc.createElement("td"); |
| otd.colSpan = td.colSpan; |
| otd.innerHTML = mozbr; |
| tr.insertBefore(otd, tr.cells[index]); |
| } |
| editor.forceRedraw(); |
| editor.updateToolbar(); |
| }; |
| |
| function splitCol(td) { |
| var nc = parseInt("" + td.colSpan); |
| td.colSpan = 1; |
| tr = td.parentNode; |
| var ref = td.nextSibling; |
| while (--nc > 0) { |
| var otd = editor._doc.createElement("td"); |
| otd.rowSpan = td.rowSpan; |
| otd.innerHTML = mozbr; |
| tr.insertBefore(otd, ref); |
| } |
| editor.forceRedraw(); |
| editor.updateToolbar(); |
| }; |
| |
| function splitCell(td) { |
| var nc = parseInt("" + td.colSpan); |
| splitCol(td); |
| var items = td.parentNode.cells; |
| var index = td.cellIndex; |
| while (nc-- > 0) { |
| splitRow(items[index++]); |
| } |
| }; |
| |
| function selectNextNode(el) { |
| var node = el.nextSibling; |
| while (node && node.nodeType != 1) { |
| node = node.nextSibling; |
| } |
| if (!node) { |
| node = el.previousSibling; |
| while (node && node.nodeType != 1) { |
| node = node.previousSibling; |
| } |
| } |
| if (!node) { |
| node = el.parentNode; |
| } |
| editor.selectNodeContents(node); |
| }; |
| |
| switch (button_id) { |
| // ROWS |
| |
| case "TO-row-insert-above": |
| case "TO-row-insert-under": |
| var tr = this.getClosest("tr"); |
| if (!tr) { |
| break; |
| } |
| var otr = tr.cloneNode(true); |
| clearRow(otr); |
| tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr); |
| editor.forceRedraw(); |
| editor.focusEditor(); |
| break; |
| case "TO-row-delete": |
| var tr = this.getClosest("tr"); |
| if (!tr) { |
| break; |
| } |
| var par = tr.parentNode; |
| if (par.rows.length == 1) { |
| alert(i18n["not-del-last-row"]); |
| break; |
| } |
| // set the caret first to a position that doesn't |
| // disappear. |
| selectNextNode(tr); |
| par.removeChild(tr); |
| editor.forceRedraw(); |
| editor.focusEditor(); |
| editor.updateToolbar(); |
| break; |
| case "TO-row-split": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| splitRow(td); |
| break; |
| |
| // COLUMNS |
| |
| case "TO-col-insert-before": |
| case "TO-col-insert-after": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| var rows = td.parentNode.parentNode.rows; |
| var index = td.cellIndex; |
| for (var i = rows.length; --i >= 0;) { |
| var tr = rows[i]; |
| var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)]; |
| var otd = editor._doc.createElement("td"); |
| otd.innerHTML = mozbr; |
| tr.insertBefore(otd, ref); |
| } |
| editor.focusEditor(); |
| break; |
| case "TO-col-split": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| splitCol(td); |
| break; |
| case "TO-col-delete": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| var index = td.cellIndex; |
| if (td.parentNode.cells.length == 1) { |
| alert(i18n["not-del-last-col"]); |
| break; |
| } |
| // set the caret first to a position that doesn't disappear |
| selectNextNode(td); |
| var rows = td.parentNode.parentNode.rows; |
| for (var i = rows.length; --i >= 0;) { |
| var tr = rows[i]; |
| tr.removeChild(tr.cells[index]); |
| } |
| editor.forceRedraw(); |
| editor.focusEditor(); |
| editor.updateToolbar(); |
| break; |
| |
| // CELLS |
| |
| case "TO-cell-split": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| splitCell(td); |
| break; |
| case "TO-cell-insert-before": |
| case "TO-cell-insert-after": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| var tr = td.parentNode; |
| var otd = editor._doc.createElement("td"); |
| otd.innerHTML = mozbr; |
| tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td); |
| editor.forceRedraw(); |
| editor.focusEditor(); |
| break; |
| case "TO-cell-delete": |
| var td = this.getClosest("td"); |
| if (!td) { |
| break; |
| } |
| if (td.parentNode.cells.length == 1) { |
| alert(i18n["not-del-last-cell"]); |
| break; |
| } |
| // set the caret first to a position that doesn't disappear |
| selectNextNode(td); |
| td.parentNode.removeChild(td); |
| editor.forceRedraw(); |
| editor.updateToolbar(); |
| break; |
| case "TO-cell-merge": |
| // !! FIXME: Mozilla specific !! |
| var sel = editor._getSelection(); |
| var range, i = 0; |
| var rows = []; |
| var row = null; |
| var cells = null; |
| if (!HTMLArea.is_ie) { |
| try { |
| while (range = sel.getRangeAt(i++)) { |
| var td = range.startContainer.childNodes[range.startOffset]; |
| if (td.parentNode != row) { |
| row = td.parentNode; |
| (cells) && rows.push(cells); |
| cells = []; |
| } |
| cells.push(td); |
| } |
| } catch(e) {/* finished walking through selection */} |
| rows.push(cells); |
| } else { |
| // Internet Explorer "browser" |
| var td = this.getClosest("td"); |
| if (!td) { |
| alert(i18n["Please click into some cell"]); |
| break; |
| } |
| var tr = td.parentElement; |
| var no_cols = prompt(i18n["How many columns would you like to merge?"], 2); |
| if (!no_cols) { |
| // cancelled |
| break; |
| } |
| var no_rows = prompt(i18n["How many rows would you like to merge?"], 2); |
| if (!no_rows) { |
| // cancelled |
| break; |
| } |
| var cell_index = td.cellIndex; |
| while (no_rows-- > 0) { |
| td = tr.cells[cell_index]; |
| cells = [td]; |
| for (var i = 1; i < no_cols; ++i) { |
| td = td.nextSibling; |
| if (!td) { |
| break; |
| } |
| cells.push(td); |
| } |
| rows.push(cells); |
| tr = tr.nextSibling; |
| if (!tr) { |
| break; |
| } |
| } |
| } |
| var HTML = ""; |
| for (i = 0; i < rows.length; ++i) { |
| // i && (HTML += "<br />"); |
| var cells = rows[i]; |
| for (var j = 0; j < cells.length; ++j) { |
| // j && (HTML += " "); |
| var cell = cells[j]; |
| HTML += cell.innerHTML; |
| (i || j) && (cell.parentNode.removeChild(cell)); |
| } |
| } |
| var td = rows[0][0]; |
| td.innerHTML = HTML; |
| td.rowSpan = rows.length; |
| td.colSpan = rows[0].length; |
| editor.selectNodeContents(td); |
| editor.forceRedraw(); |
| editor.focusEditor(); |
| break; |
| |
| // PROPERTIES |
| |
| case "TO-table-prop": |
| this.dialogTableProperties(); |
| break; |
| |
| case "TO-row-prop": |
| this.dialogRowCellProperties(false); |
| break; |
| |
| case "TO-cell-prop": |
| this.dialogRowCellProperties(true); |
| break; |
| |
| default: |
| alert("Button [" + button_id + "] not yet implemented"); |
| } |
| }; |
| |
| // the list of buttons added by this plugin |
| TableOperations.btnList = [ |
| // table properties button |
| ["table-prop", "table"], |
| null, // separator |
| |
| // ROWS |
| ["row-prop", "tr"], |
| ["row-insert-above", "tr"], |
| ["row-insert-under", "tr"], |
| ["row-delete", "tr"], |
| ["row-split", "td[rowSpan!=1]"], |
| null, |
| |
| // COLS |
| ["col-insert-before", "td"], |
| ["col-insert-after", "td"], |
| ["col-delete", "td"], |
| ["col-split", "td[colSpan!=1]"], |
| null, |
| |
| // CELLS |
| ["cell-prop", "td"], |
| ["cell-insert-before", "td"], |
| ["cell-insert-after", "td"], |
| ["cell-delete", "td"], |
| ["cell-merge", "tr"], |
| ["cell-split", "td[colSpan!=1,rowSpan!=1]"] |
| ]; |
| |
| |
| |
| //// GENERIC CODE [style of any element; this should be moved into a separate |
| //// file as it'll be very useful] |
| //// BEGIN GENERIC CODE ----------------------------------------------------- |
| |
| TableOperations.getLength = function(value) { |
| var len = parseInt(value); |
| if (isNaN(len)) { |
| len = ""; |
| } |
| return len; |
| }; |
| |
| // Applies the style found in "params" to the given element. |
| TableOperations.processStyle = function(params, element) { |
| var style = element.style; |
| for (var i in params) { |
| var val = params[i]; |
| switch (i) { |
| case "f_st_backgroundColor": |
| style.backgroundColor = val; |
| break; |
| case "f_st_color": |
| style.color = val; |
| break; |
| case "f_st_backgroundImage": |
| if (/\S/.test(val)) { |
| style.backgroundImage = "url(" + val + ")"; |
| } else { |
| style.backgroundImage = "none"; |
| } |
| break; |
| case "f_st_borderWidth": |
| style.borderWidth = val; |
| break; |
| case "f_st_borderStyle": |
| style.borderStyle = val; |
| break; |
| case "f_st_borderColor": |
| style.borderColor = val; |
| break; |
| case "f_st_borderCollapse": |
| style.borderCollapse = val ? "collapse" : ""; |
| break; |
| case "f_st_width": |
| if (/\S/.test(val)) { |
| style.width = val + params["f_st_widthUnit"]; |
| } else { |
| style.width = ""; |
| } |
| break; |
| case "f_st_height": |
| if (/\S/.test(val)) { |
| style.height = val + params["f_st_heightUnit"]; |
| } else { |
| style.height = ""; |
| } |
| break; |
| case "f_st_textAlign": |
| if (val == "char") { |
| var ch = params["f_st_textAlignChar"]; |
| if (ch == '"') { |
| ch = '\\"'; |
| } |
| style.textAlign = '"' + ch + '"'; |
| } else { |
| style.textAlign = val; |
| } |
| break; |
| case "f_st_verticalAlign": |
| style.verticalAlign = val; |
| break; |
| case "f_st_float": |
| style.cssFloat = val; |
| break; |
| // case "f_st_margin": |
| // style.margin = val + "px"; |
| // break; |
| // case "f_st_padding": |
| // style.padding = val + "px"; |
| // break; |
| } |
| } |
| }; |
| |
| // Returns an HTML element for a widget that allows color selection. That is, |
| // a button that contains the given color, if any, and when pressed will popup |
| // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user |
| // to select some color. If a color is selected, an input field with the name |
| // "f_st_"+name will be updated with the color value in #123456 format. |
| TableOperations.createColorButton = function(doc, editor, color, name) { |
| if (!color) { |
| color = ""; |
| } else if (!/#/.test(color)) { |
| color = HTMLArea._colorToRgb(color); |
| } |
| |
| var df = doc.createElement("span"); |
| var field = doc.createElement("input"); |
| field.type = "hidden"; |
| df.appendChild(field); |
| field.name = "f_st_" + name; |
| field.value = color; |
| var button = doc.createElement("span"); |
| button.className = "buttonColor"; |
| df.appendChild(button); |
| var span = doc.createElement("span"); |
| span.className = "chooser"; |
| // span.innerHTML = " "; |
| span.style.backgroundColor = color; |
| button.appendChild(span); |
| button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }}; |
| button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }}; |
| span.onclick = function() { |
| if (this.parentNode.disabled) { |
| return false; |
| } |
| editor._popupDialog("select_color.html", function(color) { |
| if (color) { |
| span.style.backgroundColor = "#" + color; |
| field.value = "#" + color; |
| } |
| }, color); |
| }; |
| var span2 = doc.createElement("span"); |
| span2.innerHTML = "×"; |
| span2.className = "nocolor"; |
| span2.title = TableOperations.I18N["Unset color"]; |
| button.appendChild(span2); |
| span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }}; |
| span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }}; |
| span2.onclick = function() { |
| span.style.backgroundColor = ""; |
| field.value = ""; |
| }; |
| return df; |
| }; |
| |
| TableOperations.createStyleLayoutFieldset = function(doc, editor, el) { |
| var i18n = TableOperations.I18N; |
| var fieldset = doc.createElement("fieldset"); |
| var legend = doc.createElement("legend"); |
| fieldset.appendChild(legend); |
| legend.innerHTML = i18n["Layout"]; |
| var table = doc.createElement("table"); |
| fieldset.appendChild(table); |
| table.style.width = "100%"; |
| var tbody = doc.createElement("tbody"); |
| table.appendChild(tbody); |
| |
| var tagname = el.tagName.toLowerCase(); |
| var tr, td, input, select, option, options, i; |
| |
| if (tagname != "td" && tagname != "tr" && tagname != "th") { |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| td.className = "label"; |
| tr.appendChild(td); |
| td.innerHTML = i18n["Float"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| select = doc.createElement("select"); |
| td.appendChild(select); |
| select.name = "f_st_float"; |
| options = ["None", "Left", "Right"]; |
| for (i in options) { |
| var Val = options[i]; |
| var val = options[i].toLowerCase(); |
| option = doc.createElement("option"); |
| option.innerHTML = i18n[Val]; |
| option.value = val; |
| option.selected = (("" + el.style.cssFloat).toLowerCase() == val); |
| select.appendChild(option); |
| } |
| } |
| |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| td.className = "label"; |
| tr.appendChild(td); |
| td.innerHTML = i18n["Width"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| input = doc.createElement("input"); |
| input.type = "text"; |
| input.value = TableOperations.getLength(el.style.width); |
| input.size = "5"; |
| input.name = "f_st_width"; |
| input.style.marginRight = "0.5em"; |
| td.appendChild(input); |
| select = doc.createElement("select"); |
| select.name = "f_st_widthUnit"; |
| option = doc.createElement("option"); |
| option.innerHTML = i18n["percent"]; |
| option.value = "%"; |
| option.selected = /%/.test(el.style.width); |
| select.appendChild(option); |
| option = doc.createElement("option"); |
| option.innerHTML = i18n["pixels"]; |
| option.value = "px"; |
| option.selected = /px/.test(el.style.width); |
| select.appendChild(option); |
| td.appendChild(select); |
| |
| select.style.marginRight = "0.5em"; |
| td.appendChild(doc.createTextNode(i18n["Text align"] + ":")); |
| select = doc.createElement("select"); |
| select.style.marginLeft = select.style.marginRight = "0.5em"; |
| td.appendChild(select); |
| select.name = "f_st_textAlign"; |
| options = ["Left", "Center", "Right", "Justify"]; |
| if (tagname == "td") { |
| options.push("Char"); |
| } |
| input = doc.createElement("input"); |
| input.name = "f_st_textAlignChar"; |
| input.size = "1"; |
| input.style.fontFamily = "monospace"; |
| td.appendChild(input); |
| for (i in options) { |
| var Val = options[i]; |
| var val = Val.toLowerCase(); |
| option = doc.createElement("option"); |
| option.value = val; |
| option.innerHTML = i18n[Val]; |
| option.selected = (el.style.textAlign.toLowerCase() == val); |
| select.appendChild(option); |
| } |
| function setCharVisibility(value) { |
| input.style.visibility = value ? "visible" : "hidden"; |
| if (value) { |
| input.focus(); |
| input.select(); |
| } |
| }; |
| select.onchange = function() { setCharVisibility(this.value == "char"); }; |
| setCharVisibility(select.value == "char"); |
| |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| td.className = "label"; |
| tr.appendChild(td); |
| td.innerHTML = i18n["Height"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| input = doc.createElement("input"); |
| input.type = "text"; |
| input.value = TableOperations.getLength(el.style.height); |
| input.size = "5"; |
| input.name = "f_st_height"; |
| input.style.marginRight = "0.5em"; |
| td.appendChild(input); |
| select = doc.createElement("select"); |
| select.name = "f_st_heightUnit"; |
| option = doc.createElement("option"); |
| option.innerHTML = i18n["percent"]; |
| option.value = "%"; |
| option.selected = /%/.test(el.style.height); |
| select.appendChild(option); |
| option = doc.createElement("option"); |
| option.innerHTML = i18n["pixels"]; |
| option.value = "px"; |
| option.selected = /px/.test(el.style.height); |
| select.appendChild(option); |
| td.appendChild(select); |
| |
| select.style.marginRight = "0.5em"; |
| td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":")); |
| select = doc.createElement("select"); |
| select.name = "f_st_verticalAlign"; |
| select.style.marginLeft = "0.5em"; |
| td.appendChild(select); |
| options = ["Top", "Middle", "Bottom", "Baseline"]; |
| for (i in options) { |
| var Val = options[i]; |
| var val = Val.toLowerCase(); |
| option = doc.createElement("option"); |
| option.value = val; |
| option.innerHTML = i18n[Val]; |
| option.selected = (el.style.verticalAlign.toLowerCase() == val); |
| select.appendChild(option); |
| } |
| |
| return fieldset; |
| }; |
| |
| // Returns an HTML element containing the style attributes for the given |
| // element. This can be easily embedded into any dialog; the functionality is |
| // also provided. |
| TableOperations.createStyleFieldset = function(doc, editor, el) { |
| var i18n = TableOperations.I18N; |
| var fieldset = doc.createElement("fieldset"); |
| var legend = doc.createElement("legend"); |
| fieldset.appendChild(legend); |
| legend.innerHTML = i18n["CSS Style"]; |
| var table = doc.createElement("table"); |
| fieldset.appendChild(table); |
| table.style.width = "100%"; |
| var tbody = doc.createElement("tbody"); |
| table.appendChild(tbody); |
| |
| var tr, td, input, select, option, options, i; |
| |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| td.className = "label"; |
| td.innerHTML = i18n["Background"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor"); |
| df.firstChild.nextSibling.style.marginRight = "0.5em"; |
| td.appendChild(df); |
| td.appendChild(doc.createTextNode(i18n["Image URL"] + ": ")); |
| input = doc.createElement("input"); |
| input.type = "text"; |
| input.name = "f_st_backgroundImage"; |
| if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) { |
| input.value = RegExp.$1; |
| } |
| // input.style.width = "100%"; |
| td.appendChild(input); |
| |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| td.className = "label"; |
| td.innerHTML = i18n["FG Color"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color")); |
| |
| // for better alignment we include an invisible field. |
| input = doc.createElement("input"); |
| input.style.visibility = "hidden"; |
| input.type = "text"; |
| td.appendChild(input); |
| |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| td.className = "label"; |
| td.innerHTML = i18n["Border"] + ":"; |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| |
| var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor"); |
| var btn = colorButton.firstChild.nextSibling; |
| td.appendChild(colorButton); |
| // borderFields.push(btn); |
| btn.style.marginRight = "0.5em"; |
| |
| select = doc.createElement("select"); |
| var borderFields = []; |
| td.appendChild(select); |
| select.name = "f_st_borderStyle"; |
| options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"]; |
| var currentBorderStyle = el.style.borderStyle; |
| // Gecko reports "solid solid solid solid" for "border-style: solid". |
| // That is, "top right bottom left" -- we only consider the first |
| // value. |
| (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1); |
| for (i in options) { |
| var val = options[i]; |
| option = doc.createElement("option"); |
| option.value = val; |
| option.innerHTML = val; |
| (val == currentBorderStyle) && (option.selected = true); |
| select.appendChild(option); |
| } |
| select.style.marginRight = "0.5em"; |
| function setBorderFieldsStatus(value) { |
| for (i in borderFields) { |
| var el = borderFields[i]; |
| el.style.visibility = value ? "hidden" : "visible"; |
| if (!value && (el.tagName.toLowerCase() == "input")) { |
| el.focus(); |
| el.select(); |
| } |
| } |
| }; |
| select.onchange = function() { setBorderFieldsStatus(this.value == "none"); }; |
| |
| input = doc.createElement("input"); |
| borderFields.push(input); |
| input.type = "text"; |
| input.name = "f_st_borderWidth"; |
| input.value = TableOperations.getLength(el.style.borderWidth); |
| input.size = "5"; |
| td.appendChild(input); |
| input.style.marginRight = "0.5em"; |
| var span = doc.createElement("span"); |
| span.innerHTML = i18n["pixels"]; |
| td.appendChild(span); |
| borderFields.push(span); |
| |
| setBorderFieldsStatus(select.value == "none"); |
| |
| if (el.tagName.toLowerCase() == "table") { |
| // the border-collapse style is only for tables |
| tr = doc.createElement("tr"); |
| tbody.appendChild(tr); |
| td = doc.createElement("td"); |
| td.className = "label"; |
| tr.appendChild(td); |
| input = doc.createElement("input"); |
| input.type = "checkbox"; |
| input.name = "f_st_borderCollapse"; |
| input.id = "f_st_borderCollapse"; |
| var val = (/collapse/i.test(el.style.borderCollapse)); |
| input.checked = val ? 1 : 0; |
| td.appendChild(input); |
| |
| td = doc.createElement("td"); |
| tr.appendChild(td); |
| var label = doc.createElement("label"); |
| label.htmlFor = "f_st_borderCollapse"; |
| label.innerHTML = i18n["Collapsed borders"]; |
| td.appendChild(label); |
| } |
| |
| // tr = doc.createElement("tr"); |
| // tbody.appendChild(tr); |
| // td = doc.createElement("td"); |
| // td.className = "label"; |
| // tr.appendChild(td); |
| // td.innerHTML = i18n["Margin"] + ":"; |
| // td = doc.createElement("td"); |
| // tr.appendChild(td); |
| // input = doc.createElement("input"); |
| // input.type = "text"; |
| // input.size = "5"; |
| // input.name = "f_st_margin"; |
| // td.appendChild(input); |
| // input.style.marginRight = "0.5em"; |
| // td.appendChild(doc.createTextNode(i18n["Padding"] + ":")); |
| |
| // input = doc.createElement("input"); |
| // input.type = "text"; |
| // input.size = "5"; |
| // input.name = "f_st_padding"; |
| // td.appendChild(input); |
| // input.style.marginLeft = "0.5em"; |
| // input.style.marginRight = "0.5em"; |
| // td.appendChild(doc.createTextNode(i18n["pixels"])); |
| |
| return fieldset; |
| }; |
| |
| //// END GENERIC CODE ------------------------------------------------------- |