You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2010/03/29 03:14:49 UTC
svn commit: r928555 [4/16] - in
/myfaces/tomahawk/trunk/core20/src/main/resources/META-INF/resources:
oam.custom.calendar.DB/ oam.custom.calendar.WH/ oam.custom.calendar.images/
oam.custom.inputHtml.kupudrawers/ oam.custom.inputHtml.kupuimages/ oam.cus...
Added: myfaces/tomahawk/trunk/core20/src/main/resources/META-INF/resources/oam.custom.inputHtml/kupubasetools.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/resources/META-INF/resources/oam.custom.inputHtml/kupubasetools.js?rev=928555&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/resources/META-INF/resources/oam.custom.inputHtml/kupubasetools.js (added)
+++ myfaces/tomahawk/trunk/core20/src/main/resources/META-INF/resources/oam.custom.inputHtml/kupubasetools.js Mon Mar 29 01:14:43 2010
@@ -0,0 +1,2952 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ *
+ *****************************************************************************/
+// $Id: kupubasetools.js 928511 2010-03-28 22:53:14Z lu4242 $
+
+//----------------------------------------------------------------------------
+//
+// Toolboxes
+//
+// These are addons for Kupu, simple plugins that implement a certain
+// interface to provide functionality and control view aspects.
+//
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Superclasses
+//----------------------------------------------------------------------------
+
+function KupuTool() {
+ /* Superclass (or actually more of an interface) for tools
+
+ Tools must implement at least an initialize method and an
+ updateState method, and can implement other methods to add
+ certain extra functionality (e.g. createContextMenuElements).
+ */
+
+ this.toolboxes = {};
+
+ // private methods
+ addEventHandler = addEventHandler;
+};
+
+// methods
+KupuTool.prototype.initialize = function(editor) {
+ /* Initialize the tool.
+
+ Obviously this can be overriden but it will do
+ for the most simple cases
+ */
+ this.editor = editor;
+};
+
+KupuTool.prototype.registerToolBox = function(id, toolbox) {
+ /* register a ui box
+
+ Note that this needs to be called *after* the tool has been
+ registered to the KupuEditor
+ */
+ this.toolboxes[id] = toolbox;
+ toolbox.initialize(this, this.editor);
+};
+
+KupuTool.prototype.updateState = function(selNode, event) {
+ /* Is called when user moves cursor to other element
+
+ Calls the updateState for all toolboxes and may want perform
+ some actions itself
+ */
+ for (var id in this.toolboxes) {
+ this.toolboxes[id].updateState(selNode, event);
+ };
+};
+
+KupuTool.prototype.enable = function() {
+ // Called when the tool is enabled after a form is dismissed.
+};
+
+KupuTool.prototype.disable = function() {
+ // Called when the tool is disabled (e.g. for a modal form)
+};
+
+function KupuToolBox() {
+ /* Superclass for a user-interface object that controls a tool */
+};
+
+KupuToolBox.prototype.initialize = function(tool, editor) {
+ /* store a reference to the tool and the editor */
+ this.tool = tool;
+ this.editor = editor;
+};
+
+KupuToolBox.prototype.updateState = function(selNode, event) {
+ /* update the toolbox according to the current iframe's situation */
+};
+
+function noContextMenu(object) {
+ /* Decorator for a tool to suppress the context menu */
+ object.createContextMenuElements = function(selNode, event) {
+ return [];
+ };
+ return object;
+}
+
+// Helper function for enabling/disabling tools
+function kupuButtonDisable(button) {
+ button = button || this.button;
+ if (button) {
+ button.disabled = "disabled";
+ button.className += ' disabled';
+ }
+};
+
+function kupuButtonEnable(button) {
+ button = button || this.button;
+ if (button) {
+ button.disabled = "";
+ button.className = button.className.replace(/ *\bdisabled\b/g, '');
+ }
+};
+
+//----------------------------------------------------------------------------
+// Implementations
+//----------------------------------------------------------------------------
+
+function KupuButton(buttonid, commandfunc, tool) {
+ /* Base prototype for kupu button tools */
+ if (arguments.length) {
+ this.buttonid = buttonid;
+ this.button = getFromSelector(buttonid);
+ this.commandfunc = commandfunc;
+ this.tool = tool;
+ this.disable = kupuButtonDisable;
+ this.enable = kupuButtonEnable;
+ };
+};
+
+KupuButton.prototype = new KupuTool;
+
+KupuButton.prototype.initialize = function(editor) {
+ this.editor = editor;
+ if (!this.button) return;
+ addEventHandler(this.button, 'click', this.execCommand, this);
+};
+
+KupuButton.prototype.execCommand = function() {
+ /* exec this button's command */
+ this.commandfunc(this, this.editor, this.tool);
+};
+
+KupuButton.prototype.updateState = function(selNode, event) {
+ /* override this in subclasses to determine whether a button should
+ look 'pressed in' or not
+ */
+};
+
+function KupuStateButton(buttonid, commandfunc, checkfunc, offclass, onclass) {
+ /* A button that can have two states (e.g. pressed and
+ not-pressed) based on CSS classes */
+ this.buttonid = buttonid;
+ this.button = getFromSelector(buttonid);
+ this.commandfunc = commandfunc;
+ this.checkfunc = checkfunc;
+ this.offclass = offclass;
+ this.onclass = onclass;
+ this.pressed = false;
+
+ this.execCommand = function() {
+ /* exec this button's command */
+ this.button.className = (this.pressed ? this.offclass : this.onclass);
+ this.pressed = !this.pressed;
+ this.editor.focusDocument();
+ this.commandfunc(this, this.editor);
+ };
+
+ this.updateState = function(selNode, event) {
+ /* check if we need to be clicked or unclicked, and update accordingly
+
+ if the state of the button should be changed, we set the class
+ */
+ if (!this.button) return;
+ var currclass = this.button.className;
+ var newclass = null;
+ if (this.checkfunc(selNode, this, this.editor, event)) {
+ newclass = this.onclass;
+ this.pressed = true;
+ } else {
+ newclass = this.offclass;
+ this.pressed = false;
+ };
+ if (currclass != newclass) {
+ this.button.className = newclass;
+ };
+ };
+};
+
+KupuStateButton.prototype = new KupuButton;
+
+/* Same as the state button, but the focusDocument call is delayed.
+ * Mozilla&Firefox have a bug on windows which can cause a crash if you
+ * change CSS positioning styles on an element which has focus.
+ */
+function KupuLateFocusStateButton(buttonid, commandfunc, checkfunc,
+ offclass, onclass) {
+ KupuStateButton.apply(this, [buttonid, commandfunc, checkfunc,
+ offclass, onclass]);
+}
+
+KupuLateFocusStateButton.prototype = new KupuStateButton;
+
+KupuLateFocusStateButton.prototype.execCommand = function() {
+ /* exec this button's command */
+ this.button.className = (this.pressed ? this.offclass : this.onclass);
+ this.pressed = !this.pressed;
+ this.commandfunc(this, this.editor);
+ this.editor.focusDocument();
+};
+
+function KupuRemoveElementButton(buttonid, element_name, cssclass) {
+ /* A button specialized in removing elements in the current node
+ context. Typical usages include removing links, images, etc. */
+ this.button = getFromSelector(buttonid);
+ this.element_name = element_name;
+ this.onclass = 'invisible';
+ this.offclass = cssclass;
+ this.pressed = false;
+};
+
+KupuRemoveElementButton.prototype = new KupuStateButton;
+
+KupuRemoveElementButton.prototype.commandfunc = function(button, editor) {
+ editor.focusDocument();
+ editor.removeNearestParentOfType(editor.getSelectedNode(), this.element_name);
+ editor.updateState();
+};
+
+KupuRemoveElementButton.prototype.checkfunc = function(currnode, button,
+ editor, event) {
+ var element = editor.getNearestParentOfType(currnode, this.element_name);
+ return (element ? false : true);
+};
+
+function KupuUI(textstyleselectid) {
+ /* View
+
+ This is the main view, which controls most of the toolbar buttons.
+ Even though this is probably never going to be removed from the view,
+ it was easier to implement this as a plain tool (plugin) as well.
+ */
+
+ // attributes
+ this.tsselect = getFromSelector(textstyleselectid);
+ this.paraoptions = [];
+ this.tableoptions = [];
+ this.styleoptions = [];
+ this.tableoffset = 0;
+ this.styleoffset = 0;
+ this.tablegrp = null;
+ this.optionstate = -1;
+ this.otherstyle = null;
+ this.tablestyles = {};
+ this.charstyles = {};
+ this.styles = {}; // use an object here so we can use the 'in' operator later on
+ this.blocktagre = /^(p|div|h.|ul|ol|dl|menu|dir|pre|blockquote|address|center)$/i;
+ this.spanre = /^span\b/i;
+ this.tblre = /^thead|tbody|table|t[rdh]\b/i;
+};
+
+
+KupuUI.prototype = new KupuTool;
+
+KupuUI.prototype.initialize = function(editor) {
+ /* initialize the ui like tools */
+ this.editor = editor;
+ this.cleanStyles();
+ this.enableOptions(false);
+ if (this.tsselect) {
+ this._selectevent = addEventHandler(this.tsselect, 'change', this.setTextStyleHandler, this);
+ }
+};
+
+KupuUI.prototype.getStyles = function() {
+ if (!this.paraoptions) {
+ this.cleanStyles();
+ }
+ return [ this.paraoptions, this.tableoptions ];
+};
+
+KupuUI.prototype.setTextStyleHandler = function(event) {
+ this.setTextStyle(this.tsselect.options[this.tsselect.selectedIndex].value);
+};
+
+// event handlers
+KupuUI.prototype.basicButtonHandler = function(action) {
+ /* event handler for basic actions (toolbar buttons) */
+ this.editor.execCommand(action);
+ this.editor.updateState();
+};
+
+KupuUI.prototype.saveButtonHandler = function() {
+ /* handler for the save button */
+ this.editor.saveDocument();
+};
+
+KupuUI.prototype.saveAndExitButtonHandler = function(redirect_url) {
+ /* save the document and, if successful, redirect */
+ this.editor.saveDocument(redirect_url);
+};
+
+KupuUI.prototype.cutButtonHandler = function() {
+ try {
+ this.editor.execCommand('Cut');
+ } catch (e) {
+ if (this.editor.getBrowserName() == 'Mozilla') {
+ alert(_('Cutting from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
+ } else {
+ throw e;
+ };
+ };
+ this.editor.updateState();
+};
+
+KupuUI.prototype.copyButtonHandler = function() {
+ try {
+ this.editor.execCommand('Copy');
+ } catch (e) {
+ if (this.editor.getBrowserName() == 'Mozilla') {
+ alert(_('Copying from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
+ } else {
+ throw e;
+ };
+ };
+ this.editor.updateState();
+};
+
+KupuUI.prototype.pasteButtonHandler = function() {
+ try {
+ this.editor.execCommand('Paste');
+ } catch (e) {
+ if (this.editor.getBrowserName() == 'Mozilla') {
+ alert(_('Pasting from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
+ } else {
+ throw e;
+ };
+ };
+ this.editor.updateState();
+};
+
+KupuUI.prototype.cleanStyles = function() {
+ if (!this.tsselect) return;
+ var options = this.tsselect.options;
+ var parastyles = this.styles;
+ var tablestyles = this.tablestyles;
+ var charstyles = this.charstyles;
+
+ var normal = ['Normal', 'p|'];
+ var td = ['Plain Cell', 'td|'];
+ var nostyle = ['(remove style)', ''];
+
+ var opts = [];
+ while (options.length) {
+ var opt = options[0];
+ options[0] = null;
+ var v = opt.value;
+ if (v.indexOf('|') > -1) {
+ var split = v.split('|');
+ v = split[0].toLowerCase() + "|" + split[1];
+ } else {
+ v = v.toLowerCase()+"|";
+ };
+ var optarray = [opt.text, v];
+ if (v=='td|') {
+ td = optarray;
+ } else if (v=='p|') {
+ normal = optarray;
+ } else if (v=='') {
+ nostyle = optarray;
+ } else {
+ opts.push([opt.text,v]);
+ }
+ }
+ this.tableoptions.push(td);
+ tablestyles[td[1]] = 0;
+ this.paraoptions.push(normal);
+ parastyles[normal[1]] = 0;
+
+ for (var i = 0; i < opts.length; i++) {
+ optarray = opts[i];
+ v = optarray[1];
+
+ if (this.spanre.test(v)) {
+ charstyles[v] = this.styleoptions.length;
+ this.styleoptions.push(optarray);
+ } else if (this.tblre.test(v)) {
+ tablestyles[v] = this.tableoptions.length;
+ this.tableoptions.push(optarray);
+ } else {
+ parastyles[v] = this.paraoptions.length;
+ this.paraoptions.push(optarray);
+ };
+ };
+ this.paraoptions.push(nostyle);
+ this.styleoffset = this.paraoptions.length;
+ this.tableoffset = this.styleoffset + this.styleoptions.length;
+};
+
+// Remove otherstyle and switch to appropriate style set.
+KupuUI.prototype.enableOptions = function(inTable) {
+ if (!this.tsselect) return;
+ var select = this.tsselect;
+ var options = select.options;
+ if (this.otherstyle) {
+ options[0] = null;
+ this.otherstyle = null;
+ }
+ if (this.optionstate == inTable) return; /* No change */
+
+ // while (select.firstChild) select.removeChild(select.firstChild);
+
+ function option(info) {
+ return newElement('option', {'value': info[1]}, [info[0]]);
+ }
+ if (this.optionstate==-1) {
+ for (var i = 0; i < this.paraoptions.length; i++) {
+ select.appendChild(option(this.paraoptions[i]));
+ }
+ if (this.styleoptions.length) {
+ var grp = document.createElement('optgroup');
+ grp.label = 'Character styles';
+ for (var i = 0; i < this.styleoptions.length; i++) {
+ grp.appendChild(option(this.styleoptions[i]));
+ }
+ select.appendChild(grp);
+ }
+ }
+ if (inTable) {
+ var grp = (this.tablegrp = document.createElement('optgroup'));
+ grp.label = 'Table elements';
+ for (var i = 0; i < this.tableoptions.length; i++) {
+ grp.appendChild(option(this.tableoptions[i]));
+ }
+ select.appendChild(grp);
+ } else {
+ while (select.options[this.tableoffset]) {
+ select.options[this.tableoffset] = null;
+ };
+ if (this.tablegrp) {
+ select.removeChild(this.tablegrp);
+ this.tablegrp = null;
+ };
+ };
+ this.optionstate = inTable;
+};
+
+KupuUI.prototype.setIndex = function(currnode, tag, index, styles) {
+ var className = currnode.className;
+ this.styletag = tag;
+ this.classname = className;
+ var style = tag+'|'+className;
+
+ if (style in styles) {
+ return styles[style];
+ } else if (!className && tag in styles) {
+ return styles[tag];
+ }
+ return index;
+};
+
+KupuUI.prototype.nodeStyle = function(node) {
+ var currnode = node;
+ var index = -1;
+ this.styletag = undefined;
+ this.classname = '';
+
+ // Set the table state correctly
+ this.intable = false;
+
+ while(currnode) {
+ var tag = currnode.nodeName;
+ if (/^body$/i.test(tag)) break;
+ if (this.tblre.test(tag)) {
+ this.intable = true;
+ break;
+ };
+ currnode = currnode.parentNode;
+ };
+ currnode = node;
+ while (currnode) {
+ var tag = currnode.nodeName.toLowerCase();
+
+ if (/^body$/.test(tag)) {
+ if (!this.styletag) {
+ // Forced style messes up in Firefox: return -1 to
+ // indicate no style
+ return -1;
+ }
+ break;
+ }
+ if (this.spanre.test(tag)) {
+ index = this.setIndex(currnode, tag, index, this.charstyles);
+ if (index >= 0) return index+this.styleoffset; // span takes priority
+ } else if (this.blocktagre.test(tag)) {
+ index = this.setIndex(currnode, tag, index, this.styles);
+ } else if (this.tblre.test(tag)) {
+ if (index > 0) return index; // block or span takes priority.
+ index = this.setIndex(currnode, tag, index, this.tablestyles);
+ if (index >= 0 || tag=='table') {
+ return index+this.tableoffset; // Stop processing if in a table
+ }
+ }
+ currnode = currnode.parentNode;
+ }
+ return index;
+};
+
+KupuUI.prototype.updateState = function(selNode) {
+ /* set the text-style pulldown */
+
+ // first get the nearest style
+ // search the list of nodes like in the original one, break if we encounter a match,
+ // this method does some more than the original one since it can handle commands in
+ // the form of '<style>|<classname>' next to the plain
+ // '<style>' commands
+ if (!this.tsselect) return;
+ var index = undefined;
+ var mixed = false;
+ var styletag, classname;
+
+ var selection = this.editor.getSelection();
+
+ for (var el=selNode.firstChild; el; el=el.nextSibling) {
+ if (el.nodeType==1 && selection.containsNode(el)) {
+ var i = this.nodeStyle(el);
+ if (index===undefined) {
+ index = i;
+ styletag = this.styletag;
+ classname = this.classname;
+ }
+ if (index != i || styletag!=this.styletag || classname != this.classname) {
+ mixed = true;
+ break;
+ }
+ }
+ };
+
+ if (index===undefined) {
+ index = this.nodeStyle(selNode);
+ }
+ this.enableOptions(this.intable);
+
+ if (index < 0 || mixed) {
+ if (mixed) {
+ var caption = 'Mixed styles';
+ } else if (this.styletag) {
+ var caption = 'Other: ' + this.styletag + ' '+ this.classname;
+ } else {
+ var caption = '<no style>';
+ }
+
+ var opt = newElement('option');
+ opt.text = caption;
+ this.otherstyle = opt;
+ this.tsselect.options.add(opt,0);
+ index = 0;
+ }
+ this.tsselect.selectedIndex = Math.max(index,0);
+};
+
+KupuUI.prototype._cleanNode = function(node, preserveEmpty) {
+ /* Clean up a block style node (e.g. P, DIV, Hn)
+ * Remove trailing whitespace, then also remove up to one
+ * trailing <br>
+ * If the node is now empty and no preserveEmpty, remove the node itself.
+ */
+ function stripspace() {
+ var c;
+ while ((c = node.lastChild) && c.nodeType==3 && (/^\s*$/.test(c.data))) {
+ node.removeChild(c);
+ }
+ }
+ stripspace();
+ var c = node.lastChild;
+ if (c && c.nodeType==1 && c.tagName=='BR') {
+ node.removeChild(c);
+ }
+ stripspace();
+ if (node.childNodes.length==0 && !preserveEmpty) {
+ node.parentNode.removeChild(node);
+ };
+};
+
+KupuUI.prototype._cleanCell = function(eltype, classname, strip) {
+ var alttype=eltype=='TD'?'TH':eltype=='TH'?'TD':null;
+
+ var selNode = this.editor.getSelectedNode(true);
+ var el = this.editor.getNearestParentOfType(selNode, eltype);
+ if (!el && alttype) {
+ // Maybe changing type
+ el = this.editor.getNearestParentOfType(selNode, alttype);
+ }
+
+ //either the selection is inside a cell, spans cells, or includes
+ //a collection of cells
+
+ //first, if contained in a cell
+
+ if (el) {
+ if (eltype != el.tagName) {
+ // Change node type.
+ var node = el.ownerDocument.createElement(eltype);
+ var parent = el.parentNode;
+ parent.insertBefore(node, el);
+ while (el.firstChild) {
+ node.appendChild(el.firstChild);
+ }
+ parent.removeChild(el);
+ el = node;
+ }
+ // now set the classname
+ this._setClass(el, classname);
+ if (strip && el.childNodes.length==1) {
+ var node = el.firstChild;
+ if (this.blocktagre.test(node.nodeName)) {
+ for (var n = node.firstChild; n;) {
+ var nxt = n.nextSibling;
+ el.insertBefore(n, node); // Move nodes out of block
+ n = nxt;
+ };
+ nxt = node.nextSibling;
+ el.removeChild(node);
+ node = nxt;
+ };
+ };
+ } else {
+ //otherwise, find all cells that intersect the selection
+ var selection = this.editor.getSelection();
+ var nodes = selNode.getElementsByTagName(eltype);
+
+ var cellNodes = [];
+ for (var i = 0; i < nodes.length; i++) {
+ cellNodes.push(nodes.item(i));
+ };
+ if (alttype) {
+ nodes = selNode.getElementsByTagName(alttype);
+ for (var i = 0; i < nodes.length; i++) {
+ cellNodes.push(nodes.item(i));
+ };
+ };
+
+ for (var i = 0; i < cellNodes.length; i++) {
+ el = cellNodes[i];
+
+ if(selection.intersectsNode(el)) {
+ if (eltype != el.tagName) {
+ // Change node type.
+ var node = el.ownerDocument.createElement(eltype);
+ var parent = el.parentNode;
+ parent.insertBefore(node, el);
+ while (el.firstChild) {
+ node.appendChild(el.firstChild);
+ };
+ parent.removeChild(el);
+ el = node;
+ };
+ this._setClass(el, classname);
+ }
+ }
+ }
+};
+
+KupuUI.prototype._setClass = function(el, classname) {
+ var parent = el.parentNode;
+ if (parent.tagName=='DIV') {
+ // fixup buggy formatting
+ var gp = parent.parentNode;
+ if (el != parent.firstChild) {
+ var previous = parent.cloneNode(false);
+ while (el != parent.firstChild) {
+ previous.appendChild(parent.firstChild);
+ }
+ gp.insertBefore(previous, parent);
+ this._cleanNode(previous);
+ }
+ gp.insertBefore(el, parent);
+ this._cleanNode(parent);
+ };
+ // now set the classname
+ if (classname) {
+ el.className = classname;
+ } else {
+ el.removeAttribute("class");
+ el.removeAttribute("className");
+ }
+};
+
+KupuUI.prototype._removeStyle = function() {
+ var self = this;
+ function needbreak(e) {
+ if (isblock && e) {
+ if (self.blocktagre.test(e.nodeName) || (/^br$/i.test(e.nodeName))) return;
+ parent.insertBefore(ed.newElement('br'), n);
+ }
+ }
+ var n = this.editor.getSelectedNode(true);
+ var ed = this.editor;
+ while(n) {
+ var tag = n.nodeName.toLowerCase();
+ var isblock = this.blocktagre.test(tag);
+ if (this.tblre.test(tag) && n.className) {
+ n.removeAttribute("class");
+ n.removeAttribute("className");
+ return;
+ }
+ if (isblock || tag == 'span') {
+ var parent = n.parentNode;
+ var el;
+ needbreak(n.previousSibling);
+ while ((el = n.firstChild)) {
+ parent.insertBefore(el, n);
+ }
+ needbreak(n.nextSibling);
+ parent.removeChild(n);
+ return;
+ }
+ n = n.parentNode;
+ };
+};
+
+KupuUI.prototype.setTextStyle = function(style, noupdate) {
+ /* parse the argument into a type and classname part
+ generate a block element accordingly
+ */
+ var classname = '';
+ var eltype = style.toUpperCase();
+ if (style.indexOf('|') > -1) {
+ style = style.split('|');
+ eltype = style[0].toUpperCase();
+ classname = style[1];
+ };
+
+ var doc = this.editor.getDocument();
+ var command = eltype;
+ // first create the element, then find it and set the classname
+ if (this.editor.getBrowserName() == 'IE') {
+ command = '<' + eltype + '>';
+ };
+ if (!style) {
+ this._removeStyle();
+ } else if (this.tblre.test(eltype)) {
+ this._cleanCell(eltype, classname);
+ } else if (eltype=='SPAN') {
+ doc.execCommand('removeformat', null);
+ if (this.editor.getBrowserName()=='IE') {
+ // removeformat is broken in IE: it doesn't remove span
+ // tags
+ var selNode = this.editor.getSelectedNode();
+ var selection = this.editor.getSelection();
+ var elements = selNode.getElementsByTagName('span');
+ for (var i = 0; i < elements.length; i++) {
+ var span = elements[i];
+ if (selection.containsNode(span)) {
+ var parent = span.parentNode;
+ while (span.firstChild) {
+ parent.insertBefore(span.firstChild, span);
+ };
+ parent.removeChild(span);
+ };
+ };
+ }
+ if (classname) {
+ doc.execCommand('fontsize', '2');
+ // Now convert font tags to spans
+ var inner = doc.getDocument();
+ var elements = inner.getElementsByTagName('FONT');
+ while (elements.length > 0) {
+ var font = elements[0];
+ var span = inner.createElement('SPAN');
+ span.className = classname;
+ var parent = font.parentNode;
+ parent.replaceChild(span, font);
+ while (font.firstChild) {
+ span.appendChild(font.firstChild);
+ };
+ };
+ };
+ }
+ else {
+ doc.execCommand('formatblock', command);
+
+ // now get a reference to the element just added
+ var selNode = this.editor.getSelectedNode(true);
+ var el = this.editor.getNearestParentOfType(selNode, eltype);
+ if (el) {
+ this._setClass(el, classname);
+ } else {
+ var selection = this.editor.getSelection();
+ var elements = selNode.getElementsByTagName(eltype);
+ for (var i = 0; i < elements.length; i++) {
+ el = elements[i];
+ if (selection.containsNode(el)) {
+ this._setClass(el, classname);
+ }
+ }
+ }
+ }
+ if (el) {
+ this.editor.getSelection().selectNodeContents(el);
+ }
+ if (!noupdate) {
+ this.editor.updateState();
+ }
+};
+
+KupuUI.prototype.createContextMenuElements = function(selNode, event) {
+ var ret = [];
+ ret.push(new ContextMenuElement(_('Cut'),
+ this.cutButtonHandler, this));
+ ret.push(new ContextMenuElement(_('Copy'),
+ this.copyButtonHandler, this));
+ ret.push(new ContextMenuElement(_('Paste'),
+ this.pasteButtonHandler, this));
+ return ret;
+};
+
+KupuUI.prototype.disable = function() {
+ if (this.tsselect) this.tsselect.disabled = "disabled";
+};
+
+KupuUI.prototype.enable = function() {
+ if (this.tsselect) this.tsselect.disabled = "";
+};
+
+function ColorchooserTool(fgcolorbuttonid, hlcolorbuttonid, colorchooserid) {
+ /* the colorchooser */
+
+ this.fgcolorbutton = getFromSelector(fgcolorbuttonid);
+ this.hlcolorbutton = getFromSelector(hlcolorbuttonid);
+ this.ccwindow = getFromSelector(colorchooserid);
+ this.command = null;
+}
+
+ColorchooserTool.prototype = new KupuTool;
+
+ColorchooserTool.prototype.initialize = function(editor) {
+ /* attach the event handlers */
+ this.editor = editor;
+ if (!(this.fgcolorbutton && this.hlcolorbutton && this.ccwindow)) return;
+ this.createColorchooser(this.ccwindow);
+
+ addEventHandler(this.fgcolorbutton, "click", this.openFgColorChooser, this);
+ addEventHandler(this.hlcolorbutton, "click", this.openHlColorChooser, this);
+ addEventHandler(this.ccwindow, "click", this.chooseColor, this);
+ this.hide();
+};
+
+ColorchooserTool.prototype.updateState = function(selNode) {
+ /* update state of the colorchooser */
+ this.hide();
+};
+
+ColorchooserTool.prototype.openFgColorChooser = function() {
+ /* event handler for opening the colorchooser */
+ this.command = "forecolor";
+ this.show();
+};
+
+ColorchooserTool.prototype.openHlColorChooser = function() {
+ /* event handler for closing the colorchooser */
+ if (this.editor.getBrowserName() == "IE") {
+ this.command = "backcolor";
+ } else {
+ this.command = "hilitecolor";
+ }
+ this.show();
+};
+
+ColorchooserTool.prototype.chooseColor = function(event) {
+ /* event handler for choosing the color */
+ var target = _SARISSA_IS_MOZ ? event.target : event.srcElement;
+ var cell = this.editor.getNearestParentOfType(target, 'td');
+ var ed = this.editor;
+ var doc = ed.getDocument();
+ ed.execCommand('styleWithCSS', true);
+ doc.execCommand(this.command, cell.bgColor);
+ ed.execCommand('styleWithCSS', false);
+ // this.editor.execCommand(this.command, cell.bgColor);
+ this.hide();
+
+ this.editor.logMessage(_('Color chosen'));
+};
+
+ColorchooserTool.prototype.show = function(command) {
+ /* show the colorchooser */
+ this.ccwindow.style.display = "block";
+};
+
+ColorchooserTool.prototype.hide = function() {
+ /* hide the colorchooser */
+ this.ccwindow.style.display = "none";
+};
+
+ColorchooserTool.prototype.createColorchooser = function(table) {
+ /* create the colorchooser table */
+
+ var chunks = ['00', '33', '66', '99', 'CC', 'FF'];
+ table.setAttribute('id', 'kupu-colorchooser-table');
+ table.style.borderWidth = '2px';
+ table.style.borderStyle = 'solid';
+ table.style.position = 'absolute';
+ table.style.cursor = 'default';
+ table.style.display = 'none';
+
+ var tbody = document.createElement('tbody');
+
+ for (var i=0; i < 6; i++) {
+ var tr = document.createElement('tr');
+ var r = chunks[i];
+ for (var j=0; j < 6; j++) {
+ var g = chunks[j];
+ for (var k=0; k < 6; k++) {
+ var b = chunks[k];
+ var color = '#' + r + g + b;
+ var td = document.createElement('td');
+ td.setAttribute('bgColor', color);
+ td.style.backgroundColor = color;
+ td.style.borderWidth = '1px';
+ td.style.borderStyle = 'solid';
+ td.style.fontSize = '1px';
+ td.style.width = '10px';
+ td.style.height = '10px';
+ var text = document.createTextNode('\u00a0');
+ td.appendChild(text);
+ tr.appendChild(td);
+ }
+ }
+ tbody.appendChild(tr);
+ }
+ table.appendChild(tbody);
+
+ return table;
+};
+
+ColorchooserTool.prototype.enable = function() {
+ kupuButtonEnable(this.fgcolorbutton);
+ kupuButtonEnable(this.hlcolorbutton);
+};
+
+ColorchooserTool.prototype.disable = function() {
+ kupuButtonDisable(this.fgcolorbutton);
+ kupuButtonDisable(this.hlcolorbutton);
+};
+
+function PropertyTool(titlefieldid, descfieldid) {
+ /* The property tool */
+
+ this.titlefield = getFromSelector(titlefieldid);
+ this.descfield = getFromSelector(descfieldid);
+};
+
+PropertyTool.prototype = new KupuTool;
+
+PropertyTool.prototype.initialize = function(editor) {
+ /* attach the event handlers and set the initial values */
+ this.editor = editor;
+ addEventHandler(this.titlefield, "change", this.updateProperties, this);
+ addEventHandler(this.descfield, "change", this.updateProperties, this);
+
+ // set the fields
+ var heads = this.editor.getInnerDocument().getElementsByTagName('head');
+ if (!heads[0]) {
+ this.editor.logMessage(_('No head in document!'), 1);
+ } else {
+ var head = heads[0];
+ var titles = head.getElementsByTagName('title');
+ if (titles.length) {
+ this.titlefield.value = titles[0].text;
+ }
+ var metas = head.getElementsByTagName('meta');
+ if (metas.length) {
+ for (var i=0; i < metas.length; i++) {
+ var meta = metas[i];
+ if (meta.getAttribute('name') &&
+ meta.getAttribute('name').toLowerCase() ==
+ 'description') {
+ this.descfield.value = meta.getAttribute('content');
+ break;
+ }
+ }
+ }
+ }
+};
+
+PropertyTool.prototype.updateProperties = function() {
+ /* event handler for updating the properties form */
+ var doc = this.editor.getInnerDocument();
+ var heads = doc.getElementsByTagName('HEAD');
+ if (!heads) {
+ this.editor.logMessage(_('No head in document!'), 1);
+ return;
+ }
+
+ var head = heads[0];
+
+ // set the title
+ var titles = head.getElementsByTagName('title');
+ if (!titles) {
+ var title = doc.createElement('title');
+ var text = doc.createTextNode(this.titlefield.value);
+ title.appendChild(text);
+ head.appendChild(title);
+ } else {
+ var title = titles[0];
+ // IE6 title has no children, and refuses appendChild.
+ // Delete and recreate the title.
+ if (title.childNodes.length == 0) {
+ title.removeNode(true);
+ title = doc.createElement('title');
+ title.innerText = this.titlefield.value;
+ head.appendChild(title);
+ } else {
+ title.childNodes[0].nodeValue = this.titlefield.value;
+ }
+ }
+ document.title = this.titlefield.value;
+
+ // let's just fulfill the usecase, not think about more properties
+ // set the description
+ var metas = doc.getElementsByTagName('meta');
+ var descset = 0;
+ for (var i=0; i < metas.length; i++) {
+ var meta = metas[i];
+ if (meta.getAttribute('name') &&
+ meta.getAttribute('name').toLowerCase() == 'description') {
+ meta.setAttribute('content', this.descfield.value);
+ descset = 1;
+ }
+ }
+
+ if (!descset) {
+ var meta = doc.createElement('meta');
+ meta.setAttribute('name', 'description');
+ meta.setAttribute('content', this.descfield.value);
+ head.appendChild(meta);
+ }
+
+ this.editor.logMessage(_('Properties modified'));
+};
+
+function LinkTool(popupurl, popupwidth, popupheight, popupprops) {
+ /* Add and update hyperlinks */
+ this.popupurl = popupurl || 'kupupopups/link.html';
+ this.popupwidth = popupwidth || 300;
+ this.popupheight = popupheight || 200;
+ this.popupprops = popupprops || '';
+}
+
+LinkTool.prototype = new KupuTool;
+
+LinkTool.prototype.initialize = function(editor) {
+ this.editor = editor;
+};
+
+LinkTool.prototype.createLinkHandler = function(event) {
+ /* create a link according to a url entered in a popup */
+ var linkWindow = openPopup(this.popupurl, this.popupwidth,
+ this.popupheight, this.popupprops);
+ linkWindow.linktool = this;
+ linkWindow.focus();
+};
+
+LinkTool.prototype.updateLink = function (linkel, url, type, name, target, title, className, bForce) {
+ if (type && type == 'anchor') {
+ linkel.removeAttribute('href');
+ linkel.setAttribute('name', name);
+ } else {
+ linkel.href = url;
+ if (linkel.innerHTML == "" || (bForce && linkel.innerHTML==url)) {
+ var doc = this.editor.getInnerDocument();
+ while (linkel.firstChild) { linkel.removeChild(linkel.firstChild); };
+ linkel.appendChild(doc.createTextNode(title || url));
+ }
+ if (title) {
+ linkel.title = title;
+ } else {
+ linkel.removeAttribute('title');
+ }
+ if (target) {
+ linkel.setAttribute('target', target);
+ }
+ else {
+ linkel.removeAttribute('target');
+ };
+ if (className===undefined) {
+ linkel.removeAttribute('className');
+ } else {
+ linkel.className = className;
+ }
+ linkel.style.color = this.linkcolor;
+ };
+};
+
+LinkTool.prototype.formatSelectedLink = function(url, type, name, target, title, className, bForce) {
+ var currnode = this.editor.getSelectedNode();
+
+ // selection inside link
+ var linkel = this.editor.getNearestParentOfType(currnode, 'A');
+ if (linkel) {
+ this.updateLink(linkel, url, type, name, target, title, className, bForce);
+ return true;
+ }
+
+ if (currnode.nodeType!=1) return false;
+
+ // selection contains links
+ var linkelements = currnode.getElementsByTagName('A');
+ var selection = this.editor.getSelection();
+ var containsLink = false;
+ for (var i = 0; i < linkelements.length; i++) {
+ linkel = linkelements[i];
+ if (selection.containsNode(linkel)) {
+ this.updateLink(linkel, url, type, name, target, title, className, bForce);
+ containsLink = true;
+ }
+ };
+ return containsLink;
+};
+
+// Can create a link in the following circumstances:
+// The selection is inside a link:
+// just update the link attributes.
+// The selection contains links:
+// update the attributes of the contained links
+// No links inside or outside the selection:
+// create a link around the selection
+// No selection:
+// insert a link containing the title
+//
+// the order of the arguments is a bit odd here because of backward
+// compatibility
+LinkTool.prototype.createLink = function(url, type, name, target, title, className) {
+ url = url.strip();
+ if (!url) {
+ this.deleteLink();
+ return;
+ };
+ if (!this.formatSelectedLink(url, type, name, target, title, className)) {
+ // No links inside or outside.
+ this.editor.execCommand("CreateLink", url);
+ if (!this.formatSelectedLink(url, type, name, target, title, className, true)) {
+ // Insert link with no text selected, insert the title
+ // or URI instead.
+ var doc = this.editor.getInnerDocument();
+ var linkel = doc.createElement("a");
+ linkel.setAttribute('href', url);
+ linkel.setAttribute('class', className || 'generated');
+ this.editor.getSelection().replaceWithNode(linkel, true);
+ this.updateLink(linkel, url, type, name, target, title, className);
+ };
+ }
+};
+
+LinkTool.prototype.deleteLink = function() {
+ /* delete the current link */
+ var currnode = this.editor.getSelectedNode();
+ var linkel = this.editor.getNearestParentOfType(currnode, 'a');
+ if (!linkel) {
+ this.editor.logMessage(_('Not inside link'));
+ return;
+ };
+ while (linkel.childNodes.length) {
+ linkel.parentNode.insertBefore(linkel.childNodes[0], linkel);
+ };
+ linkel.parentNode.removeChild(linkel);
+};
+
+LinkTool.prototype.createContextMenuElements = function(selNode, event) {
+ /* create the 'Create link' or 'Remove link' menu elements */
+ var ret = [];
+ var link = this.editor.getNearestParentOfType(selNode, 'a');
+ if (link) {
+ ret.push(new ContextMenuElement(_('Delete link'), this.deleteLink, this));
+ } else {
+ ret.push(new ContextMenuElement(_('Create link'), this.createLinkHandler, this));
+ };
+ return ret;
+};
+
+function LinkToolBox(inputid, buttonid, toolboxid, plainclass, activeclass) {
+ /* create and edit links */
+
+ this.input = getFromSelector(inputid);
+ this.button = getFromSelector(buttonid);
+ this.toolboxel = getFromSelector(toolboxid);
+ this.plainclass = plainclass;
+ this.activeclass = activeclass;
+};
+
+LinkToolBox.prototype = new LinkToolBox;
+
+LinkToolBox.prototype.initialize = function(tool, editor) {
+ /* attach the event handlers */
+ this.tool = tool;
+ this.editor = editor;
+ if (!this.button) return;
+ addEventHandler(this.input, "blur", this.updateLink, this);
+ addEventHandler(this.button, "click", this.addLink, this);
+};
+
+LinkToolBox.prototype.updateState = function(selNode) {
+ /* if we're inside a link, update the input, else empty it */
+ var linkel = this.editor.getNearestParentOfType(selNode, 'a');
+ if (linkel) {
+ // check first before setting a class for backward compatibility
+ if (this.toolboxel) {
+ this.toolboxel.className = this.activeclass;
+ };
+ this.input.value = linkel.getAttribute('href');
+ } else {
+ // check first before setting a class for backward compatibility
+ if (this.toolboxel) {
+ this.toolboxel.className = this.plainclass;
+ };
+ this.input.value = '';
+ }
+};
+
+LinkToolBox.prototype.addLink = function(event) {
+ /* add a link */
+ var url = this.input.value;
+ this.editor.focusDocument();
+ this.tool.createLink(url);
+ this.editor.updateState();
+};
+
+LinkToolBox.prototype.updateLink = function() {
+ /* update the current link */
+ var currnode = this.editor.getSelectedNode();
+ var linkel = this.editor.getNearestParentOfType(currnode, 'A');
+ if (!linkel) {
+ return;
+ }
+
+ var url = this.input.value;
+ linkel.setAttribute('href', url);
+
+ this.editor.updateState();
+};
+
+function ImageTool(popupurl, popupwidth, popupheight, popupprops) {
+ /* Image tool to add images */
+ this.popupurl = popupurl || 'kupupopups/image.html';
+ this.popupwidth = popupwidth || 300;
+ this.popupheight = popupheight || 200;
+ this.popupprops = popupprops || '';
+};
+
+ImageTool.prototype = new KupuTool;
+
+ImageTool.prototype.initialize = function(editor) {
+ /* attach the event handlers */
+ this.editor = editor;
+};
+
+ImageTool.prototype.createImageHandler = function(event) {
+ /* create an image according to a url entered in a popup */
+ var imageWindow = openPopup(this.popupurl, this.popupwidth,
+ this.popupheight, this.popupprops);
+ imageWindow.imagetool = this;
+ imageWindow.focus();
+};
+
+ImageTool.prototype.newNode = function(name, obj) {
+ var ed = this.editor;
+ var currobj = ed.getNearestParentOfType(ed.getSelectedNode(), name);
+ if (currobj) {
+ var p = currobj.parentNode;
+ p.insertBefore(obj, currobj);
+ p.removeChild(currobj);
+ return obj;
+ } else {
+ return ed.insertNodeAtSelection(obj, 1);
+ }
+};
+
+ImageTool.prototype.createImage = function(url, alttext, imgclass) {
+ /* create an image */
+ var img = this.editor.getInnerDocument().createElement('img');
+ img.src = url;
+ img.setAttribute('kupu-src', url);
+ img.removeAttribute('height');
+ img.removeAttribute('width');
+ if (alttext) {
+ img.alt = alttext;
+ };
+ if (imgclass) {
+ img.className = imgclass;
+ };
+ this.newNode('IMG', img);
+ return img;
+};
+
+ImageTool.prototype.create_flash = function(url, alttext, className, width, height) {
+ var ed = this.editor;
+ var obj = ed.newElement('object',
+ {src:url, alt:alttext, className:className, width:width,
+ height:height, type:'application/x-shockwave-flash',
+ 'data':url},
+ [ed.newElement('param', {name:'movie', value:url})]);
+ this.newNode('OBJECT', obj);
+};
+
+ImageTool.prototype.setImageClass = function(imgclass) {
+ /* set the class of the selected image */
+ var currnode = this.editor.getSelectedNode();
+ var currimg = this.editor.getNearestParentOfType(currnode, 'IMG');
+ if (currimg) {
+ currimg.className = imgclass;
+ };
+};
+
+ImageTool.prototype.createContextMenuElements = function(selNode, event) {
+ return [new ContextMenuElement(_('Create image'), this.createImageHandler,
+ this)];
+};
+
+function ImageToolBox(inputfieldid, insertbuttonid, classselectid, toolboxid,
+ plainclass, activeclass) {
+ /* toolbox for adding images */
+
+ this.inputfield = getFromSelector(inputfieldid);
+ this.insertbutton = getFromSelector(insertbuttonid);
+ this.classselect = getFromSelector(classselectid);
+ this.toolboxel = getFromSelector(toolboxid);
+ this.plainclass = plainclass;
+ this.activeclass = activeclass;
+};
+
+ImageToolBox.prototype = new KupuToolBox;
+
+ImageToolBox.prototype.initialize = function(tool, editor) {
+ this.tool = tool;
+ this.editor = editor;
+ addEventHandler(this.classselect, "change", this.setImageClass, this);
+ addEventHandler(this.insertbutton, "click", this.addImage, this);
+};
+
+ImageToolBox.prototype.updateState = function(selNode, event) {
+ /* update the state of the toolbox element */
+ var imageel = this.editor.getNearestParentOfType(selNode, 'img');
+ if (imageel) {
+ // check first before setting a class for backward compatibility
+ if (this.toolboxel) {
+ this.toolboxel.className = this.activeclass;
+ this.inputfield.value = imageel.getAttribute('src');
+ var imgclass = imageel.className ? imageel.className : 'image-inline';
+ selectSelectItem(this.classselect, imgclass);
+ };
+ } else {
+ if (this.toolboxel) {
+ this.toolboxel.className = this.plainclass;
+ };
+ };
+};
+
+ImageToolBox.prototype.addImage = function() {
+ /* add an image */
+ var url = this.inputfield.value;
+ var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
+ this.editor.focusDocument();
+ this.tool.createImage(url, null, sel_class);
+ this.editor.updateState();
+};
+
+ImageToolBox.prototype.setImageClass = function() {
+ /* set the class for the current image */
+ var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
+ this.editor.focusDocument();
+ this.tool.setImageClass(sel_class);
+ this.editor.updateState();
+};
+
+function TableTool() {
+ /* The table tool */
+};
+
+TableTool.prototype = new KupuTool;
+
+// XXX There are some awfully long methods in here!!
+TableTool.prototype.createContextMenuElements = function(selNode, event) {
+ var table = this.editor.getNearestParentOfType(selNode, 'table');
+ if (!table) {
+ var ret = [];
+ var el = new ContextMenuElement(_('Add table'), this.addPlainTable, this);
+ ret.push(el);
+ return ret;
+ } else {
+ var ret = [];
+ ret.push(new ContextMenuElement(_('Add row'), this.addTableRow, this));
+ ret.push(new ContextMenuElement(_('Delete row'), this.delTableRow, this));
+ ret.push(new ContextMenuElement(_('Add column'), this.addTableColumn, this));
+ ret.push(new ContextMenuElement(_('Delete column'), this.delTableColumn, this));
+ ret.push(new ContextMenuElement(_('Delete Table'), this.delTable, this));
+ return ret;
+ };
+};
+
+TableTool.prototype.addPlainTable = function() {
+ /* event handler for the context menu */
+ this.createTable(2, 3, 1, 'plain');
+};
+
+TableTool.prototype.createTable = function(rows, cols, makeHeader, tableclass) {
+ /* add a table */
+ if (rows < 1 || rows > 99 || cols < 1 || cols > 99) {
+ this.editor.logMessage(_('Invalid table size'), 1);
+ return;
+ };
+
+ var doc = this.editor.getInnerDocument();
+ var table = doc.createElement("table");
+ table.className = tableclass;
+
+ // If the user wants a row of headings, make them
+ if (makeHeader) {
+ var tr = doc.createElement("tr");
+ var thead = doc.createElement("thead");
+ for (var i=1; i <= cols; i++) {
+ var th = doc.createElement("th");
+ th.appendChild(doc.createTextNode("Col " + i));
+ tr.appendChild(th);
+ }
+ thead.appendChild(tr);
+ table.appendChild(thead);
+ }
+
+ var tbody = doc.createElement("tbody");
+ for (var i=0; i < rows; i++) {
+ var tr = doc.createElement("tr");
+ for (var j=0; j < cols; j++) {
+ var td = doc.createElement("td");
+ var content = doc.createTextNode('\u00a0');
+ td.appendChild(content);
+ tr.appendChild(td);
+ }
+ tbody.appendChild(tr);
+ }
+ table.appendChild(tbody);
+ this.editor.insertNodeAtSelection(table);
+
+ this._setTableCellHandlers(table);
+ return table;
+};
+
+TableTool.prototype._setTableCellHandlers = function(table) {
+ // make each cell select its full contents if it's clicked
+ addEventHandler(table, 'click', this._selectContentIfEmpty, this);
+
+ var cells = table.getElementsByTagName('td');
+ for (var i=0; i < cells.length; i++) {
+ addEventHandler(cells[i], 'click', this._selectContentIfEmpty, this);
+ };
+
+ // select the nbsp in the first cell
+ var firstcell = cells[0];
+ if (firstcell) {
+ var children = firstcell.childNodes;
+ if (children.length == 1 && children[0].nodeType == 3 &&
+ children[0].nodeValue == '\xa0') {
+ var selection = this.editor.getSelection();
+ selection.selectNodeContents(firstcell);
+ };
+ };
+};
+
+TableTool.prototype._selectContentIfEmpty = function() {
+ var selNode = this.editor.getSelectedNode();
+ var cell = this.editor.getNearestParentOfType(selNode, 'td');
+ if (!cell) {
+ return;
+ };
+ var children = cell.childNodes;
+ if (children.length == 1 && children[0].nodeType == 3 &&
+ children[0].nodeValue == '\xa0') {
+ var selection = this.editor.getSelection();
+ selection.selectNodeContents(cell);
+ };
+};
+
+TableTool.prototype.addTableRow = function() {
+ /* Find the current row and add a row after it */
+ var currnode = this.editor.getSelectedNode();
+ var currtbody = this.editor.getNearestParentOfType(currnode, "TBODY");
+ var bodytype = "tbody";
+ if (!currtbody) {
+ currtbody = this.editor.getNearestParentOfType(currnode, "THEAD");
+ bodytype = "thead";
+ }
+ var parentrow = this.editor.getNearestParentOfType(currnode, "TR");
+ var nextrow = parentrow.nextSibling;
+
+ // get the number of cells we should place
+ var colcount = 0;
+ for (var i=0; i < currtbody.childNodes.length; i++) {
+ var el = currtbody.childNodes[i];
+ if (el.nodeType != 1) {
+ continue;
+ }
+ if (el.nodeName.toLowerCase() == 'tr') {
+ var cols = 0;
+ for (var j=0; j < el.childNodes.length; j++) {
+ if (el.childNodes[j].nodeType == 1) {
+ cols++;
+ }
+ }
+ if (cols > colcount) {
+ colcount = cols;
+ }
+ }
+ }
+
+ var newrow = this.editor.getInnerDocument().createElement("TR");
+
+ for (var i = 0; i < colcount; i++) {
+ var newcell;
+ if (bodytype == 'tbody') {
+ newcell = this.editor.getInnerDocument().createElement("TD");
+ } else {
+ newcell = this.editor.getInnerDocument().createElement("TH");
+ }
+ var newcellvalue = this.editor.getInnerDocument().createTextNode("\u00a0");
+ newcell.appendChild(newcellvalue);
+ newrow.appendChild(newcell);
+ }
+
+ if (!nextrow) {
+ currtbody.appendChild(newrow);
+ } else {
+ currtbody.insertBefore(newrow, nextrow);
+ }
+};
+
+TableTool.prototype.delTableRow = function() {
+ /* Find the current row and delete it */
+ var currnode = this.editor.getSelectedNode();
+ var parentrow = this.editor.getNearestParentOfType(currnode, "TR");
+ if (!parentrow) {
+ this.editor.logMessage(_('No row to delete'), 1);
+ return;
+ }
+
+ // move selection aside
+ // XXX: doesn't work if parentrow is the only row of thead/tbody/tfoot
+ // XXX: doesn't preserve the colindex
+ var selection = this.editor.getSelection();
+ if (parentrow.nextSibling) {
+ selection.selectNodeContents(parentrow.nextSibling.firstChild);
+ } else if (parentrow.previousSibling) {
+ selection.selectNodeContents(parentrow.previousSibling.firstChild);
+ };
+
+ // remove the row
+ parentrow.parentNode.removeChild(parentrow);
+};
+
+TableTool.prototype.addTableColumn = function() {
+ /* Add a new column after the current column */
+ var currnode = this.editor.getSelectedNode();
+ var currtd = this.editor.getNearestParentOfType(currnode, 'TD');
+ if (!currtd) {
+ currtd = this.editor.getNearestParentOfType(currnode, 'TH');
+ }
+ if (!currtd) {
+ this.editor.logMessage(_('No parentcolumn found!'), 1);
+ return;
+ }
+ var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
+
+ // get the current index
+ var tdindex = this._getColIndex(currtd);
+
+ // now add a column to all rows
+ // first the thead
+ var theads = currtable.getElementsByTagName('THEAD');
+ if (theads) {
+ for (var i=0; i < theads.length; i++) {
+ // let's assume table heads only have ths
+ var currthead = theads[i];
+ for (var j=0; j < currthead.childNodes.length; j++) {
+ var tr = currthead.childNodes[j];
+ if (tr.nodeType != 1) {
+ continue;
+ }
+ var currindex = 0;
+ for (var k=0; k < tr.childNodes.length; k++) {
+ var th = tr.childNodes[k];
+ if (th.nodeType != 1) {
+ continue;
+ }
+ if (currindex == tdindex) {
+ var doc = this.editor.getInnerDocument();
+ var newth = doc.createElement('th');
+ var text = doc.createTextNode('\u00a0');
+ newth.appendChild(text);
+ if (tr.childNodes.length == k+1) {
+ // the column will be on the end of the row
+ tr.appendChild(newth);
+ } else {
+ tr.insertBefore(newth, tr.childNodes[k + 1]);
+ }
+ break;
+ }
+ currindex++;
+ }
+ }
+ }
+ }
+
+ // then the tbody
+ var tbodies = currtable.getElementsByTagName('TBODY');
+ if (tbodies) {
+ for (var i=0; i < tbodies.length; i++) {
+ // let's assume table heads only have ths
+ var currtbody = tbodies[i];
+ for (var j=0; j < currtbody.childNodes.length; j++) {
+ var tr = currtbody.childNodes[j];
+ if (tr.nodeType != 1) {
+ continue;
+ }
+ var currindex = 0;
+ for (var k=0; k < tr.childNodes.length; k++) {
+ var td = tr.childNodes[k];
+ if (td.nodeType != 1) {
+ continue;
+ }
+ if (currindex == tdindex) {
+ var doc = this.editor.getInnerDocument();
+ var newtd = doc.createElement('td');
+ var text = doc.createTextNode('\u00a0');
+ newtd.appendChild(text);
+ if (tr.childNodes.length == k+1) {
+ // the column will be on the end of the row
+ tr.appendChild(newtd);
+ } else {
+ tr.insertBefore(newtd, tr.childNodes[k + 1]);
+ }
+ break;
+ }
+ currindex++;
+ }
+ }
+ }
+ }
+};
+
+TableTool.prototype.delTableColumn = function() {
+ /* remove a column */
+ var currnode = this.editor.getSelectedNode();
+ var currtd = this.editor.getNearestParentOfType(currnode, 'TD');
+ if (!currtd) {
+ currtd = this.editor.getNearestParentOfType(currnode, 'TH');
+ }
+ var currcolindex = this._getColIndex(currtd);
+ var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
+
+ // move selection aside
+ var selection = this.editor.getSelection();
+ if (currtd.nextSibling) {
+ selection.selectNodeContents(currtd.nextSibling);
+ } else if (currtd.previousSibling) {
+ selection.selectNodeContents(currtd.previousSibling);
+ };
+
+ // remove the theaders
+ var heads = currtable.getElementsByTagName('THEAD');
+ if (heads.length) {
+ for (var i=0; i < heads.length; i++) {
+ var thead = heads[i];
+ for (var j=0; j < thead.childNodes.length; j++) {
+ var tr = thead.childNodes[j];
+ if (tr.nodeType != 1) {
+ continue;
+ }
+ var currindex = 0;
+ for (var k=0; k < tr.childNodes.length; k++) {
+ var th = tr.childNodes[k];
+ if (th.nodeType != 1) {
+ continue;
+ }
+ if (currindex == currcolindex) {
+ tr.removeChild(th);
+ break;
+ }
+ currindex++;
+ }
+ }
+ }
+ }
+
+ // now we remove the column field, a bit harder since we need to take
+ // colspan and rowspan into account XXX Not right, fix theads as well
+ var bodies = currtable.getElementsByTagName('TBODY');
+ for (var i=0; i < bodies.length; i++) {
+ var currtbody = bodies[i];
+ for (var j=0; j < currtbody.childNodes.length; j++) {
+ var tr = currtbody.childNodes[j];
+ if (tr.nodeType != 1) {
+ continue;
+ }
+ var currindex = 0;
+ for (var k=0; k < tr.childNodes.length; k++) {
+ var cell = tr.childNodes[k];
+ if (cell.nodeType != 1) {
+ continue;
+ }
+ if (currindex == currcolindex) {
+ tr.removeChild(cell);
+ break;
+ }
+ currindex++;
+ }
+ }
+ }
+};
+
+TableTool.prototype.delTable = function() {
+ /* delete the current table */
+ var currnode = this.editor.getSelectedNode();
+ var table = this.editor.getNearestParentOfType(currnode, 'table');
+ if (!table) {
+ this.editor.logMessage(_('Not inside a table!'));
+ return;
+ };
+ table.parentNode.removeChild(table);
+};
+
+TableTool.prototype.setColumnAlign = function(newalign) {
+ /* change the alignment of a full column */
+ var currnode = this.editor.getSelectedNode();
+ var currtd = this.editor.getNearestParentOfType(currnode, "TD");
+ var bodytype = 'tbody';
+ if (!currtd) {
+ currtd = this.editor.getNearestParentOfType(currnode, "TH");
+ bodytype = 'thead';
+ }
+ var currcolindex = this._getColIndex(currtd);
+ var currtable = this.editor.getNearestParentOfType(currnode, "TABLE");
+
+ // unfortunately this is not enough to make the browsers display
+ // the align, we need to set it on individual cells as well and
+ // mind the rowspan...
+ for (var i=0; i < currtable.childNodes.length; i++) {
+ var currtbody = currtable.childNodes[i];
+ if (currtbody.nodeType != 1 ||
+ (/^thead|tbody$/i.test(currtbody.nodeName))) {
+ continue;
+ }
+ for (var j=0; j < currtbody.childNodes.length; j++) {
+ var row = currtbody.childNodes[j];
+ if (row.nodeType != 1) {
+ continue;
+ }
+ var index = 0;
+ for (var k=0; k < row.childNodes.length; k++) {
+ var cell = row.childNodes[k];
+ if (cell.nodeType != 1) {
+ continue;
+ }
+ if (index == currcolindex) {
+ if (this.editor.config.use_css) {
+ cell.style.textAlign = newalign;
+ } else {
+ cell.setAttribute('align', newalign);
+ }
+ cell.className = 'align-' + newalign;
+ }
+ index++;
+ }
+ }
+ }
+};
+
+TableTool.prototype.setTableClass = function(sel_class) {
+ /* set the class for the table */
+ var currnode = this.editor.getSelectedNode();
+ var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
+
+ if (currtable) {
+ currtable.className = sel_class;
+ }
+};
+
+TableTool.prototype._getColIndex = function(currcell) {
+ /* Given a node, return an integer for which column it is */
+ var prevsib = currcell.previousSibling;
+ var currcolindex = 0;
+ while (prevsib) {
+ if (prevsib.nodeType == 1 &&
+ (prevsib.tagName.toUpperCase() == "TD" ||
+ prevsib.tagName.toUpperCase() == "TH")) {
+ var colspan = prevsib.colSpan;
+ if (colspan) {
+ currcolindex += parseInt(colspan);
+ } else {
+ currcolindex++;
+ }
+ }
+ prevsib = prevsib.previousSibling;
+ if (currcolindex > 30) {
+ alert("Recursion detected when counting column position");
+ return;
+ }
+ }
+
+ return currcolindex;
+};
+
+TableTool.prototype._getColumnAlign = function(selNode) {
+ /* return the alignment setting of the current column */
+ var align;
+ var td = this.editor.getNearestParentOfType(selNode, 'td');
+ if (!td) {
+ td = this.editor.getNearestParentOfType(selNode, 'th');
+ };
+ if (td) {
+ align = td.getAttribute('align');
+ if (this.editor.config.use_css) {
+ align = td.style.textAlign;
+ };
+ };
+ return align;
+};
+
+TableTool.prototype.fixTable = function(event) {
+ /* fix the table so it can be processed by Kupu */
+ // since this can be quite a nasty creature we can't just use the
+ // helper methods
+
+ // first we create a new tbody element
+ var currnode = this.editor.getSelectedNode();
+ var table = this.editor.getNearestParentOfType(currnode, 'TABLE');
+ if (!table) {
+ this.editor.logMessage(_('Not inside a table!'));
+ return;
+ };
+ this._fixTableHelper(table);
+};
+
+TableTool.prototype._isBodyRow = function(row) {
+ for (var node = row.firstChild; node; node=node.nextSibling) {
+ if (/^td$/i.test(node.nodeName)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+TableTool.prototype._cleanCell = function(el) {
+ // Remove formatted div or p from a cell
+ var nxt, n;
+ for (var node = el.firstChild; node;) {
+ if (/^div|p$/i.test(node.nodeName)) {
+ for (var n = node.firstChild; n;) {
+ var nxt = n.nextSibling;
+ el.insertBefore(n, node); // Move nodes out of div
+ n = nxt;
+ }
+ nxt = node.nextSibling;
+ el.removeChild(node);
+ node = nxt;
+ } else {
+ node = node.nextSibling;
+ }
+ }
+ var c;
+ while (el.firstChild && (c = el.firstChild).nodeType==3 && (/^\s+/.test(c.data))) {
+ c.data = c.data.replace(/^\s+/, '');
+ if (!c.data) {
+ el.removeChild(c);
+ } else {
+ break;
+ };
+ };
+ while (el.lastChild && (c = el.lastChild).nodeType==3 && (/\s+$/.test(c.data))) {
+ c.data = c.data.replace(/\s+$/, '');
+ if (!c.data) {
+ el.removeChild(c);
+ } else {
+ break;
+ };
+ };
+ el.removeAttribute('colSpan');
+ el.removeAttribute('rowSpan');
+};
+
+TableTool.prototype._countCols = function(rows, numcols) {
+ for (var i=0; i < rows.length; i++) {
+ var row = rows[i];
+ var currnumcols = 0;
+ for (var node = row.firstChild; node; node=node.nextSibling) {
+ if (/^(td|th)$/i.test(node.nodeName)) {
+ currnumcols += parseInt(node.getAttribute('colSpan') || '1');
+ };
+ };
+ if (currnumcols > numcols) {
+ numcols = currnumcols;
+ };
+ };
+ return numcols;
+};
+
+TableTool.prototype._cleanRows = function(rows, container, numcols) {
+ // now walk through all rows to clean them up
+ for (var i=0; i < rows.length; i++) {
+ var row = rows[i];
+ var doc = this.editor.getInnerDocument();
+ var newrow = doc.createElement('tr');
+ if (row.className) {
+ newrow.className = row.className;
+ }
+ for (var node = row.firstChild; node;) {
+ var nxt = node.nextSibling;
+ if (/^(td|th)$/i.test(node.nodeName)) {
+ this._cleanCell(node);
+ newrow.appendChild(node);
+ };
+ node = nxt;
+ };
+ if (newrow.childNodes.length) {
+ container.appendChild(newrow);
+ };
+ };
+ // now make sure all rows have the correct length
+ for (var row = container.firstChild; row; row=row.nextSibling) {
+ var cellname = row.lastChild.nodeName;
+ while (row.childNodes.length < numcols) {
+ var cell = doc.createElement(cellname);
+ var nbsp = doc.createTextNode('\u00a0');
+ cell.appendChild(nbsp);
+ row.appendChild(cell);
+ };
+ };
+};
+
+TableTool.prototype._fixTableHelper = function(table) {
+ /* the code to actually fix tables */
+ var doc = this.editor.getInnerDocument();
+ var thead = doc.createElement('thead');
+ var tbody = doc.createElement('tbody');
+ var tfoot = doc.createElement('tfoot');
+
+ var table_classes = this.editor.config.table_classes;
+ function cleanClassName(name) {
+ var allowed_classes = table_classes['class'];
+ for (var i = 0; i < allowed_classes.length; i++) {
+ var classname = allowed_classes[i];
+ classname = classname.classname || classname;
+ if (classname==name) return name;
+ };
+ return allowed_classes[0];
+ }
+ if (table_classes) {
+ table.className = cleanClassName(table.className);
+ } else {
+ table.removeAttribute('class');
+ table.removeAttribute('className');
+ };
+ table.removeAttribute('border');
+ table.removeAttribute('cellpadding');
+ table.removeAttribute('cellPadding');
+ table.removeAttribute('cellspacing');
+ table.removeAttribute('cellSpacing');
+
+ // now get all the rows of the table, the rows can either be
+ // direct descendants of the table or inside a 'tbody', 'thead'
+ // or 'tfoot' element
+
+ var hrows = [], brows = [], frows = [];
+ for (var node = table.firstChild; node; node = node.nextSibling) {
+ var nodeName = node.nodeName.toLowerCase();
+ if (/tr/i.test(node.nodeName)) {
+ brows.push(node);
+ } else if (/thead|tbody|tfoot/i.test(node.nodeName)) {
+ var rows = nodeName=='thead' ? hrows : nodeName=='tfoot' ? frows : brows;
+ for (var inode = node.firstChild; inode; inode = inode.nextSibling) {
+ if (/tr/i.test(inode.nodeName)) {
+ rows.push(inode);
+ };
+ };
+ };
+ };
+ /* Extract thead and tfoot from tbody */
+ while (brows.length && !this._isBodyRow(brows[0])) {
+ hrows.push(brows[0]);
+ brows.shift();
+ }
+ while (brows.length && !this._isBodyRow(brows[brows.length-1])) {
+ var last = brows[brows.length-1];
+ brows.length -= 1;
+ frows.unshift(last);
+ }
+ // now find out how many cells our rows should have
+ var numcols = this._countCols(hrows, 0);
+ numcols = this._countCols(brows, numcols);
+ numcols = this._countCols(frows, numcols);
+
+ // now walk through all rows to clean them up
+ this._cleanRows(hrows, thead);
+ this._cleanRows(brows, tbody);
+ this._cleanRows(frows, tfoot);
+
+ // now remove all the old stuff from the table and add the new
+ // tbody
+ while (table.firstChild) {
+ table.removeChild(table.firstChild);
+ }
+ if (hrows.length) {
+ table.appendChild(thead);
+ }
+ if (brows.length) {
+ table.appendChild(tbody);
+ }
+ if (frows.length) {
+ table.appendChild(tfoot);
+ }
+};
+
+TableTool.prototype.fixAllTables = function() {
+ /* fix all the tables in the document at once */
+ var tables = this.editor.getInnerDocument().getElementsByTagName('table');
+ for (var i=0; i < tables.length; i++) {
+ this._fixTableHelper(tables[i]);
+ };
+};
+
+function TableToolBox(addtabledivid, edittabledivid, newrowsinputid,
+ newcolsinputid, makeheaderinputid, classselectid, alignselectid, addtablebuttonid,
+ addrowbuttonid, delrowbuttonid, addcolbuttonid, delcolbuttonid, fixbuttonid,
+ delbuttonid, fixallbuttonid, toolboxid, plainclass, activeclass) {
+ /* The table tool */
+
+ // a lot of dependencies on html elements here, but most implementations
+ // will use them all I guess
+ this.addtablediv = getFromSelector(addtabledivid);
+ this.edittablediv = getFromSelector(edittabledivid);
+ this.newrowsinput = getFromSelector(newrowsinputid);
+ this.newcolsinput = getFromSelector(newcolsinputid);
+ this.makeheaderinput = getFromSelector(makeheaderinputid);
+ this.classselect = getFromSelector(classselectid);
+ this.alignselect = getFromSelector(alignselectid);
+ this.addtablebutton = getFromSelector(addtablebuttonid);
+ this.addrowbutton = getFromSelector(addrowbuttonid);
+ this.delrowbutton = getFromSelector(delrowbuttonid);
+ this.addcolbutton = getFromSelector(addcolbuttonid);
+ this.delcolbutton = getFromSelector(delcolbuttonid);
+ this.fixbutton = getFromSelector(fixbuttonid);
+ this.delbutton = getFromSelector(delbuttonid);
+ this.fixallbutton = getFromSelector(fixallbuttonid);
+ this.toolboxel = getFromSelector(toolboxid);
+ this.plainclass = plainclass;
+ this.activeclass = activeclass;
+};
+
+TableToolBox.prototype = new KupuToolBox;
+
+// register event handlers
+TableToolBox.prototype.initialize = function(tool, editor) {
+ /* attach the event handlers */
+ this.tool = tool;
+ this.editor = editor;
+ // build the select list of table classes if configured
+ if (this.editor.config.table_classes) {
+ var classes = this.editor.config.table_classes['class'];
+ while (this.classselect.hasChildNodes()) {
+ this.classselect.removeChild(this.classselect.firstChild);
+ };
+ for (var i=0; i < classes.length; i++) {
+ var classname = classes[i];
+ classname = classname.classname || classname;
+ var option = document.createElement('option');
+ var content = document.createTextNode(classname);
+ option.appendChild(content);
+ option.setAttribute('value', classname);
+ this.classselect.appendChild(option);
+ };
+ };
+ addEventHandler(this.addtablebutton, "click", this.addTable, this);
+ addEventHandler(this.addrowbutton, "click", this.addTableRow, this);
+ addEventHandler(this.delrowbutton, "click", this.delTableRow, this);
+ addEventHandler(this.addcolbutton, "click", this.addTableColumn, this);
+ addEventHandler(this.delcolbutton, "click", this.delTableColumn, this);
+ addEventHandler(this.alignselect, "change", this.setColumnAlign, this);
+ addEventHandler(this.classselect, "change", this.setTableClass, this);
+ addEventHandler(this.fixbutton, "click", this.fixTable, this);
+ addEventHandler(this.delbutton, "click", this.delTable, this);
+ addEventHandler(this.fixallbutton, "click", this.fixAllTables, this);
+ this.addtablediv.style.display = "block";
+ this.edittablediv.style.display = "none";
+};
+
+TableToolBox.prototype.updateState = function(selNode) {
+ /* update the state (add/edit) and update the pulldowns (if required) */
+ var table = this.editor.getNearestParentOfType(selNode, 'table');
+ if (table) {
+ this.addtablediv.style.display = "none";
+ this.edittablediv.style.display = "block";
+
+ var align = this.tool._getColumnAlign(selNode);
+ selectSelectItem(this.alignselect, align);
+ selectSelectItem(this.classselect, table.className);
+ if (this.toolboxel) {
+ this.toolboxel.className = this.activeclass;
+ };
+ } else {
+ this.edittablediv.style.display = "none";
+ this.addtablediv.style.display = "block";
+ this.alignselect.selectedIndex = 0;
+ this.classselect.selectedIndex = 0;
+ if (this.toolboxel) {
+ this.toolboxel.className = this.plainclass;
+ };
+ };
+};
+
+TableToolBox.prototype.addTable = function() {
+ /* add a table */
+ var rows = this.newrowsinput.value;
+ var cols = this.newcolsinput.value;
+ var makeHeader = this.makeheaderinput.checked;
+ var tableclass = this.classselect.options[this.classselect.selectedIndex].value;
+
+ this.tool.createTable(rows, cols, makeHeader, tableclass);
+ this.editor.focusDocument();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.setColumnAlign = function() {
+ /* set the alignment of the current column */
+ var newalign = this.alignselect.options[this.alignselect.selectedIndex].value;
+ this.editor.focusDocument();
+ this.tool.setColumnAlign(newalign);
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.setTableClass = function() {
+ /* set the class for the current table */
+ var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
+ if (sel_class) {
+ this.editor.focusDocument();
+ this.tool.setTableClass(sel_class);
+ this.editor.updateState();
+ };
+};
+
+TableToolBox.prototype.addTableRow = function() {
+ this.editor.focusDocument();
+ this.tool.addTableRow();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.delTableRow = function() {
+ this.editor.focusDocument();
+ this.tool.delTableRow();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.addTableColumn = function() {
+ this.editor.focusDocument();
+ this.tool.addTableColumn();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.delTableColumn = function() {
+ this.editor.focusDocument();
+ this.tool.delTableColumn();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.fixTable = function() {
+ this.editor.focusDocument();
+ this.tool.fixTable();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.fixAllTables = function() {
+ this.editor.focusDocument();
+ this.tool.fixAllTables();
+ this.editor.updateState();
+};
+
+TableToolBox.prototype.delTable = function() {
+ this.editor.focusDocument();
+ this.tool.delTable();
+ this.editor.updateState();
+};
+
+function ListTool(addulbuttonid, addolbuttonid, ulstyleselectid, olstyleselectid) {
+ /* tool to set list styles */
+
+ this.addulbutton = getFromSelector(addulbuttonid);
+ this.addolbutton = getFromSelector(addolbuttonid);
+ this.ulselect = getFromSelector(ulstyleselectid);
+ this.olselect = getFromSelector(olstyleselectid);
+
+ this.style_to_type = {'decimal': '1',
+ 'lower-alpha': 'a',
+ 'upper-alpha': 'A',
+ 'lower-roman': 'i',
+ 'upper-roman': 'I',
+ 'disc': 'disc',
+ 'square': 'square',
+ 'circle': 'circle',
+ 'none': 'none'
+ };
+ this.type_to_style = {'1': 'decimal',
+ 'a': 'lower-alpha',
+ 'A': 'upper-alpha',
+ 'i': 'lower-roman',
+ 'I': 'upper-roman',
+ 'disc': 'disc',
+ 'square': 'square',
+ 'circle': 'circle',
+ 'none': 'none'
+ };
+};
+
+ListTool.prototype = new KupuTool;
+
+ListTool.prototype.initialize = function(editor) {
+ /* attach event handlers */
+ this.editor = editor;
+ if (this.addulbutton) {
+ addEventHandler(this.addulbutton, "click", this.addUnorderedList, this);
+ }
+ if (this.addolbutton) {
+ addEventHandler(this.addolbutton, "click", this.addOrderedList, this);
+ }
+ if (this.ulselect) {
+ addEventHandler(this.ulselect, "change", this.setUnorderedListStyle, this);
+ this.ulselect.style.display = "none";
+ }
+ if (this.olselect) {
+ addEventHandler(this.olselect, "change", this.setOrderedListStyle, this);
+ this.olselect.style.display = "none";
+ }
+};
+
+ListTool.prototype._handleStyles = function(currnode, onselect, offselect) {
+ if (this.editor.config.use_css) {
+ var currstyle = currnode.style.listStyleType;
+ } else {
+ var currstyle = this.type_to_style[currnode.getAttribute('type')];
+ }
+ if (onselect) {
+ selectSelectItem(onselect, currstyle);
+ onselect.style.display = "inline";
+ }
+ if (offselect) {
+ offselect.style.display = "none";
+ offselect.selectedIndex = 0;
+ }
+};
+
+ListTool.prototype.updateState = function(selNode) {
+ /* update the visibility and selection of the list type pulldowns */
+ // we're going to walk through the tree manually since we want to
+ // check on 2 items at the same time
+ for (var currnode=selNode; currnode; currnode=currnode.parentNode) {
+ var tag = currnode.nodeName.toLowerCase();
+ if (tag == 'ul') {
+ this._handleStyles(currnode, this.ulselect, this.olselect);
+ return;
+ } else if (tag == 'ol') {
+ this._handleStyles(currnode, this.olselect, this.ulselect);
+ return;
+ }
+ }
+ if (this.ulselect) {
+ this.ulselect.selectedIndex = 0;
+ this.ulselect.style.display = "none";
+ };
+ if (this.olselect) {
+ this.olselect.selectedIndex = 0;
+ this.olselect.style.display = "none";
+ };
+};
+
+ListTool.prototype.addList = function(command) {
+ if (this.ulselect) this.ulselect.style.display = "inline";
+ if (this.olselect) this.olselect.style.display = "none";
+ this.editor.execCommand(command);
+ this.editor.focusDocument();
+};
+
+ListTool.prototype.addUnorderedList = function() {
+ /* add an unordered list */
+ this.addList("insertunorderedlist");
+};
+
+ListTool.prototype.addOrderedList = function() {
+ /* add an ordered list */
+ this.addList("insertorderedlist");
+};
+
+ListTool.prototype.setListStyle = function(tag, select) {
+ /* set the type of an ul */
+ if (!select) return;
+ var currnode = this.editor.getSelectedNode();
+ var l = this.editor.getNearestParentOfType(currnode, tag);
+ var style = select.options[select.selectedIndex].value;
+ if (this.editor.config.use_css) {
+ l.style.listStyleType = style;
+ } else {
+ l.setAttribute('type', this.style_to_type[style]);
+ }
+ this.editor.focusDocument();
+};
+
+ListTool.prototype.setUnorderedListStyle = function() {
+ /* set the type of an ul */
+ this.setListStyle('ul', this.ulselect);
+};
+
+ListTool.prototype.setOrderedListStyle = function() {
+ /* set the type of an ol */
+ this.setListStyle('ol', this.olselect);
+};
+
+ListTool.prototype.enable = function() {
+ kupuButtonEnable(this.addulbutton);
+ kupuButtonEnable(this.addolbutton);
+ if (this.ulselect) this.ulselect.disabled = "";
+ if (this.olselect) this.olselect.disabled = "";
+};
+
+ListTool.prototype.disable = function() {
+ kupuButtonDisable(this.addulbutton);
+ kupuButtonDisable(this.addolbutton);
+ if (this.ulselect) this.ulselect.disabled = "disabled";
+ if (this.olselect) this.olselect.disabled = "disabled";
+};
+
+function ShowPathTool() {
+ /* shows the path to the current element in the status bar */
+};
+
+ShowPathTool.prototype = new KupuTool;
+
+ShowPathTool.prototype.updateState = function(selNode) {
+ /* calculate and display the path */
+ var path = '';
+ var url = null; // for links we want to display the url too
+ var currnode = selNode;
+ var nn;
+ while (currnode != null && (nn=currnode.nodeName.toLowerCase()) != '#document') {
+ if (nn=='a') {
+ url = currnode.getAttribute('href');
+ };
+ path = '/' + nn + path;
+ currnode = currnode.parentNode;
+ }
+
+ try {
+ window.status = url ?
+ (path.toString() + ' - contains link to \'' +
+ url.toString() + '\'') :
+ path;
+ } catch (e) {
+ this.editor.logMessage(_('Could not set status bar message, ' +
+ 'check your browser\'s security settings.'), 1);
+ };
+};
+
+function ViewSourceTool() {
+ /* tool to provide a 'show source' context menu option */
+ this.sourceWindow = null;
+};
+
+ViewSourceTool.prototype = new KupuTool;
+
+ViewSourceTool.prototype.viewSource = function() {
+ /* open a window and write the current contents of the iframe to it */
+ if (this.sourceWindow) {
+ this.sourceWindow.close();
+ };
+ this.sourceWindow = window.open('#', 'sourceWindow');
+
+ //var transform = this.editor._filterContent(this.editor.getInnerDocument().documentElement);
+ //var contents = transform.xml;
+ var contents = '<html>\n' + this.editor.getInnerDocument().documentElement.innerHTML + '\n</html>';
+
+ var doc = this.sourceWindow.document;
+ doc.write('\xa0');
+ doc.close();
+ var body = doc.getElementsByTagName("body")[0];
+ while (body.hasChildNodes()) {
+ body.removeChild(body.firstChild);
+ };
+ var pre = doc.createElement('pre');
+ var textNode = doc.createTextNode(contents);
+ body.appendChild(pre);
+ pre.appendChild(textNode);
+};
+
+ViewSourceTool.prototype.createContextMenuElements = function(selNode, event) {
+ /* create the context menu element */
+ return [new ContextMenuElement(_('View source'), this.viewSource, this)];
+};
+
+function DefinitionListTool(dlbuttonid) {
+ /* a tool for managing definition lists
+
+ the dl elements should behave much like plain lists, and the keypress
+ behaviour should be similar
+ */
+
+ this.dlbutton = getFromSelector(dlbuttonid);
+};
+
+DefinitionListTool.prototype = new KupuTool;
+
+DefinitionListTool.prototype.initialize = function(editor) {
+ /* initialize the tool */
+ this.editor = editor;
+ if (!this.dlbutton) return;
+ addEventHandler(this.dlbutton, 'click', this.createDefinitionList, this);
+ addEventHandler(editor.getInnerDocument(), 'keyup', this._keyDownHandler, this);
+ addEventHandler(editor.getInnerDocument(), 'keypress', this._keyPressHandler, this);
+};
+
+// even though the following methods may seem view related, they belong
+// here, since they describe core functionality rather then view-specific
+// stuff
+DefinitionListTool.prototype.handleEnterPress = function(selNode) {
+ var dl = this.editor.getNearestParentOfType(selNode, 'dl');
+ if (dl) {
+ var dt = this.editor.getNearestParentOfType(selNode, 'dt');
+ if (dt) {
+ if (dt.childNodes.length == 1 && dt.childNodes[0].nodeValue == '\xa0') {
+ this.escapeFromDefinitionList(dl, dt, selNode);
+ return;
+ };
+
+ var selection = this.editor.getSelection();
+ var startoffset = selection.startOffset();
+ var endoffset = selection.endOffset();
+ if (endoffset > startoffset) {
+ // throw away any selected stuff
+ selection.cutChunk(startoffset, endoffset);
+ selection = this.editor.getSelection();
+ startoffset = selection.startOffset();
+ };
+
+ var ellength = selection.getElementLength(selection.parentElement());
+ if (startoffset >= ellength - 1) {
+ // create a new element
+ this.createDefinition(dl, dt);
+ } else {
+ var doc = this.editor.getInnerDocument();
+ var newdt = selection.splitNodeAtSelection(dt);
+ var newdd = doc.createElement('dd');
+ while (newdt.hasChildNodes()) {
+ if (newdt.firstChild != newdt.lastChild || newdt.firstChild.nodeName.toLowerCase() != 'br') {
+ newdd.appendChild(newdt.firstChild);
+ };
+ };
+ newdt.parentNode.replaceChild(newdd, newdt);
+ selection.selectNodeContents(newdd);
+ selection.collapse();
+ };
+ } else {
+ var dd = this.editor.getNearestParentOfType(selNode, 'dd');
+ if (!dd) {
+ this.editor.logMessage(_('Not inside a definition list element!'));
+ return;
+ };
+ if (dd.childNodes.length == 1 && dd.childNodes[0].nodeValue == '\xa0') {
+ this.escapeFromDefinitionList(dl, dd, selNode);
+ return;
+ };
+ var selection = this.editor.getSelection();
+ var startoffset = selection.startOffset();
+ var endoffset = selection.endOffset();
+ if (endoffset > startoffset) {
+ // throw away any selected stuff
+ selection.cutChunk(startoffset, endoffset);
[... 473 lines stripped ...]