Annotation of kupu/common/kupubasetools.js, revision 1.1.1.1

1.1       dwinter     1: /*****************************************************************************
                      2:  *
                      3:  * Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
                      4:  *
                      5:  * This software is distributed under the terms of the Kupu
                      6:  * License. See LICENSE.txt for license text. For a list of Kupu
                      7:  * Contributors see CREDITS.txt.
                      8:  *
                      9:  *****************************************************************************/
                     10: 
                     11: // table auskommentiert dirk wintergruen 2005-08-30
                     12: // $Id: kupubasetools.js 14575 2005-07-12 20:18:12Z duncan $
                     13: 
                     14: 
                     15: //----------------------------------------------------------------------------
                     16: //
                     17: // Toolboxes
                     18: //
                     19: //  These are addons for Kupu, simple plugins that implement a certain 
                     20: //  interface to provide functionality and control view aspects.
                     21: //
                     22: //----------------------------------------------------------------------------
                     23: 
                     24: //----------------------------------------------------------------------------
                     25: // Superclasses
                     26: //----------------------------------------------------------------------------
                     27: 
                     28: function KupuTool() {
                     29:     /* Superclass (or actually more of an interface) for tools 
                     30:     
                     31:         Tools must implement at least an initialize method and an 
                     32:         updateState method, and can implement other methods to add 
                     33:         certain extra functionality (e.g. createContextMenuElements).
                     34:     */
                     35: 
                     36:     this.toolboxes = {};
                     37: 
                     38:     // methods
                     39:     this.initialize = function(editor) {
                     40:         /* Initialize the tool.
                     41: 
                     42:             Obviously this can be overriden but it will do
                     43:             for the most simple cases
                     44:         */
                     45:         this.editor = editor;
                     46:     };
                     47: 
                     48:     this.registerToolBox = function(id, toolbox) {
                     49:         /* register a ui box 
                     50:         
                     51:             Note that this needs to be called *after* the tool has been 
                     52:             registered to the KupuEditor
                     53:         */
                     54:         this.toolboxes[id] = toolbox;
                     55:         toolbox.initialize(this, this.editor);
                     56:     };
                     57:     
                     58:     this.updateState = function(selNode, event) {
                     59:         /* Is called when user moves cursor to other element 
                     60: 
                     61:             Calls the updateState for all toolboxes and may want perform
                     62:             some actions itself
                     63:         */
                     64:         for (id in this.toolboxes) {
                     65:             this.toolboxes[id].updateState(selNode, event);
                     66:         };
                     67:     };
                     68: 
                     69:     this.enable = function() {
                     70:         // Called when the tool is enabled after a form is dismissed.
                     71:     }
                     72: 
                     73:     this.disable = function() {
                     74:         // Called when the tool is disabled (e.g. for a modal form)
                     75:     }
                     76:     // private methods
                     77:     addEventHandler = addEventHandler;
                     78:     
                     79:     this._selectSelectItem = function(select, item) {
                     80:         this.editor.logMessage(_('Deprecation warning: KupuTool._selectSelectItem'));
                     81:     };
                     82:     this._fixTabIndex = function(element) {
                     83:         var tabIndex = this.editor.getDocument().getEditable().tabIndex-1;
                     84:         if (tabIndex && !element.tabIndex) {
                     85:             element.tabIndex = tabIndex;
                     86:         }
                     87:     }
                     88: }
                     89: 
                     90: function KupuToolBox() {
                     91:     /* Superclass for a user-interface object that controls a tool */
                     92: 
                     93:     this.initialize = function(tool, editor) {
                     94:         /* store a reference to the tool and the editor */
                     95:         this.tool = tool;
                     96:         this.editor = editor;
                     97:     };
                     98: 
                     99:     this.updateState = function(selNode, event) {
                    100:         /* update the toolbox according to the current iframe's situation */
                    101:     };
                    102:     
                    103:     this._selectSelectItem = function(select, item) {
                    104:         this.editor.logMessage(_('Deprecation warning: KupuToolBox._selectSelectItem'));
                    105:     };
                    106: };
                    107: 
                    108: function NoContextMenu(object) {
                    109:     /* Decorator for a tool to suppress the context menu */
                    110:     object.createContextMenuElements = function(selNode, event) {
                    111:         return [];
                    112:     }
                    113:     return object;
                    114: }
                    115: 
                    116: // Helper function for enabling/disabling tools
                    117: function KupuButtonDisable(button) {
                    118:     button = button || this.button;
                    119:     button.disabled = "disabled";
                    120:     button.className += ' disabled';
                    121: }
                    122: function KupuButtonEnable(button) {
                    123:     button = button || this.button;
                    124:     button.disabled = "";
                    125:     button.className = button.className.replace(/ *\bdisabled\b/g, '');
                    126: }
                    127: 
                    128: 
                    129: //----------------------------------------------------------------------------
                    130: // Implementations
                    131: //----------------------------------------------------------------------------
                    132: 
                    133: function KupuButton(buttonid, commandfunc, tool) {
                    134:     /* Base prototype for kupu button tools */
                    135:     this.buttonid = buttonid;
                    136:     this.button = getFromSelector(buttonid);
                    137:     this.commandfunc = commandfunc;
                    138:     this.tool = tool;
                    139: 
                    140:     this.initialize = function(editor) {
                    141:         this.editor = editor;
                    142:         this._fixTabIndex(this.button);
                    143:         addEventHandler(this.button, 'click', this.execCommand, this);
                    144:     };
                    145: 
                    146:     this.execCommand = function() {
                    147:         /* exec this button's command */
                    148:         this.commandfunc(this, this.editor, this.tool);
                    149:     };
                    150: 
                    151:     this.updateState = function(selNode, event) {
                    152:         /* override this in subclasses to determine whether a button should
                    153:             look 'pressed in' or not
                    154:         */
                    155:     };
                    156:     this.disable = KupuButtonDisable;
                    157:     this.enable = KupuButtonEnable;
                    158: };
                    159: 
                    160: KupuButton.prototype = new KupuTool;
                    161: function KupuStateButton(buttonid, commandfunc, checkfunc, offclass, onclass) {
                    162:     /* A button that can have two states (e.g. pressed and
                    163:        not-pressed) based on CSS classes */
                    164:     this.buttonid = buttonid;
                    165:     this.button = getFromSelector(buttonid);
                    166:     this.commandfunc = commandfunc;
                    167:     this.checkfunc = checkfunc;
                    168:     this.offclass = offclass;
                    169:     this.onclass = onclass;
                    170:     this.pressed = false;
                    171: 
                    172:     this.execCommand = function() {
                    173:         /* exec this button's command */
                    174:         this.button.className = (this.pressed ? this.offclass : this.onclass);
                    175:         this.pressed = !this.pressed;
                    176:         this.editor.focusDocument();
                    177:         this.commandfunc(this, this.editor);
                    178:     };
                    179: 
                    180:     this.updateState = function(selNode, event) {
                    181:         /* check if we need to be clicked or unclicked, and update accordingly 
                    182:         
                    183:             if the state of the button should be changed, we set the class
                    184:         */
                    185:         var currclass = this.button.className;
                    186:         var newclass = null;
                    187:         if (this.checkfunc(selNode, this, this.editor, event)) {
                    188:             newclass = this.onclass;
                    189:             this.pressed = true;
                    190:         } else {
                    191:             newclass = this.offclass;
                    192:             this.pressed = false;
                    193:         };
                    194:         if (currclass != newclass) {
                    195:             this.button.className = newclass;
                    196:         };
                    197:     };
                    198: };
                    199: 
                    200: KupuStateButton.prototype = new KupuButton;
                    201: 
                    202: /* Same as the state button, but the focusDocument call is delayed.
                    203:  * Mozilla&Firefox have a bug on windows which can cause a crash if you
                    204:  * change CSS positioning styles on an element which has focus.
                    205:  */
                    206: function KupuLateFocusStateButton(buttonid, commandfunc, checkfunc, offclass, onclass) {
                    207:     KupuStateButton.apply(this, [buttonid, commandfunc, checkfunc, offclass, onclass]);
                    208:     this.execCommand = function() {
                    209:         /* exec this button's command */
                    210:         this.button.className = (this.pressed ? this.offclass : this.onclass);
                    211:         this.pressed = !this.pressed;
                    212:         this.commandfunc(this, this.editor);
                    213:         this.editor.focusDocument();
                    214:     };
                    215: }
                    216: KupuLateFocusStateButton.prototype = new KupuStateButton;
                    217: 
                    218: function KupuRemoveElementButton(buttonid, element_name, cssclass) {
                    219:     /* A button specialized in removing elements in the current node
                    220:        context. Typical usages include removing links, images, etc. */
                    221:     this.button = getFromSelector(buttonid);
                    222:     this.onclass = 'invisible';
                    223:     this.offclass = cssclass;
                    224:     this.pressed = false;
                    225: 
                    226:     this.commandfunc = function(button, editor) {
                    227:         editor.removeNearestParentOfType(editor.getSelectedNode(), element_name);
                    228:     };
                    229: 
                    230:     this.checkfunc = function(currnode, button, editor, event) {
                    231:         var element = editor.getNearestParentOfType(currnode, element_name);
                    232:         return (element ? false : true);
                    233:     };
                    234: };
                    235: 
                    236: KupuRemoveElementButton.prototype = new KupuStateButton;
                    237: 
                    238: function KupuUI(textstyleselectid) {
                    239:     /* View 
                    240:     
                    241:         This is the main view, which controls most of the toolbar buttons.
                    242:         Even though this is probably never going to be removed from the view,
                    243:         it was easier to implement this as a plain tool (plugin) as well.
                    244:     */
                    245:     
                    246:     // attributes
                    247:     this.tsselect = getFromSelector(textstyleselectid);
                    248: 
                    249:     this.initialize = function(editor) {
                    250:         /* initialize the ui like tools */
                    251:         this.editor = editor;
                    252:         this._fixTabIndex(this.tsselect);
                    253:         this._selectevent = addEventHandler(this.tsselect, 'change', this.setTextStyleHandler, this);
                    254:     };
                    255: 
                    256:     this.setTextStyleHandler = function(event) {
                    257:         this.setTextStyle(this.tsselect.options[this.tsselect.selectedIndex].value);
                    258:     };
                    259:     
                    260:     // event handlers
                    261:     this.basicButtonHandler = function(action) {
                    262:         /* event handler for basic actions (toolbar buttons) */
                    263:         this.editor.execCommand(action);
                    264:         this.editor.updateState();
                    265:     };
                    266: 
                    267:     this.saveButtonHandler = function() {
                    268:         /* handler for the save button */
                    269:         this.editor.saveDocument();
                    270:     };
                    271: 
                    272:     this.saveAndExitButtonHandler = function(redirect_url) {
                    273:         /* save the document and, if successful, redirect */
                    274:         this.editor.saveDocument(redirect_url);
                    275:     };
                    276: 
                    277:     this.cutButtonHandler = function() {
                    278:         try {
                    279:             this.editor.execCommand('Cut');
                    280:         } catch (e) {
                    281:             if (this.editor.getBrowserName() == 'Mozilla') {
                    282:                 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'));
                    283:             } else {
                    284:                 throw e;
                    285:             };
                    286:         };
                    287:         this.editor.updateState();
                    288:     };
                    289: 
                    290:     this.copyButtonHandler = function() {
                    291:         try {
                    292:             this.editor.execCommand('Copy');
                    293:         } catch (e) {
                    294:             if (this.editor.getBrowserName() == 'Mozilla') {
                    295:                 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'));
                    296:             } else {
                    297:                 throw e;
                    298:             };
                    299:         };
                    300:         this.editor.updateState();
                    301:     };
                    302: 
                    303:     this.pasteButtonHandler = function() {
                    304:         try {
                    305:             this.editor.execCommand('Paste');
                    306:         } catch (e) {
                    307:             if (this.editor.getBrowserName() == 'Mozilla') {
                    308:                 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'));
                    309:             } else {
                    310:                 throw e;
                    311:             };
                    312:         };
                    313:         this.editor.updateState();
                    314:     };
                    315: 
                    316:     this.setTextStyle = function(style) {
                    317:         /* method for the text style pulldown
                    318:             
                    319:             parse the argument into a type and classname part if it contains
                    320:             a pipe symbol (|), generate a block element
                    321:         */
                    322:         var classname = "";
                    323:         var eltype = style;
                    324:         if (style.indexOf('|') > -1) {
                    325:             style = style.split('|');
                    326:             eltype = style[0];
                    327:             classname = style[1];
                    328:         };
                    329: 
                    330:         var command = eltype;
                    331:         // first create the element, then find it and set the classname
                    332:         if (this.editor.getBrowserName() == 'IE') {
                    333:             command = '<' + eltype + '>';
                    334:         };
                    335:         this.editor.getDocument().execCommand('formatblock', command);
                    336: 
                    337:         // now get a reference to the element just added
                    338:         var selNode = this.editor.getSelectedNode();
                    339:         var el = this.editor.getNearestParentOfType(selNode, eltype);
                    340: 
                    341:         // now set the classname
                    342:         if (classname) {
                    343:             el.className = classname;
                    344:         };
                    345:         this.editor.updateState();
                    346:     };
                    347: 
                    348:     this.updateState = function(selNode) {
                    349:         /* set the text-style pulldown */
                    350:     
                    351:         // first get the nearest style
                    352:         var styles = {}; // use an object here so we can use the 'in' operator later on
                    353:         for (var i=0; i < this.tsselect.options.length; i++) {
                    354:             // XXX we should cache this
                    355:             styles[this.tsselect.options[i].value.toUpperCase()] = i;
                    356:         }
                    357:         
                    358:         var currnode = selNode;
                    359:         var index = 0;
                    360:         while (currnode) {
                    361:             if (currnode.nodeName.toUpperCase() in styles) {
                    362:                 index = styles[currnode.nodeName.toUpperCase()];
                    363:                 break
                    364:             }
                    365:             currnode = currnode.parentNode;
                    366:         }
                    367: 
                    368:         this.tsselect.selectedIndex = index;
                    369:     };
                    370:   
                    371:     this.createContextMenuElements = function(selNode, event) {
                    372:         var ret = new Array();
                    373:         ret.push(new ContextMenuElement(_('Cut'), 
                    374:                     this.cutButtonHandler, this));
                    375:         ret.push(new ContextMenuElement(_('Copy'), 
                    376:                     this.copyButtonHandler, this));
                    377:         ret.push(new ContextMenuElement(_('Paste'), 
                    378:                     this.pasteButtonHandler, this));
                    379:         return ret;
                    380:     };
                    381:     this.disable = function() {
                    382:         this.tsselect.disabled = "disabled";
                    383:     }
                    384:     this.enable = function() {
                    385:         this.tsselect.disabled = "";
                    386:     }
                    387: }
                    388: 
                    389: KupuUI.prototype = new KupuTool;
                    390: 
                    391: // function ColorchooserTool(fgcolorbuttonid, hlcolorbuttonid, colorchooserid) {
                    392: //     /* the colorchooser */
                    393:     
                    394: //     this.fgcolorbutton = getFromSelector(fgcolorbuttonid);
                    395: //     this.hlcolorbutton = getFromSelector(hlcolorbuttonid);
                    396: //     this.ccwindow = getFromSelector(colorchooserid);
                    397: //     this.command = null;
                    398: 
                    399: //     this.initialize = function(editor) {
                    400: //         /* attach the event handlers */
                    401: //         this.editor = editor;
                    402:         
                    403: //         this.createColorchooser(this.ccwindow);
                    404: 
                    405: //         addEventHandler(this.fgcolorbutton, "click", this.openFgColorChooser, this);
                    406: //         addEventHandler(this.hlcolorbutton, "click", this.openHlColorChooser, this);
                    407: //         addEventHandler(this.ccwindow, "click", this.chooseColor, this);
                    408: 
                    409: //         this.hide();
                    410: 
                    411: //         this.editor.logMessage(_('Colorchooser tool initialized'));
                    412: //     };
                    413: 
                    414: //     this.updateState = function(selNode) {
                    415: //         /* update state of the colorchooser */
                    416: //         this.hide();
                    417: //     };
                    418: 
                    419: //     this.openFgColorChooser = function() {
                    420: //         /* event handler for opening the colorchooser */
                    421: //         this.command = "forecolor";
                    422: //         this.show();
                    423: //     };
                    424: 
                    425: //     this.openHlColorChooser = function() {
                    426: //         /* event handler for closing the colorchooser */
                    427: //         if (this.editor.getBrowserName() == "IE") {
                    428: //             this.command = "backcolor";
                    429: //         } else {
                    430: //             this.command = "hilitecolor";
                    431: //         }
                    432: //         this.show();
                    433: //     };
                    434: 
                    435: //     this.chooseColor = function(event) {
                    436: //         /* event handler for choosing the color */
                    437: //         var target = _SARISSA_IS_MOZ ? event.target : event.srcElement;
                    438: //         var cell = this.editor.getNearestParentOfType(target, 'td');
                    439: //         this.editor.execCommand(this.command, cell.getAttribute('bgColor'));
                    440: //         this.hide();
                    441:     
                    442: //         this.editor.logMessage(_('Color chosen'));
                    443: //     };
                    444: 
                    445: //     this.show = function(command) {
                    446: //         /* show the colorchooser */
                    447: //         this.ccwindow.style.display = "block";
                    448: //     };
                    449: 
                    450: //     this.hide = function() {
                    451: //         /* hide the colorchooser */
                    452: //         this.command = null;
                    453: //         this.ccwindow.style.display = "none";
                    454: //     };
                    455: 
                    456: //     this.createColorchooser = function(table) {
                    457: //         /* create the colorchooser table */
                    458:         
                    459: //         var chunks = new Array('00', '33', '66', '99', 'CC', 'FF');
                    460: //         table.setAttribute('id', 'kupu-colorchooser-table');
                    461: //         table.style.borderWidth = '2px';
                    462: //         table.style.borderStyle = 'solid';
                    463: //         table.style.position = 'absolute';
                    464: //         table.style.cursor = 'default';
                    465: //         table.style.display = 'none';
                    466: 
                    467: //         var tbody = document.createElement('tbody');
                    468: 
                    469: //         for (var i=0; i < 6; i++) {
                    470: //             var tr = document.createElement('tr');
                    471: //             var r = chunks[i];
                    472: //             for (var j=0; j < 6; j++) {
                    473: //                 var g = chunks[j];
                    474: //                 for (var k=0; k < 6; k++) {
                    475: //                     var b = chunks[k];
                    476: //                     var color = '#' + r + g + b;
                    477: //                     var td = document.createElement('td');
                    478: //                     td.setAttribute('bgColor', color);
                    479: //                     td.style.backgroundColor = color;
                    480: //                     td.style.borderWidth = '1px';
                    481: //                     td.style.borderStyle = 'solid';
                    482: //                     td.style.fontSize = '1px';
                    483: //                     td.style.width = '10px';
                    484: //                     td.style.height = '10px';
                    485: //                     var text = document.createTextNode('\u00a0');
                    486: //                     td.appendChild(text);
                    487: //                     tr.appendChild(td);
                    488: //                 }
                    489: //             }
                    490: //             tbody.appendChild(tr);
                    491: //         }
                    492: //         table.appendChild(tbody);
                    493: 
                    494: //         return table;
                    495: //     };
                    496: //     this.enable = function() {
                    497: //         KupuButtonEnable(this.fgcolorbutton);
                    498: //         KupuButtonEnable(this.hlcolorbutton);
                    499: //     }
                    500: //     this.disable = function() {
                    501: //         KupuButtonDisable(this.fgcolorbutton);
                    502: //         KupuButtonDisable(this.hlcolorbutton);
                    503: //     }
                    504: // }
                    505: 
                    506: // ColorchooserTool.prototype = new KupuTool;
                    507: 
                    508: function PropertyTool(titlefieldid, descfieldid) {
                    509:     /* The property tool */
                    510: 
                    511:     this.titlefield = getFromSelector(titlefieldid);
                    512:     this.descfield = getFromSelector(descfieldid);
                    513: 
                    514:     this.initialize = function(editor) {
                    515:         /* attach the event handlers and set the initial values */
                    516:         this.editor = editor;
                    517:         addEventHandler(this.titlefield, "change", this.updateProperties, this);
                    518:         addEventHandler(this.descfield, "change", this.updateProperties, this);
                    519:         
                    520:         // set the fields
                    521:         var heads = this.editor.getInnerDocument().getElementsByTagName('head');
                    522:         if (!heads[0]) {
                    523:             this.editor.logMessage(_('No head in document!'), 1);
                    524:         } else {
                    525:             var head = heads[0];
                    526:             var titles = head.getElementsByTagName('title');
                    527:             if (titles.length) {
                    528:                 this.titlefield.value = titles[0].text;
                    529:             }
                    530:             var metas = head.getElementsByTagName('meta');
                    531:             if (metas.length) {
                    532:                 for (var i=0; i < metas.length; i++) {
                    533:                     var meta = metas[i];
                    534:                     if (meta.getAttribute('name') && 
                    535:                             meta.getAttribute('name').toLowerCase() == 
                    536:                             'description') {
                    537:                         this.descfield.value = meta.getAttribute('content');
                    538:                         break;
                    539:                     }
                    540:                 }
                    541:             }
                    542:         }
                    543: 
                    544:         this.editor.logMessage(_('Property tool initialized'));
                    545:     };
                    546: 
                    547:     this.updateProperties = function() {
                    548:         /* event handler for updating the properties form */
                    549:         var doc = this.editor.getInnerDocument();
                    550:         var heads = doc.getElementsByTagName('HEAD');
                    551:         if (!heads) {
                    552:             this.editor.logMessage(_('No head in document!'), 1);
                    553:             return;
                    554:         }
                    555: 
                    556:         var head = heads[0];
                    557: 
                    558:         // set the title
                    559:         var titles = head.getElementsByTagName('title');
                    560:         if (!titles) {
                    561:             var title = doc.createElement('title');
                    562:             var text = doc.createTextNode(this.titlefield.value);
                    563:             title.appendChild(text);
                    564:             head.appendChild(title);
                    565:         } else {
                    566:             var title = titles[0];
                    567:             // IE6 title has no children, and refuses appendChild.
                    568:             // Delete and recreate the title.
                    569:             if (title.childNodes.length == 0) {
                    570:                 title.removeNode(true);
                    571:                 title = doc.createElement('title');
                    572:                 title.innerText = this.titlefield.value;
                    573:                 head.appendChild(title);
                    574:             } else {
                    575:                 title.childNodes[0].nodeValue = this.titlefield.value;
                    576:             }
                    577:         }
                    578:         document.title = this.titlefield.value;
                    579: 
                    580:         // let's just fulfill the usecase, not think about more properties
                    581:         // set the description
                    582:         var metas = doc.getElementsByTagName('meta');
                    583:         var descset = 0;
                    584:         for (var i=0; i < metas.length; i++) {
                    585:             var meta = metas[i];
                    586:             if (meta.getAttribute('name') && 
                    587:                     meta.getAttribute('name').toLowerCase() == 'description') {
                    588:                 meta.setAttribute('content', this.descfield.value);
                    589:             }
                    590:         }
                    591: 
                    592:         if (!descset) {
                    593:             var meta = doc.createElement('meta');
                    594:             meta.setAttribute('name', 'description');
                    595:             meta.setAttribute('content', this.descfield.value);
                    596:             head.appendChild(meta);
                    597:         }
                    598: 
                    599:         this.editor.logMessage(_('Properties modified'));
                    600:     };
                    601: }
                    602: 
                    603: PropertyTool.prototype = new KupuTool;
                    604: 
                    605: function LinkTool() {
                    606:     /* Add and update hyperlinks */
                    607:     
                    608:     this.initialize = function(editor) {
                    609:         this.editor = editor;
                    610:         this.editor.logMessage(_('Link tool initialized'));
                    611:     };
                    612:     
                    613:     this.createLinkHandler = function(event) {
                    614:         /* create a link according to a url entered in a popup */
                    615:         var linkWindow = openPopup('kupupopups/link.html', 300, 200);
                    616:         linkWindow.linktool = this;
                    617:         linkWindow.focus();
                    618:     };
                    619: 
                    620:     this.updateLink = function (linkel, url, type, name, target, title) {
                    621:         if (type && type == 'anchor') {
                    622:             linkel.removeAttribute('href');
                    623:             linkel.setAttribute('name', name);
                    624:         } else {
                    625:             linkel.href = url;
                    626:             if (linkel.innerHTML == "") {
                    627:                 var doc = this.editor.getInnerDocument();
                    628:                 linkel.appendChild(doc.createTextNode(title || url));
                    629:             }
                    630:             if (title) {
                    631:                 linkel.title = title;
                    632:             } else {
                    633:                 linkel.removeAttribute('title');
                    634:             }
                    635:             if (target && target != '') {
                    636:                 linkel.setAttribute('target', target);
                    637:             }
                    638:             else {
                    639:                 linkel.removeAttribute('target');
                    640:             };
                    641:             linkel.style.color = this.linkcolor;
                    642:         };
                    643:     };
                    644: 
                    645:     this.formatSelectedLink = function(url, type, name, target, title) {
                    646:         var currnode = this.editor.getSelectedNode();
                    647: 
                    648:         // selection inside link
                    649:         var linkel = this.editor.getNearestParentOfType(currnode, 'A');
                    650:         if (linkel) {
                    651:             this.updateLink(linkel, url, type, name, target, title);
                    652:             return true;
                    653:         }
                    654: 
                    655:         if (currnode.nodeType!=1) return false;
                    656: 
                    657:         // selection contains links
                    658:         var linkelements = currnode.getElementsByTagName('A');
                    659:         var selection = this.editor.getSelection();
                    660:         var containsLink = false;
                    661:         for (var i = 0; i < linkelements.length; i++) {
                    662:             linkel = linkelements[i];
                    663:             if (selection.containsNode(linkel)) {
                    664:                 this.updateLink(linkel, url, type, name, target, title);
                    665:                 containsLink = true;
                    666:             }
                    667:         };
                    668:         return containsLink;
                    669:     }
                    670: 
                    671:     // Can create a link in the following circumstances:
                    672:     //   The selection is inside a link:
                    673:     //      just update the link attributes.
                    674:     //   The selection contains links:
                    675:     //      update the attributes of the contained links
                    676:     //   No links inside or outside the selection:
                    677:     //      create a link around the selection
                    678:     //   No selection:
                    679:     //      insert a link containing the title
                    680:     //
                    681:     // the order of the arguments is a bit odd here because of backward
                    682:     // compatibility
                    683:     this.createLink = function(url, type, name, target, title) {
                    684:         if (!this.formatSelectedLink(url, type, name, target, title)) {
                    685:             // No links inside or outside.
                    686:             this.editor.execCommand("CreateLink", url);
                    687:             if (!this.formatSelectedLink(url, type, name, target, title)) {
                    688:                 // Insert link with no text selected, insert the title
                    689:                 // or URI instead.
                    690:                 var doc = this.editor.getInnerDocument();
                    691:                 linkel = doc.createElement("a");
                    692:                 linkel.setAttribute('href', url);
                    693:                 linkel.setAttribute('class', 'generated');
                    694:                 this.editor.getSelection().replaceWithNode(linkel, true);
                    695:                 this.updateLink(linkel, url, type, name, target, title);
                    696:             };
                    697:         }
                    698:         this.editor.logMessage(_('Link added'));
                    699:         this.editor.updateState();
                    700:     };
                    701:     
                    702:     this.deleteLink = function() {
                    703:         /* delete the current link */
                    704:         var currnode = this.editor.getSelectedNode();
                    705:         var linkel = this.editor.getNearestParentOfType(currnode, 'a');
                    706:         if (!linkel) {
                    707:             this.editor.logMessage(_('Not inside link'));
                    708:             return;
                    709:         };
                    710:         while (linkel.childNodes.length) {
                    711:             linkel.parentNode.insertBefore(linkel.childNodes[0], linkel);
                    712:         };
                    713:         linkel.parentNode.removeChild(linkel);
                    714:         
                    715:         this.editor.logMessage(_('Link removed'));
                    716:         this.editor.updateState();
                    717:     };
                    718:     
                    719:     this.createContextMenuElements = function(selNode, event) {
                    720:         /* create the 'Create link' or 'Remove link' menu elements */
                    721:         var ret = new Array();
                    722:         var link = this.editor.getNearestParentOfType(selNode, 'a');
                    723:         if (link) {
                    724:             ret.push(new ContextMenuElement(_('Delete link'), this.deleteLink, this));
                    725:         } else {
                    726:             ret.push(new ContextMenuElement(_('Create link'), this.createLinkHandler, this));
                    727:         };
                    728:         return ret;
                    729:     };
                    730: }
                    731: 
                    732: LinkTool.prototype = new KupuTool;
                    733: 
                    734: function LinkToolBox(inputid, buttonid, toolboxid, plainclass, activeclass) {
                    735:     /* create and edit links */
                    736:     
                    737:     this.input = getFromSelector(inputid);
                    738:     this.button = getFromSelector(buttonid);
                    739:     this.toolboxel = getFromSelector(toolboxid);
                    740:     this.plainclass = plainclass;
                    741:     this.activeclass = activeclass;
                    742:     
                    743:     this.initialize = function(tool, editor) {
                    744:         /* attach the event handlers */
                    745:         this.tool = tool;
                    746:         this.editor = editor;
                    747:         addEventHandler(this.input, "blur", this.updateLink, this);
                    748:         addEventHandler(this.button, "click", this.addLink, this);
                    749:     };
                    750: 
                    751:     this.updateState = function(selNode) {
                    752:         /* if we're inside a link, update the input, else empty it */
                    753:         var linkel = this.editor.getNearestParentOfType(selNode, 'a');
                    754:         if (linkel) {
                    755:             // check first before setting a class for backward compatibility
                    756:             if (this.toolboxel) {
                    757:                 this.toolboxel.className = this.activeclass;
                    758:             };
                    759:             this.input.value = linkel.getAttribute('href');
                    760:         } else {
                    761:             // check first before setting a class for backward compatibility
                    762:             if (this.toolboxel) {
                    763:                 this.toolboxel.className = this.plainclass;
                    764:             };
                    765:             this.input.value = '';
                    766:         }
                    767:     };
                    768:     
                    769:     this.addLink = function(event) {
                    770:         /* add a link */
                    771:         var url = this.input.value;
                    772:         this.tool.createLink(url);
                    773:     };
                    774:     
                    775:     this.updateLink = function() {
                    776:         /* update the current link */
                    777:         var currnode = this.editor.getSelectedNode();
                    778:         var linkel = this.editor.getNearestParentOfType(currnode, 'A');
                    779:         if (!linkel) {
                    780:             return;
                    781:         }
                    782: 
                    783:         var url = this.input.value;
                    784:         linkel.setAttribute('href', url);
                    785: 
                    786:         this.editor.logMessage(_('Link modified'));
                    787:     };
                    788: };
                    789: 
                    790: LinkToolBox.prototype = new LinkToolBox;
                    791: 
                    792: function ImageTool() {
                    793:     /* Image tool to add images */
                    794:     
                    795:     this.initialize = function(editor) {
                    796:         /* attach the event handlers */
                    797:         this.editor = editor;
                    798:         this.editor.logMessage(_('Image tool initialized'));
                    799:     };
                    800: 
                    801:     this.createImageHandler = function(event) {
                    802:         /* create an image according to a url entered in a popup */
                    803:         var imageWindow = openPopup('kupupopups/image.html', 300, 200);
                    804:         imageWindow.imagetool = this;
                    805:         imageWindow.focus();
                    806:     };
                    807: 
                    808:     this.createImage = function(url, alttext, imgclass) {
                    809:         /* create an image */
                    810:         var img = this.editor.getInnerDocument().createElement('img');
                    811:         img.src = url;
                    812:         img.removeAttribute('height');
                    813:         img.removeAttribute('width');
                    814:         if (alttext) {
                    815:             img.alt = alttext;
                    816:         };
                    817:         if (imgclass) {
                    818:             img.className = imgclass;
                    819:         };
                    820:         img = this.editor.insertNodeAtSelection(img, 1);
                    821:         this.editor.logMessage(_('Image inserted'));
                    822:         this.editor.updateState();
                    823:         return img;
                    824:     };
                    825: 
                    826:     this.setImageClass = function(imgclass) {
                    827:         /* set the class of the selected image */
                    828:         var currnode = this.editor.getSelectedNode();
                    829:         var currimg = this.editor.getNearestParentOfType(currnode, 'IMG');
                    830:         if (currimg) {
                    831:             currimg.className = imgclass;
                    832:         };
                    833:     };
                    834: 
                    835:     this.createContextMenuElements = function(selNode, event) {
                    836:         return new Array(new ContextMenuElement(_('Create image'), this.createImageHandler, this));
                    837:     };
                    838: }
                    839: 
                    840: ImageTool.prototype = new KupuTool;
                    841: 
                    842: function ImageToolBox(inputfieldid, insertbuttonid, classselectid, toolboxid, plainclass, activeclass) {
                    843:     /* toolbox for adding images */
                    844: 
                    845:     this.inputfield = getFromSelector(inputfieldid);
                    846:     this.insertbutton = getFromSelector(insertbuttonid);
                    847:     this.classselect = getFromSelector(classselectid);
                    848:     this.toolboxel = getFromSelector(toolboxid);
                    849:     this.plainclass = plainclass;
                    850:     this.activeclass = activeclass;
                    851: 
                    852:     this.initialize = function(tool, editor) {
                    853:         this.tool = tool;
                    854:         this.editor = editor;
                    855:         addEventHandler(this.classselect, "change", this.setImageClass, this);
                    856:         addEventHandler(this.insertbutton, "click", this.addImage, this);
                    857:     };
                    858: 
                    859:     this.updateState = function(selNode, event) {
                    860:         /* update the state of the toolbox element */
                    861:         var imageel = this.editor.getNearestParentOfType(selNode, 'img');
                    862:         if (imageel) {
                    863:             // check first before setting a class for backward compatibility
                    864:             if (this.toolboxel) {
                    865:                 this.toolboxel.className = this.activeclass;
                    866:                 this.inputfield.value = imageel.getAttribute('src');
                    867:                 var imgclass = imageel.className ? imageel.className : 'image-inline';
                    868:                 selectSelectItem(this.classselect, imgclass);
                    869:             };
                    870:         } else {
                    871:             if (this.toolboxel) {
                    872:                 this.toolboxel.className = this.plainclass;
                    873:             };
                    874:         };
                    875:     };
                    876: 
                    877:     this.addImage = function() {
                    878:         /* add an image */
                    879:         var url = this.inputfield.value;
                    880:         var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
                    881:         this.tool.createImage(url, null, sel_class);
                    882:         this.editor.focusDocument();
                    883:     };
                    884: 
                    885:     this.setImageClass = function() {
                    886:         /* set the class for the current image */
                    887:         var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
                    888:         this.tool.setImageClass(sel_class);
                    889:         this.editor.focusDocument();
                    890:     };
                    891: };
                    892: 
                    893: ImageToolBox.prototype = new KupuToolBox;
                    894: 
                    895: // function TableTool() {
                    896: //     /* The table tool */
                    897: 
                    898: //     // XXX There are some awfully long methods in here!!
                    899: //     this.createContextMenuElements = function(selNode, event) {
                    900: //         var table =  this.editor.getNearestParentOfType(selNode, 'table');
                    901: //         if (!table) {
                    902: //             ret = new Array();
                    903: //             var el = new ContextMenuElement(_('Add table'), this.addPlainTable, this);
                    904: //             ret.push(el);
                    905: //             return ret;
                    906: //         } else {
                    907: //             var ret = new Array();
                    908: //             ret.push(new ContextMenuElement(_('Add row'), this.addTableRow, this));
                    909: //             ret.push(new ContextMenuElement(_('Delete row'), this.delTableRow, this));
                    910: //             ret.push(new ContextMenuElement(_('Add column'), this.addTableColumn, this));
                    911: //             ret.push(new ContextMenuElement(_('Delete column'), this.delTableColumn, this));
                    912: //             ret.push(new ContextMenuElement(_('Delete Table'), this.delTable, this));
                    913: //             return ret;
                    914: //         };
                    915: //     };
                    916: 
                    917: //     this.addPlainTable = function() {
                    918: //         /* event handler for the context menu */
                    919: //         this.createTable(2, 3, 1, 'plain');
                    920: //     };
                    921: 
                    922: //     this.createTable = function(rows, cols, makeHeader, tableclass) {
                    923: //         /* add a table */
                    924: //         if (rows < 1 || rows > 99 || cols < 1 || cols > 99) {
                    925: //             this.editor.logMessage(_('Invalid table size'), 1);
                    926: //             return;
                    927: //         };
                    928: 
                    929: //         var doc = this.editor.getInnerDocument();
                    930: 
                    931: //         table = doc.createElement("table");
                    932: //         table.className = tableclass;
                    933: 
                    934: //         // If the user wants a row of headings, make them
                    935: //         if (makeHeader) {
                    936: //             var tr = doc.createElement("tr");
                    937: //             var thead = doc.createElement("thead");
                    938: //             for (i=0; i < cols; i++) {
                    939: //                 var th = doc.createElement("th");
                    940: //                 th.appendChild(doc.createTextNode("Col " + i+1));
                    941: //                 tr.appendChild(th);
                    942: //             }
                    943: //             thead.appendChild(tr);
                    944: //             table.appendChild(thead);
                    945: //         }
                    946: 
                    947: //         tbody = doc.createElement("tbody");
                    948: //         for (var i=0; i < rows; i++) {
                    949: //             var tr = doc.createElement("tr");
                    950: //             for (var j=0; j < cols; j++) {
                    951: //                 var td = doc.createElement("td");
                    952: //                 var content = doc.createTextNode('\u00a0');
                    953: //                 td.appendChild(content);
                    954: //                 tr.appendChild(td);
                    955: //             }
                    956: //             tbody.appendChild(tr);
                    957: //         }
                    958: //         table.appendChild(tbody);
                    959: //         this.editor.insertNodeAtSelection(table);
                    960: 
                    961: //         this._setTableCellHandlers(table);
                    962: 
                    963: //         this.editor.logMessage(_('Table added'));
                    964: //         this.editor.updateState();
                    965: //         return table;
                    966: //     };
                    967: 
                    968: //     this._setTableCellHandlers = function(table) {
                    969: //         // make each cell select its full contents if it's clicked
                    970: //         addEventHandler(table, 'click', this._selectContentIfEmpty, this);
                    971: 
                    972: //         var cells = table.getElementsByTagName('td');
                    973: //         for (var i=0; i < cells.length; i++) {
                    974: //             addEventHandler(cells[i], 'click', this._selectContentIfEmpty, this);
                    975: //         };
                    976:         
                    977: //         // select the nbsp in the first cell
                    978: //         var firstcell = cells[0];
                    979: //         if (firstcell) {
                    980: //             var children = firstcell.childNodes;
                    981: //             if (children.length == 1 && children[0].nodeType == 3 && 
                    982: //                     children[0].nodeValue == '\xa0') {
                    983: //                 var selection = this.editor.getSelection();
                    984: //                 selection.selectNodeContents(firstcell);
                    985: //             };
                    986: //         };
                    987: //     };
                    988:     
                    989: //     this._selectContentIfEmpty = function() {
                    990: //         var selNode = this.editor.getSelectedNode();
                    991: //         var cell = this.editor.getNearestParentOfType(selNode, 'td');
                    992: //         if (!cell) {
                    993: //             return;
                    994: //         };
                    995: //         var children = cell.childNodes;
                    996: //         if (children.length == 1 && children[0].nodeType == 3 && 
                    997: //                 children[0].nodeValue == '\xa0') {
                    998: //             var selection = this.editor.getSelection();
                    999: //             selection.selectNodeContents(cell);
                   1000: //         };
                   1001: //     };
                   1002: 
                   1003: //     this.addTableRow = function() {
                   1004: //         /* Find the current row and add a row after it */
                   1005: //         var currnode = this.editor.getSelectedNode();
                   1006: //         var currtbody = this.editor.getNearestParentOfType(currnode, "TBODY");
                   1007: //         var bodytype = "tbody";
                   1008: //         if (!currtbody) {
                   1009: //             currtbody = this.editor.getNearestParentOfType(currnode, "THEAD");
                   1010: //             bodytype = "thead";
                   1011: //         }
                   1012: //         var parentrow = this.editor.getNearestParentOfType(currnode, "TR");
                   1013: //         var nextrow = parentrow.nextSibling;
                   1014: 
                   1015: //         // get the number of cells we should place
                   1016: //         var colcount = 0;
                   1017: //         for (var i=0; i < currtbody.childNodes.length; i++) {
                   1018: //             var el = currtbody.childNodes[i];
                   1019: //             if (el.nodeType != 1) {
                   1020: //                 continue;
                   1021: //             }
                   1022: //             if (el.nodeName.toLowerCase() == 'tr') {
                   1023: //                 var cols = 0;
                   1024: //                 for (var j=0; j < el.childNodes.length; j++) {
                   1025: //                     if (el.childNodes[j].nodeType == 1) {
                   1026: //                         cols++;
                   1027: //                     }
                   1028: //                 }
                   1029: //                 if (cols > colcount) {
                   1030: //                     colcount = cols;
                   1031: //                 }
                   1032: //             }
                   1033: //         }
                   1034: 
                   1035: //         var newrow = this.editor.getInnerDocument().createElement("TR");
                   1036: 
                   1037: //         for (var i = 0; i < colcount; i++) {
                   1038: //             var newcell;
                   1039: //             if (bodytype == 'tbody') {
                   1040: //                 newcell = this.editor.getInnerDocument().createElement("TD");
                   1041: //             } else {
                   1042: //                 newcell = this.editor.getInnerDocument().createElement("TH");
                   1043: //             }
                   1044: //             var newcellvalue = this.editor.getInnerDocument().createTextNode("\u00a0");
                   1045: //             newcell.appendChild(newcellvalue);
                   1046: //             newrow.appendChild(newcell);
                   1047: //         }
                   1048: 
                   1049: //         if (!nextrow) {
                   1050: //             currtbody.appendChild(newrow);
                   1051: //         } else {
                   1052: //             currtbody.insertBefore(newrow, nextrow);
                   1053: //         }
                   1054:         
                   1055: //         this.editor.focusDocument();
                   1056: //         this.editor.logMessage(_('Table row added'));
                   1057: //     };
                   1058: 
                   1059: //     this.delTableRow = function() {
                   1060: //         /* Find the current row and delete it */
                   1061: //         var currnode = this.editor.getSelectedNode();
                   1062: //         var parentrow = this.editor.getNearestParentOfType(currnode, "TR");
                   1063: //         if (!parentrow) {
                   1064: //             this.editor.logMessage(_('No row to delete'), 1);
                   1065: //             return;
                   1066: //         }
                   1067: 
                   1068: //         // move selection aside
                   1069: //         // XXX: doesn't work if parentrow is the only row of thead/tbody/tfoot
                   1070: //         // XXX: doesn't preserve the colindex
                   1071: //         var selection = this.editor.getSelection();
                   1072: //         if (parentrow.nextSibling) {
                   1073: //             selection.selectNodeContents(parentrow.nextSibling.firstChild);
                   1074: //         } else if (parentrow.previousSibling) {
                   1075: //             selection.selectNodeContents(parentrow.previousSibling.firstChild);
                   1076: //         };
                   1077: 
                   1078: //         // remove the row
                   1079: //         parentrow.parentNode.removeChild(parentrow);
                   1080: 
                   1081: //         this.editor.focusDocument();
                   1082: //         this.editor.logMessage(_('Table row removed'));
                   1083: //     };
                   1084: 
                   1085: //     this.addTableColumn = function() {
                   1086: //         /* Add a new column after the current column */
                   1087: //         var currnode = this.editor.getSelectedNode();
                   1088: //         var currtd = this.editor.getNearestParentOfType(currnode, 'TD');
                   1089: //         if (!currtd) {
                   1090: //             currtd = this.editor.getNearestParentOfType(currnode, 'TH');
                   1091: //         }
                   1092: //         if (!currtd) {
                   1093: //             this.editor.logMessage(_('No parentcolumn found!'), 1);
                   1094: //             return;
                   1095: //         }
                   1096: //         var currtr = this.editor.getNearestParentOfType(currnode, 'TR');
                   1097: //         var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
                   1098:         
                   1099: //         // get the current index
                   1100: //         var tdindex = this._getColIndex(currtd);
                   1101: //         // XXX this looks like a debug message, remove
                   1102: //         this.editor.logMessage(_('tdindex: ${tdindex}'));
                   1103: 
                   1104: //         // now add a column to all rows
                   1105: //         // first the thead
                   1106: //         var theads = currtable.getElementsByTagName('THEAD');
                   1107: //         if (theads) {
                   1108: //             for (var i=0; i < theads.length; i++) {
                   1109: //                 // let's assume table heads only have ths
                   1110: //                 var currthead = theads[i];
                   1111: //                 for (var j=0; j < currthead.childNodes.length; j++) {
                   1112: //                     var tr = currthead.childNodes[j];
                   1113: //                     if (tr.nodeType != 1) {
                   1114: //                         continue;
                   1115: //                     }
                   1116: //                     var currindex = 0;
                   1117: //                     for (var k=0; k < tr.childNodes.length; k++) {
                   1118: //                         var th = tr.childNodes[k];
                   1119: //                         if (th.nodeType != 1) {
                   1120: //                             continue;
                   1121: //                         }
                   1122: //                         if (currindex == tdindex) {
                   1123: //                             var doc = this.editor.getInnerDocument();
                   1124: //                             var newth = doc.createElement('th');
                   1125: //                             var text = doc.createTextNode('\u00a0');
                   1126: //                             newth.appendChild(text);
                   1127: //                             if (tr.childNodes.length == k+1) {
                   1128: //                                 // the column will be on the end of the row
                   1129: //                                 tr.appendChild(newth);
                   1130: //                             } else {
                   1131: //                                 tr.insertBefore(newth, tr.childNodes[k + 1]);
                   1132: //                             }
                   1133: //                             break;
                   1134: //                         }
                   1135: //                         currindex++;
                   1136: //                     }
                   1137: //                 }
                   1138: //             }
                   1139: //         }
                   1140: 
                   1141: //         // then the tbody
                   1142: //         var tbodies = currtable.getElementsByTagName('TBODY');
                   1143: //         if (tbodies) {
                   1144: //             for (var i=0; i < tbodies.length; i++) {
                   1145: //                 // let's assume table heads only have ths
                   1146: //                 var currtbody = tbodies[i];
                   1147: //                 for (var j=0; j < currtbody.childNodes.length; j++) {
                   1148: //                     var tr = currtbody.childNodes[j];
                   1149: //                     if (tr.nodeType != 1) {
                   1150: //                         continue;
                   1151: //                     }
                   1152: //                     var currindex = 0;
                   1153: //                     for (var k=0; k < tr.childNodes.length; k++) {
                   1154: //                         var td = tr.childNodes[k];
                   1155: //                         if (td.nodeType != 1) {
                   1156: //                             continue;
                   1157: //                         }
                   1158: //                         if (currindex == tdindex) {
                   1159: //                             var doc = this.editor.getInnerDocument();
                   1160: //                             var newtd = doc.createElement('td');
                   1161: //                             var text = doc.createTextNode('\u00a0');
                   1162: //                             newtd.appendChild(text);
                   1163: //                             if (tr.childNodes.length == k+1) {
                   1164: //                                 // the column will be on the end of the row
                   1165: //                                 tr.appendChild(newtd);
                   1166: //                             } else {
                   1167: //                                 tr.insertBefore(newtd, tr.childNodes[k + 1]);
                   1168: //                             }
                   1169: //                             break;
                   1170: //                         }
                   1171: //                         currindex++;
                   1172: //                     }
                   1173: //                 }
                   1174: //             }
                   1175: //         }
                   1176: //         this.editor.focusDocument();
                   1177: //         this.editor.logMessage(_('Table column added'));
                   1178: //     };
                   1179: 
                   1180: //     this.delTableColumn = function() {
                   1181: //         /* remove a column */
                   1182: //         var currnode = this.editor.getSelectedNode();
                   1183: //         var currtd = this.editor.getNearestParentOfType(currnode, 'TD');
                   1184: //         if (!currtd) {
                   1185: //             currtd = this.editor.getNearestParentOfType(currnode, 'TH');
                   1186: //         }
                   1187: //         var currcolindex = this._getColIndex(currtd);
                   1188: //         var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
                   1189: 
                   1190: //         // move selection aside
                   1191: //         var selection = this.editor.getSelection();
                   1192: //         if (currtd.nextSibling) {
                   1193: //             selection.selectNodeContents(currtd.nextSibling);
                   1194: //         } else if (currtd.previousSibling) {
                   1195: //             selection.selectNodeContents(currtd.previousSibling);
                   1196: //         };
                   1197: 
                   1198: //         // remove the theaders
                   1199: //         var heads = currtable.getElementsByTagName('THEAD');
                   1200: //         if (heads.length) {
                   1201: //             for (var i=0; i < heads.length; i++) {
                   1202: //                 var thead = heads[i];
                   1203: //                 for (var j=0; j < thead.childNodes.length; j++) {
                   1204: //                     var tr = thead.childNodes[j];
                   1205: //                     if (tr.nodeType != 1) {
                   1206: //                         continue;
                   1207: //                     }
                   1208: //                     var currindex = 0;
                   1209: //                     for (var k=0; k < tr.childNodes.length; k++) {
                   1210: //                         var th = tr.childNodes[k];
                   1211: //                         if (th.nodeType != 1) {
                   1212: //                             continue;
                   1213: //                         }
                   1214: //                         if (currindex == currcolindex) {
                   1215: //                             tr.removeChild(th);
                   1216: //                             break;
                   1217: //                         }
                   1218: //                         currindex++;
                   1219: //                     }
                   1220: //                 }
                   1221: //             }
                   1222: //         }
                   1223: 
                   1224: //         // now we remove the column field, a bit harder since we need to take 
                   1225: //         // colspan and rowspan into account XXX Not right, fix theads as well
                   1226: //         var bodies = currtable.getElementsByTagName('TBODY');
                   1227: //         for (var i=0; i < bodies.length; i++) {
                   1228: //             var currtbody = bodies[i];
                   1229: //             var relevant_rowspan = 0;
                   1230: //             for (var j=0; j < currtbody.childNodes.length; j++) {
                   1231: //                 var tr = currtbody.childNodes[j];
                   1232: //                 if (tr.nodeType != 1) {
                   1233: //                     continue;
                   1234: //                 }
                   1235: //                 var currindex = 0
                   1236: //                 for (var k=0; k < tr.childNodes.length; k++) {
                   1237: //                     var cell = tr.childNodes[k];
                   1238: //                     if (cell.nodeType != 1) {
                   1239: //                         continue;
                   1240: //                     }
                   1241: //                     var colspan = cell.colSpan;
                   1242: //                     if (currindex == currcolindex) {
                   1243: //                         tr.removeChild(cell);
                   1244: //                         break;
                   1245: //                     }
                   1246: //                     currindex++;
                   1247: //                 }
                   1248: //             }
                   1249: //         }
                   1250: //         this.editor.focusDocument();
                   1251: //         this.editor.logMessage(_('Table column deleted'));
                   1252: //     };
                   1253: 
                   1254: //     this.delTable = function() {
                   1255: //         /* delete the current table */
                   1256: //         var currnode = this.editor.getSelectedNode();
                   1257: //         var table = this.editor.getNearestParentOfType(currnode, 'table');
                   1258: //         if (!table) {
                   1259: //             this.editor.logMessage(_('Not inside a table!'));
                   1260: //             return;
                   1261: //         };
                   1262: //         table.parentNode.removeChild(table);
                   1263: //         this.editor.logMessage(_('Table removed'));
                   1264: //     };
                   1265: 
                   1266: //     this.setColumnAlign = function(newalign) {
                   1267: //         /* change the alignment of a full column */
                   1268: //         var currnode = this.editor.getSelectedNode();
                   1269: //         var currtd = this.editor.getNearestParentOfType(currnode, "TD");
                   1270: //         var bodytype = 'tbody';
                   1271: //         if (!currtd) {
                   1272: //             currtd = this.editor.getNearestParentOfType(currnode, "TH");
                   1273: //             bodytype = 'thead';
                   1274: //         }
                   1275: //         var currcolindex = this._getColIndex(currtd);
                   1276: //         var currtable = this.editor.getNearestParentOfType(currnode, "TABLE");
                   1277: 
                   1278: //         // unfortunately this is not enough to make the browsers display
                   1279: //         // the align, we need to set it on individual cells as well and
                   1280: //         // mind the rowspan...
                   1281: //         for (var i=0; i < currtable.childNodes.length; i++) {
                   1282: //             var currtbody = currtable.childNodes[i];
                   1283: //             if (currtbody.nodeType != 1 || 
                   1284: //                     (currtbody.nodeName.toUpperCase() != "THEAD" &&
                   1285: //                         currtbody.nodeName.toUpperCase() != "TBODY")) {
                   1286: //                 continue;
                   1287: //             }
                   1288: //             for (var j=0; j < currtbody.childNodes.length; j++) {
                   1289: //                 var row = currtbody.childNodes[j];
                   1290: //                 if (row.nodeType != 1) {
                   1291: //                     continue;
                   1292: //                 }
                   1293: //                 var index = 0;
                   1294: //                 for (var k=0; k < row.childNodes.length; k++) {
                   1295: //                     var cell = row.childNodes[k];
                   1296: //                     if (cell.nodeType != 1) {
                   1297: //                         continue;
                   1298: //                     }
                   1299: //                     if (index == currcolindex) {
                   1300: //                         if (this.editor.config.use_css) {
                   1301: //                             cell.style.textAlign = newalign;
                   1302: //                         } else {
                   1303: //                             cell.setAttribute('align', newalign);
                   1304: //                         }
                   1305: //                         cell.className = 'align-' + newalign;
                   1306: //                     }
                   1307: //                     index++;
                   1308: //                 }
                   1309: //             }
                   1310: //         }
                   1311: //     };
                   1312: 
                   1313: //     this.setTableClass = function(sel_class) {
                   1314: //         /* set the class for the table */
                   1315: //         var currnode = this.editor.getSelectedNode();
                   1316: //         var currtable = this.editor.getNearestParentOfType(currnode, 'TABLE');
                   1317: 
                   1318: //         if (currtable) {
                   1319: //             currtable.className = sel_class;
                   1320: //         }
                   1321: //     };
                   1322: 
                   1323: //     this._getColIndex = function(currcell) {
                   1324: //         /* Given a node, return an integer for which column it is */
                   1325: //         var prevsib = currcell.previousSibling;
                   1326: //         var currcolindex = 0;
                   1327: //         while (prevsib) {
                   1328: //             if (prevsib.nodeType == 1 && 
                   1329: //                     (prevsib.tagName.toUpperCase() == "TD" || 
                   1330: //                         prevsib.tagName.toUpperCase() == "TH")) {
                   1331: //                 var colspan = prevsib.colSpan;
                   1332: //                 if (colspan) {
                   1333: //                     currcolindex += parseInt(colspan);
                   1334: //                 } else {
                   1335: //                     currcolindex++;
                   1336: //                 }
                   1337: //             }
                   1338: //             prevsib = prevsib.previousSibling;
                   1339: //             if (currcolindex > 30) {
                   1340: //                 alert(_("Recursion detected when counting column position"));
                   1341: //                 return;
                   1342: //             }
                   1343: //         }
                   1344: 
                   1345: //         return currcolindex;
                   1346: //     };
                   1347: 
                   1348: //     this._getColumnAlign = function(selNode) {
                   1349: //         /* return the alignment setting of the current column */
                   1350: //         var align;
                   1351: //         var td = this.editor.getNearestParentOfType(selNode, 'td');
                   1352: //         if (!td) {
                   1353: //             td = this.editor.getNearestParentOfType(selNode, 'th');
                   1354: //         };
                   1355: //         if (td) {
                   1356: //             align = td.getAttribute('align');
                   1357: //             if (this.editor.config.use_css) {
                   1358: //                 align = td.style.textAlign;
                   1359: //             };
                   1360: //         };
                   1361: //         return align;
                   1362: //     };
                   1363: 
                   1364: //     this.fixTable = function(event) {
                   1365: //         /* fix the table so it can be processed by Kupu */
                   1366: //         // since this can be quite a nasty creature we can't just use the
                   1367: //         // helper methods
                   1368:         
                   1369: //         // first we create a new tbody element
                   1370: //         var currnode = this.editor.getSelectedNode();
                   1371: //         var table = this.editor.getNearestParentOfType(currnode, 'TABLE');
                   1372: //         if (!table) {
                   1373: //             this.editor.logMessage(_('Not inside a table!'));
                   1374: //             return;
                   1375: //         };
                   1376: //         this._fixTableHelper(table);
                   1377: //     };
                   1378: 
                   1379: //     this._fixTableHelper = function(table) {
                   1380: //         /* the code to actually fix tables */
                   1381: //         var doc = this.editor.getInnerDocument();
                   1382: //         var tbody = doc.createElement('tbody');
                   1383: 
                   1384: //         if (this.editor.config.table_classes) {
                   1385: //             var allowed_classes = this.editor.config.table_classes['class'];
                   1386: //             if (!allowed_classes.contains(table.className)) {
                   1387: //                 table.className = allowed_classes[0];
                   1388: //             };
                   1389: //         } else {
                   1390: //             table.removeAttribute('class');
                   1391: //             table.removeAttribute('className');
                   1392: //         };
                   1393: //         table.removeAttribute('border');
                   1394: //         table.removeAttribute('cellpadding');
                   1395: //         table.removeAttribute('cellPadding');
                   1396: //         table.removeAttribute('cellspacing');
                   1397: //         table.removeAttribute('cellSpacing');
                   1398: 
                   1399: //         // now get all the rows of the table, the rows can either be
                   1400: //         // direct descendants of the table or inside a 'tbody', 'thead'
                   1401: //         // or 'tfoot' element
                   1402: //         var rows = new Array();
                   1403: //         var parents = new Array('thead', 'tbody', 'tfoot');
                   1404: //         for (var i=0; i < table.childNodes.length; i++) {
                   1405: //             var node = table.childNodes[i];
                   1406: //             if (node.nodeName.toLowerCase() == 'tr') {
                   1407: //                 rows.push(node);
                   1408: //             } else if (parents.contains(node.nodeName.toLowerCase())) {
                   1409: //                 for (var j=0; j < node.childNodes.length; j++) {
                   1410: //                     var inode = node.childNodes[j];
                   1411: //                     if (inode.nodeName.toLowerCase() == 'tr') {
                   1412: //                         rows.push(inode);
                   1413: //                     };
                   1414: //                 };
                   1415: //             };
                   1416: //         };
                   1417:         
                   1418: //         // now find out how many cells our rows should have
                   1419: //         var numcols = 0;
                   1420: //         for (var i=0; i < rows.length; i++) {
                   1421: //             var row = rows[i];
                   1422: //             var currnumcols = 0;
                   1423: //             for (var j=0; j < row.childNodes.length; j++) {
                   1424: //                 var node = row.childNodes[j];
                   1425: //                 if (node.nodeName.toLowerCase() == 'td' ||
                   1426: //                         node.nodeName.toLowerCase() == 'th') {
                   1427: //                     var colspan = 1;
                   1428: //                     if (node.getAttribute('colSpan')) {
                   1429: //                         colspan = parseInt(node.getAttribute('colSpan'));
                   1430: //                     };
                   1431: //                     currnumcols += colspan;
                   1432: //                 };
                   1433: //             };
                   1434: //             if (currnumcols > numcols) {
                   1435: //                 numcols = currnumcols;
                   1436: //             };
                   1437: //         };
                   1438: 
                   1439: //         // now walk through all rows to clean them up
                   1440: //         for (var i=0; i < rows.length; i++) {
                   1441: //             var row = rows[i];
                   1442: //             var newrow = doc.createElement('tr');
                   1443: //             var currcolnum = 0;
                   1444: //             while (row.childNodes.length > 0) {
                   1445: //                 var node = row.childNodes[0];
                   1446: //                 if (node.nodeName.toLowerCase() != 'td' && node.nodeName.toLowerCase() != 'th') {
                   1447: //                     row.removeChild(node);
                   1448: //                     continue;
                   1449: //                 };
                   1450: //                 node.removeAttribute('colSpan');
                   1451: //                 node.removeAttribute('rowSpan');
                   1452: //                 newrow.appendChild(node);
                   1453: //             };
                   1454: //             if (newrow.childNodes.length) {
                   1455: //                 tbody.appendChild(newrow);
                   1456: //             };
                   1457: //         };
                   1458: 
                   1459: //         // now make sure all rows have the correct length
                   1460: //         for (var i=0; i < tbody.childNodes.length; i++) {
                   1461: //             var row = tbody.childNodes[i];
                   1462: //             var cellname = row.childNodes[0].nodeName;
                   1463: //             while (row.childNodes.length < numcols) {
                   1464: //                 var cell = doc.createElement(cellname);
                   1465: //                 var nbsp = doc.createTextNode('\u00a0');
                   1466: //                 cell.appendChild(nbsp);
                   1467: //                 row.appendChild(cell);
                   1468: //             };
                   1469: //         };
                   1470:         
                   1471: //         // now remove all the old stuff from the table and add the new tbody
                   1472: //         var tlength = table.childNodes.length;
                   1473: //         for (var i=0; i < tlength; i++) {
                   1474: //             table.removeChild(table.childNodes[0]);
                   1475: //         };
                   1476: //         table.appendChild(tbody);
                   1477: 
                   1478: //         this.editor.focusDocument();
                   1479: //         this.editor.logMessage(_('Table cleaned up'));
                   1480: //     };
                   1481: 
                   1482: //     this.fixAllTables = function() {
                   1483: //         /* fix all the tables in the document at once */
                   1484: //         var tables = this.editor.getInnerDocument().getElementsByTagName('table');
                   1485: //         for (var i=0; i < tables.length; i++) {
                   1486: //             this._fixTableHelper(tables[i]);
                   1487: //         };
                   1488: //     };
                   1489: // };
                   1490: 
                   1491: // TableTool.prototype = new KupuTool;
                   1492: 
                   1493: // function TableToolBox(addtabledivid, edittabledivid, newrowsinputid, 
                   1494: //                     newcolsinputid, makeheaderinputid, classselectid, alignselectid, addtablebuttonid,
                   1495: //                     addrowbuttonid, delrowbuttonid, addcolbuttonid, delcolbuttonid, fixbuttonid,
                   1496: //                     fixallbuttonid, toolboxid, plainclass, activeclass) {
                   1497: //     /* The table tool */
                   1498: 
                   1499: //     // XXX There are some awfully long methods in here!!
                   1500:     
                   1501: 
                   1502: //     // a lot of dependencies on html elements here, but most implementations
                   1503: //     // will use them all I guess
                   1504: //     this.addtablediv = getFromSelector(addtabledivid);
                   1505: //     this.edittablediv = getFromSelector(edittabledivid);
                   1506: //     this.newrowsinput = getFromSelector(newrowsinputid);
                   1507: //     this.newcolsinput = getFromSelector(newcolsinputid);
                   1508: //     this.makeheaderinput = getFromSelector(makeheaderinputid);
                   1509: //     this.classselect = getFromSelector(classselectid);
                   1510: //     this.alignselect = getFromSelector(alignselectid);
                   1511: //     this.addtablebutton = getFromSelector(addtablebuttonid);
                   1512: //     this.addrowbutton = getFromSelector(addrowbuttonid);
                   1513: //     this.delrowbutton = getFromSelector(delrowbuttonid);
                   1514: //     this.addcolbutton = getFromSelector(addcolbuttonid);
                   1515: //     this.delcolbutton = getFromSelector(delcolbuttonid);
                   1516: //     this.fixbutton = getFromSelector(fixbuttonid);
                   1517: //     this.fixallbutton = getFromSelector(fixallbuttonid);
                   1518: //     this.toolboxel = getFromSelector(toolboxid);
                   1519: //     this.plainclass = plainclass;
                   1520: //     this.activeclass = activeclass;
                   1521: 
                   1522: //     // register event handlers
                   1523: //     this.initialize = function(tool, editor) {
                   1524: //         /* attach the event handlers */
                   1525: //         this.tool = tool;
                   1526: //         this.editor = editor;
                   1527: //         // build the select list of table classes if configured
                   1528: //         if (this.editor.config.table_classes) {
                   1529: //             var classes = this.editor.config.table_classes['class'];
                   1530: //             while (this.classselect.hasChildNodes()) {
                   1531: //                 this.classselect.removeChild(this.classselect.firstChild);
                   1532: //             };
                   1533: //             for (var i=0; i < classes.length; i++) {
                   1534: //                 var classname = classes[i];
                   1535: //                 var option = document.createElement('option');
                   1536: //                 var content = document.createTextNode(classname);
                   1537: //                 option.appendChild(content);
                   1538: //                 option.setAttribute('value', classname);
                   1539: //                 this.classselect.appendChild(option);
                   1540: //             };
                   1541: //         };
                   1542: //         addEventHandler(this.addtablebutton, "click", this.addTable, this);
                   1543: //         addEventHandler(this.addrowbutton, "click", this.tool.addTableRow, this.tool);
                   1544: //         addEventHandler(this.delrowbutton, "click", this.tool.delTableRow, this.tool);
                   1545: //         addEventHandler(this.addcolbutton, "click", this.tool.addTableColumn, this.tool);
                   1546: //         addEventHandler(this.delcolbutton, "click", this.tool.delTableColumn, this.tool);
                   1547: //         addEventHandler(this.alignselect, "change", this.setColumnAlign, this);
                   1548: //         addEventHandler(this.classselect, "change", this.setTableClass, this);
                   1549: //         addEventHandler(this.fixbutton, "click", this.tool.fixTable, this.tool);
                   1550: //         addEventHandler(this.fixallbutton, "click", this.tool.fixAllTables, this.tool);
                   1551: //         this.addtablediv.style.display = "block";
                   1552: //         this.edittablediv.style.display = "none";
                   1553: //         this.editor.logMessage(_('Table tool initialized'));
                   1554: //     };
                   1555: 
                   1556: //     this.updateState = function(selNode) {
                   1557: //         /* update the state (add/edit) and update the pulldowns (if required) */
                   1558: //         var table = this.editor.getNearestParentOfType(selNode, 'table');
                   1559: //         if (table) {
                   1560: //             this.addtablediv.style.display = "none";
                   1561: //             this.edittablediv.style.display = "block";
                   1562: 
                   1563: //             var align = this.tool._getColumnAlign(selNode);
                   1564: //             selectSelectItem(this.alignselect, align);
                   1565: //             selectSelectItem(this.classselect, table.className);
                   1566: //             if (this.toolboxel) {
                   1567: //                 this.toolboxel.className = this.activeclass;
                   1568: //             };
                   1569: //         } else {
                   1570: //             this.edittablediv.style.display = "none";
                   1571: //             this.addtablediv.style.display = "block";
                   1572: //             this.alignselect.selectedIndex = 0;
                   1573: //             this.classselect.selectedIndex = 0;
                   1574: //             if (this.toolboxel) {
                   1575: //                 this.toolboxel.className = this.plainclass;
                   1576: //             };
                   1577: //         };
                   1578: //     };
                   1579: 
                   1580: //     this.addTable = function() {
                   1581: //         /* add a table */
                   1582: //         var rows = this.newrowsinput.value;
                   1583: //         var cols = this.newcolsinput.value;
                   1584: //         var makeHeader = this.makeheaderinput.checked;
                   1585: //         // XXX getFromSelector
                   1586: //         var classchooser = getFromSelector("kupu-table-classchooser-add");
                   1587: //         var tableclass = this.classselect.options[this.classselect.selectedIndex].value;
                   1588:         
                   1589: //         this.tool.createTable(rows, cols, makeHeader, tableclass);
                   1590: //     };
                   1591: 
                   1592: //     this.setColumnAlign = function() {
                   1593: //         /* set the alignment of the current column */
                   1594: //         var newalign = this.alignselect.options[this.alignselect.selectedIndex].value;
                   1595: //         this.tool.setColumnAlign(newalign);
                   1596: //     };
                   1597: 
                   1598: //     this.setTableClass = function() {
                   1599: //         /* set the class for the current table */
                   1600: //         var sel_class = this.classselect.options[this.classselect.selectedIndex].value;
                   1601: //         if (sel_class) {
                   1602: //             this.tool.setTableClass(sel_class);
                   1603: //         };
                   1604: //     };
                   1605: // };
                   1606: 
                   1607: // TableToolBox.prototype = new KupuToolBox;
                   1608: 
                   1609: function ListTool(addulbuttonid, addolbuttonid, ulstyleselectid, olstyleselectid) {
                   1610:     /* tool to set list styles */
                   1611: 
                   1612:     this.addulbutton = getFromSelector(addulbuttonid);
                   1613:     this.addolbutton = getFromSelector(addolbuttonid);
                   1614:     this.ulselect = getFromSelector(ulstyleselectid);
                   1615:     this.olselect = getFromSelector(olstyleselectid);
                   1616: 
                   1617:     this.style_to_type = {'decimal': '1',
                   1618:                             'lower-alpha': 'a',
                   1619:                             'upper-alpha': 'A',
                   1620:                             'lower-roman': 'i',
                   1621:                             'upper-roman': 'I',
                   1622:                             'disc': 'disc',
                   1623:                             'square': 'square',
                   1624:                             'circle': 'circle',
                   1625:                             'none': 'none'
                   1626:                             };
                   1627:     this.type_to_style = {'1': 'decimal',
                   1628:                             'a': 'lower-alpha',
                   1629:                             'A': 'upper-alpha',
                   1630:                             'i': 'lower-roman',
                   1631:                             'I': 'upper-roman',
                   1632:                             'disc': 'disc',
                   1633:                             'square': 'square',
                   1634:                             'circle': 'circle',
                   1635:                             'none': 'none'
                   1636:                             };
                   1637:     
                   1638:     this.initialize = function(editor) {
                   1639:         /* attach event handlers */
                   1640:         this.editor = editor;
                   1641:         this._fixTabIndex(this.addulbutton);
                   1642:         this._fixTabIndex(this.addolbutton);
                   1643:         this._fixTabIndex(this.ulselect);
                   1644:         this._fixTabIndex(this.olselect);
                   1645: 
                   1646:         addEventHandler(this.addulbutton, "click", this.addUnorderedList, this);
                   1647:         addEventHandler(this.addolbutton, "click", this.addOrderedList, this);
                   1648:         addEventHandler(this.ulselect, "change", this.setUnorderedListStyle, this);
                   1649:         addEventHandler(this.olselect, "change", this.setOrderedListStyle, this);
                   1650:         this.ulselect.style.display = "none";
                   1651:         this.olselect.style.display = "none";
                   1652: 
                   1653:         this.editor.logMessage(_('List style tool initialized'));
                   1654:     };
                   1655: 
                   1656:     this._handleStyles = function(currnode, onselect, offselect) {
                   1657:         if (this.editor.config.use_css) {
                   1658:             var currstyle = currnode.style.listStyleType;
                   1659:         } else {
                   1660:             var currstyle = this.type_to_style[currnode.getAttribute('type')];
                   1661:         }
                   1662:         selectSelectItem(onselect, currstyle);
                   1663:         offselect.style.display = "none";
                   1664:         onselect.style.display = "inline";
                   1665:         offselect.selectedIndex = 0;
                   1666:     };
                   1667: 
                   1668:     this.updateState = function(selNode) {
                   1669:         /* update the visibility and selection of the list type pulldowns */
                   1670:         // we're going to walk through the tree manually since we want to 
                   1671:         // check on 2 items at the same time
                   1672:         for (var currnode=selNode; currnode; currnode=currnode.parentNode) {
                   1673:             var tag = currnode.nodeName.toLowerCase();
                   1674:             if (tag == 'ul') {
                   1675:                 this._handleStyles(currnode, this.ulselect, this.olselect);
                   1676:                 return;
                   1677:             } else if (tag == 'ol') {
                   1678:                 this._handleStyles(currnode, this.olselect, this.ulselect);
                   1679:                 return;
                   1680:             }
                   1681:         }
                   1682:         with(this.ulselect) {
                   1683:             selectedIndex = 0;
                   1684:             style.display = "none";
                   1685:         };
                   1686:         with(this.olselect) {
                   1687:             selectedIndex = 0;
                   1688:             style.display = "none";
                   1689:         };
                   1690:     };
                   1691: 
                   1692:     this.addList = function(command) {
                   1693:         this.ulselect.style.display = "inline";
                   1694:         this.olselect.style.display = "none";
                   1695:         this.editor.execCommand(command);
                   1696:         this.editor.focusDocument();
                   1697:     };
                   1698:     this.addUnorderedList = function() {
                   1699:         /* add an unordered list */
                   1700:         this.addList("insertunorderedlist");
                   1701:     };
                   1702: 
                   1703:     this.addOrderedList = function() {
                   1704:         /* add an ordered list */
                   1705:         this.addList("insertorderedlist");
                   1706:     };
                   1707: 
                   1708:     this.setListStyle = function(tag, select) {
                   1709:         /* set the type of an ul */
                   1710:         var currnode = this.editor.getSelectedNode();
                   1711:         var l = this.editor.getNearestParentOfType(currnode, tag);
                   1712:         var style = select.options[select.selectedIndex].value;
                   1713:         if (this.editor.config.use_css) {
                   1714:             l.style.listStyleType = style;
                   1715:         } else {
                   1716:             l.setAttribute('type', this.style_to_type[style]);
                   1717:         }
                   1718:         this.editor.focusDocument();
                   1719:         this.editor.logMessage(_('List style changed'));
                   1720:     };
                   1721: 
                   1722:     this.setUnorderedListStyle = function() {
                   1723:         /* set the type of an ul */
                   1724:         this.setListStyle('ul', this.ulselect);
                   1725:     };
                   1726: 
                   1727:     this.setOrderedListStyle = function() {
                   1728:         /* set the type of an ol */
                   1729:         this.setListStyle('ol', this.olselect);
                   1730:     };
                   1731: 
                   1732:     this.enable = function() {
                   1733:         KupuButtonEnable(this.addulbutton);
                   1734:         KupuButtonEnable(this.addolbutton);
                   1735:         this.ulselect.disabled = "";
                   1736:         this.olselect.disabled = "";
                   1737:     }
                   1738:     this.disable = function() {
                   1739:         KupuButtonDisable(this.addulbutton);
                   1740:         KupuButtonDisable(this.addolbutton);
                   1741:         this.ulselect.disabled = "disabled";
                   1742:         this.olselect.disabled = "disabled";
                   1743:     }
                   1744: };
                   1745: 
                   1746: ListTool.prototype = new KupuTool;
                   1747: 
                   1748: function ShowPathTool() {
                   1749:     /* shows the path to the current element in the status bar */
                   1750: 
                   1751:     this.updateState = function(selNode) {
                   1752:         /* calculate and display the path */
                   1753:         var path = '';
                   1754:         var url = null; // for links we want to display the url too
                   1755:         var currnode = selNode;
                   1756:         while (currnode != null && currnode.nodeName != '#document') {
                   1757:             if (currnode.nodeName.toLowerCase() == 'a') {
                   1758:                 url = currnode.getAttribute('href');
                   1759:             };
                   1760:             path = '/' + currnode.nodeName.toLowerCase() + path;
                   1761:             currnode = currnode.parentNode;
                   1762:         }
                   1763:         
                   1764:         try {
                   1765:             window.status = url ? 
                   1766:                     (path.toString() + ' - contains link to \'' + 
                   1767:                         url.toString() + '\'') :
                   1768:                     path;
                   1769:         } catch (e) {
                   1770:             this.editor.logMessage(_('Could not set status bar message, ' +
                   1771:                                     'check your browser\'s security settings.'
                   1772:                                     ), 1);
                   1773:         };
                   1774:     };
                   1775: };
                   1776: 
                   1777: ShowPathTool.prototype = new KupuTool;
                   1778: 
                   1779: function ViewSourceTool() {
                   1780:     /* tool to provide a 'show source' context menu option */
                   1781:     this.sourceWindow = null;
                   1782:     
                   1783:     this.viewSource = function() {
                   1784:         /* open a window and write the current contents of the iframe to it */
                   1785:         if (this.sourceWindow) {
                   1786:             this.sourceWindow.close();
                   1787:         };
                   1788:         this.sourceWindow = window.open('#', 'sourceWindow');
                   1789:         
                   1790:         //var transform = this.editor._filterContent(this.editor.getInnerDocument().documentElement);
                   1791:         //var contents = transform.xml; 
                   1792:         var contents = '<html>\n' + this.editor.getInnerDocument().documentElement.innerHTML + '\n</html>';
                   1793:         
                   1794:         var doc = this.sourceWindow.document;
                   1795:         doc.write('\xa0');
                   1796:         doc.close();
                   1797:         var body = doc.getElementsByTagName("body")[0];
                   1798:         while (body.hasChildNodes()) {
                   1799:             body.removeChild(body.firstChild);
                   1800:         };
                   1801:         var pre = doc.createElement('pre');
                   1802:         var textNode = doc.createTextNode(contents);
                   1803:         body.appendChild(pre);
                   1804:         pre.appendChild(textNode);
                   1805:     };
                   1806:     
                   1807:     this.createContextMenuElements = function(selNode, event) {
                   1808:         /* create the context menu element */
                   1809:         return new Array(new ContextMenuElement(_('View source'), this.viewSource, this));
                   1810:     };
                   1811: };
                   1812: 
                   1813: ViewSourceTool.prototype = new KupuTool;
                   1814: 
                   1815: function DefinitionListTool(dlbuttonid) {
                   1816:     /* a tool for managing definition lists
                   1817: 
                   1818:         the dl elements should behave much like plain lists, and the keypress
                   1819:         behaviour should be similar
                   1820:     */
                   1821: 
                   1822:     this.dlbutton = getFromSelector(dlbuttonid);
                   1823:     
                   1824:     this.initialize = function(editor) {
                   1825:         /* initialize the tool */
                   1826:         this.editor = editor;
                   1827:         this._fixTabIndex(this.dlbutton);
                   1828:         addEventHandler(this.dlbutton, 'click', this.createDefinitionList, this);
                   1829:         addEventHandler(editor.getInnerDocument(), 'keyup', this._keyDownHandler, this);
                   1830:         addEventHandler(editor.getInnerDocument(), 'keypress', this._keyPressHandler, this);
                   1831:     };
                   1832: 
                   1833:     // even though the following methods may seem view related, they belong 
                   1834:     // here, since they describe core functionality rather then view-specific
                   1835:     // stuff
                   1836:     this.handleEnterPress = function(selNode) {
                   1837:         var dl = this.editor.getNearestParentOfType(selNode, 'dl');
                   1838:         if (dl) {
                   1839:             var dt = this.editor.getNearestParentOfType(selNode, 'dt');
                   1840:             if (dt) {
                   1841:                 if (dt.childNodes.length == 1 && dt.childNodes[0].nodeValue == '\xa0') {
                   1842:                     this.escapeFromDefinitionList(dl, dt, selNode);
                   1843:                     return;
                   1844:                 };
                   1845: 
                   1846:                 var selection = this.editor.getSelection();
                   1847:                 var startoffset = selection.startOffset();
                   1848:                 var endoffset = selection.endOffset(); 
                   1849:                 if (endoffset > startoffset) {
                   1850:                     // throw away any selected stuff
                   1851:                     selection.cutChunk(startoffset, endoffset);
                   1852:                     selection = this.editor.getSelection();
                   1853:                     startoffset = selection.startOffset();
                   1854:                 };
                   1855:                 
                   1856:                 var ellength = selection.getElementLength(selection.parentElement());
                   1857:                 if (startoffset >= ellength - 1) {
                   1858:                     // create a new element
                   1859:                     this.createDefinition(dl, dt);
                   1860:                 } else {
                   1861:                     var doc = this.editor.getInnerDocument();
                   1862:                     var newdt = selection.splitNodeAtSelection(dt);
                   1863:                     var newdd = doc.createElement('dd');
                   1864:                     while (newdt.hasChildNodes()) {
                   1865:                         if (newdt.firstChild != newdt.lastChild || newdt.firstChild.nodeName.toLowerCase() != 'br') {
                   1866:                             newdd.appendChild(newdt.firstChild);
                   1867:                         };
                   1868:                     };
                   1869:                     newdt.parentNode.replaceChild(newdd, newdt);
                   1870:                     selection.selectNodeContents(newdd);
                   1871:                     selection.collapse();
                   1872:                 };
                   1873:             } else {
                   1874:                 var dd = this.editor.getNearestParentOfType(selNode, 'dd');
                   1875:                 if (!dd) {
                   1876:                     this.editor.logMessage(_('Not inside a definition list element!'));
                   1877:                     return;
                   1878:                 };
                   1879:                 if (dd.childNodes.length == 1 && dd.childNodes[0].nodeValue == '\xa0') {
                   1880:                     this.escapeFromDefinitionList(dl, dd, selNode);
                   1881:                     return;
                   1882:                 };
                   1883:                 var selection = this.editor.getSelection();
                   1884:                 var startoffset = selection.startOffset();
                   1885:                 var endoffset = selection.endOffset();
                   1886:                 if (endoffset > startoffset) {
                   1887:                     // throw away any selected stuff
                   1888:                     selection.cutChunk(startoffset, endoffset);
                   1889:                     selection = this.editor.getSelection();
                   1890:                     startoffset = selection.startOffset();
                   1891:                 };
                   1892:                 var ellength = selection.getElementLength(selection.parentElement());
                   1893:                 if (startoffset >= ellength - 1) {
                   1894:                     // create a new element
                   1895:                     this.createDefinitionTerm(dl, dd);
                   1896:                 } else {
                   1897:                     // add a break and continue in this element
                   1898:                     var br = this.editor.getInnerDocument().createElement('br');
                   1899:                     this.editor.insertNodeAtSelection(br, 1);
                   1900:                     //var selection = this.editor.getSelection();
                   1901:                     //selection.moveStart(1);
                   1902:                     selection.collapse(true);
                   1903:                 };
                   1904:             };
                   1905:         };
                   1906:     };
                   1907: 
                   1908:     this.handleTabPress = function(selNode) {
                   1909:     };
                   1910: 
                   1911:     this._keyDownHandler = function(event) {
                   1912:         var selNode = this.editor.getSelectedNode();
                   1913:         var dl = this.editor.getNearestParentOfType(selNode, 'dl');
                   1914:         if (!dl) {
                   1915:             return;
                   1916:         };
                   1917:         switch (event.keyCode) {
                   1918:             case 13:
                   1919:                 if (event.preventDefault) {
                   1920:                     event.preventDefault();
                   1921:                 } else {
                   1922:                     event.returnValue = false;
                   1923:                 };
                   1924:                 break;
                   1925:         };
                   1926:     };
                   1927: 
                   1928:     this._keyPressHandler = function(event) {
                   1929:         var selNode = this.editor.getSelectedNode();
                   1930:         var dl = this.editor.getNearestParentOfType(selNode, 'dl');
                   1931:         if (!dl) {
                   1932:             return;
                   1933:         };
                   1934:         switch (event.keyCode) {
                   1935:             case 13:
                   1936:                 this.handleEnterPress(selNode);
                   1937:                 if (event.preventDefault) {
                   1938:                     event.preventDefault();
                   1939:                 } else {
                   1940:                     event.returnValue = false;
                   1941:                 };
                   1942:                 break;
                   1943:             case 9:
                   1944:                 if (event.preventDefault) {
                   1945:                     event.preventDefault();
                   1946:                 } else {
                   1947:                     event.returnValue = false;
                   1948:                 };
                   1949:                 this.handleTabPress(selNode);
                   1950:         };
                   1951:     };
                   1952: 
                   1953:     this.createDefinitionList = function() {
                   1954:         /* create a new definition list (dl) */
                   1955:         var selection = this.editor.getSelection();
                   1956:         var doc = this.editor.getInnerDocument();
                   1957: 
                   1958:         var selection = this.editor.getSelection();
                   1959:         var cloned = selection.cloneContents();
                   1960:         // first get the 'first line' (until the first break) and use it
                   1961:         // as the dt's content
                   1962:         var iterator = new NodeIterator(cloned);
                   1963:         var currnode = null;
                   1964:         var remove = false;
                   1965:         while (currnode = iterator.next()) {
                   1966:             if (currnode.nodeName.toLowerCase() == 'br') {
                   1967:                 remove = true;
                   1968:             };
                   1969:             if (remove) {
                   1970:                 var next = currnode;
                   1971:                 while (!next.nextSibling) {
                   1972:                     next = next.parentNode;
                   1973:                 };
                   1974:                 next = next.nextSibling;
                   1975:                 iterator.setCurrent(next);
                   1976:                 currnode.parentNode.removeChild(currnode);
                   1977:             };
                   1978:         };
                   1979: 
                   1980:         var dtcontentcontainer = cloned;
                   1981:         var collapsetoend = false;
                   1982:         
                   1983:         var dl = doc.createElement('dl');
                   1984:         this.editor.insertNodeAtSelection(dl);
                   1985:         var dt = this.createDefinitionTerm(dl);
                   1986:         if (dtcontentcontainer.hasChildNodes()) {
                   1987:             collapsetoend = true;
                   1988:             while (dt.hasChildNodes()) {
                   1989:                 dt.removeChild(dt.firstChild);
                   1990:             };
                   1991:             while (dtcontentcontainer.hasChildNodes()) {
                   1992:                 dt.appendChild(dtcontentcontainer.firstChild);
                   1993:             };
                   1994:         };
                   1995: 
                   1996:         var selection = this.editor.getSelection();
                   1997:         selection.selectNodeContents(dt);
                   1998:         selection.collapse(collapsetoend);
                   1999:     };
                   2000: 
                   2001:     this.createDefinitionTerm = function(dl, dd) {
                   2002:         /* create a new definition term inside the current dl */
                   2003:         var doc = this.editor.getInnerDocument();
                   2004:         var dt = doc.createElement('dt');
                   2005:         // somehow Mozilla seems to add breaks to all elements...
                   2006:         if (dd) {
                   2007:             if (dd.lastChild.nodeName.toLowerCase() == 'br') {
                   2008:                 dd.removeChild(dd.lastChild);
                   2009:             };
                   2010:         };
                   2011:         // dd may be null here, if so we assume this is the first element in 
                   2012:         // the dl
                   2013:         if (!dd || dl == dd.lastChild) {
                   2014:             dl.appendChild(dt);
                   2015:         } else {
                   2016:             var nextsibling = dd.nextSibling;
                   2017:             if (nextsibling) {
                   2018:                 dl.insertBefore(dt, nextsibling);
                   2019:             } else {
                   2020:                 dl.appendChild(dt);
                   2021:             };
                   2022:         };
                   2023:         var nbsp = doc.createTextNode('\xa0');
                   2024:         dt.appendChild(nbsp);
                   2025:         var selection = this.editor.getSelection();
                   2026:         selection.selectNodeContents(dt);
                   2027:         selection.collapse();
                   2028: 
                   2029:         this.editor.focusDocument();
                   2030:         return dt;
                   2031:     };
                   2032: 
                   2033:     this.createDefinition = function(dl, dt, initial_content) {
                   2034:         var doc = this.editor.getInnerDocument();
                   2035:         var dd = doc.createElement('dd');
                   2036:         var nextsibling = dt.nextSibling;
                   2037:         // somehow Mozilla seems to add breaks to all elements...
                   2038:         if (dt) {
                   2039:             if (dt.lastChild.nodeName.toLowerCase() == 'br') {
                   2040:                 dt.removeChild(dt.lastChild);
                   2041:             };
                   2042:         };
                   2043:         while (nextsibling) {
                   2044:             var name = nextsibling.nodeName.toLowerCase();
                   2045:             if (name == 'dd' || name == 'dt') {
                   2046:                 break;
                   2047:             } else {
                   2048:                 nextsibling = nextsibling.nextSibling;
                   2049:             };
                   2050:         };
                   2051:         if (nextsibling) {
                   2052:             dl.insertBefore(dd, nextsibling);
                   2053:             //this._fixStructure(doc, dl, nextsibling);
                   2054:         } else {
                   2055:             dl.appendChild(dd);
                   2056:         };
                   2057:         if (initial_content) {
                   2058:             for (var i=0; i < initial_content.length; i++) {
                   2059:                 dd.appendChild(initial_content[i]);
                   2060:             };
                   2061:         };
                   2062:         var nbsp = doc.createTextNode('\xa0');
                   2063:         dd.appendChild(nbsp);
                   2064:         var selection = this.editor.getSelection();
                   2065:         selection.selectNodeContents(dd);
                   2066:         selection.collapse();
                   2067:     };
                   2068: 
                   2069:     this.escapeFromDefinitionList = function(dl, currel, selNode) {
                   2070:         var doc = this.editor.getInnerDocument();
                   2071:         var p = doc.createElement('p');
                   2072:         var nbsp = doc.createTextNode('\xa0');
                   2073:         p.appendChild(nbsp);
                   2074: 
                   2075:         if (dl.lastChild == currel) {
                   2076:             dl.parentNode.insertBefore(p, dl.nextSibling);
                   2077:         } else {
                   2078:             for (var i=0; i < dl.childNodes.length; i++) {
                   2079:                 var child = dl.childNodes[i];
                   2080:                 if (child == currel) {
                   2081:                     var newdl = this.editor.getInnerDocument().createElement('dl');
                   2082:                     while (currel.nextSibling) {
                   2083:                         newdl.appendChild(currel.nextSibling);
                   2084:                     };
                   2085:                     dl.parentNode.insertBefore(newdl, dl.nextSibling);
                   2086:                     dl.parentNode.insertBefore(p, dl.nextSibling);
                   2087:                 };
                   2088:             };
                   2089:         };
                   2090:         currel.parentNode.removeChild(currel);
                   2091:         var selection = this.editor.getSelection();
                   2092:         selection.selectNodeContents(p);
                   2093:         selection.collapse();
                   2094:         this.editor.focusDocument();
                   2095:     };
                   2096: 
                   2097:     this._fixStructure = function(doc, dl, offsetnode) {
                   2098:         /* makes sure the order of the elements is correct */
                   2099:         var currname = offsetnode.nodeName.toLowerCase();
                   2100:         var currnode = offsetnode.nextSibling;
                   2101:         while (currnode) {
                   2102:             if (currnode.nodeType == 1) {
                   2103:                 var nodename = currnode.nodeName.toLowerCase();
                   2104:                 if (currname == 'dt' && nodename == 'dt') {
                   2105:                     var dd = doc.createElement('dd');
                   2106:                     while (currnode.hasChildNodes()) {
                   2107:                         dd.appendChild(currnode.childNodes[0]);
                   2108:                     };
                   2109:                     currnode.parentNode.replaceChild(dd, currnode);
                   2110:                 } else if (currname == 'dd' && nodename == 'dd') {
                   2111:                     var dt = doc.createElement('dt');
                   2112:                     while (currnode.hasChildNodes()) {
                   2113:                         dt.appendChild(currnode.childNodes[0]);
                   2114:                     };
                   2115:                     currnode.parentNode.replaceChild(dt, currnode);
                   2116:                 };
                   2117:             };
                   2118:             currnode = currnode.nextSibling;
                   2119:         };
                   2120:     };
                   2121: };
                   2122: 
                   2123: DefinitionListTool.prototype = new KupuTool;
                   2124: 
                   2125: function KupuZoomTool(buttonid, firsttab, lasttab) {
                   2126:     this.button = getFromSelector(buttonid);
                   2127:     firsttab = firsttab || 'kupu-tb-styles';
                   2128:     lasttab = lasttab || 'kupu-logo-button';
                   2129: 
                   2130:     this.initialize = function(editor) {
                   2131:         this.offclass = 'kupu-zoom';
                   2132:         this.onclass = 'kupu-zoom-pressed';
                   2133:         this.pressed = false;
                   2134: 
                   2135:         this.baseinitialize(editor);
                   2136:         this.button.tabIndex = this.editor.document.editable.tabIndex;
                   2137:         addEventHandler(window, "resize", this.onresize, this);
                   2138:         addEventHandler(window, "scroll", this.onscroll, this);
                   2139: 
                   2140:         /* Toolbar tabbing */
                   2141:         var lastbutton = getFromSelector(lasttab);
                   2142:         var firstbutton = getFromSelector(firsttab);
                   2143:         var iframe = editor.getInnerDocument();
                   2144:         this.setTabbing(iframe, firstbutton, lastbutton);
                   2145:         this.setTabbing(firstbutton, null, editor.getDocument().getWindow());
                   2146: 
                   2147:         this.editor.logMessage(_('Zoom tool initialized'));
                   2148:     };
                   2149: };
                   2150: 
                   2151: KupuZoomTool.prototype = new KupuLateFocusStateButton;
                   2152: KupuZoomTool.prototype.baseinitialize = KupuZoomTool.prototype.initialize;
                   2153: 
                   2154: KupuZoomTool.prototype.onscroll = function() {
                   2155:     if (!this.zoomed) return;
                   2156:     /* XXX Problem here: Mozilla doesn't generate onscroll when window is
                   2157:      * scrolled by focus move or selection. */
                   2158:     var top = window.pageYOffset!=undefined ? window.pageYOffset : document.documentElement.scrollTop;
                   2159:     var left = window.pageXOffset!=undefined ? window.pageXOffset : document.documentElement.scrollLeft;
                   2160:     if (top || left) window.scrollTo(0, 0);
                   2161: }
                   2162: 
                   2163: // Handle tab pressed from a control.
                   2164: KupuZoomTool.prototype.setTabbing = function(control, forward, backward) {
                   2165:     function TabDown(event) {
                   2166:         if (event.keyCode != 9 || !this.zoomed) return;
                   2167: 
                   2168:         var target = event.shiftKey ? backward : forward;
                   2169:         if (!target) return;
                   2170: 
                   2171:         if (event.stopPropogation) event.stopPropogation();
                   2172:         event.cancelBubble = true;
                   2173:         event.returnValue = false;
                   2174: 
                   2175:         target.focus();
                   2176:         return false;
                   2177:     }
                   2178:     addEventHandler(control, "keydown", TabDown, this);
                   2179: }
                   2180: 
                   2181: KupuZoomTool.prototype.onresize = function() {
                   2182:     if (!this.zoomed) return;
                   2183: 
                   2184:     var editor = this.editor;
                   2185:     var iframe = editor.getDocument().editable;
                   2186:     var sourcetool = editor.getTool('sourceedittool');
                   2187:     var sourceArea = sourcetool?sourcetool.getSourceArea():null;
                   2188: 
                   2189:     var fulleditor = iframe.parentNode;
                   2190:     var body = document.body;
                   2191: 
                   2192:     if (window.innerWidth) {
                   2193:         var width = window.innerWidth;
                   2194:         var height = window.innerHeight;
                   2195:     } else if (document.documentElement) {
                   2196:         var width = document.documentElement.offsetWidth-5;
                   2197:         var height = document.documentElement.offsetHeight-5;
                   2198:     } else {
                   2199:         var width = document.body.offsetWidth-5;
                   2200:         var height = document.body.offsetHeight-5;
                   2201:     }
                   2202:     width = width + 'px';
                   2203:     var offset = iframe.offsetTop;
                   2204:     if (sourceArea) offset = sourceArea.offsetTop-1;
                   2205:     // XXX: TODO: Using wrong values here, figure out why.
                   2206:     var nheight = Math.max(height - offset -1/*top border*/, 10);
                   2207:     nheight = nheight + 'px';
                   2208:     fulleditor.style.width = width; /*IE needs this*/
                   2209:     iframe.style.width = width;
                   2210:     iframe.style.height = nheight;
                   2211:     if (sourceArea) {
                   2212:         sourceArea.style.width = width;
                   2213:         sourceArea.style.height = height;
                   2214:     }
                   2215: }
                   2216: 
                   2217: KupuZoomTool.prototype.checkfunc = function(selNode, button, editor, event) {
                   2218:     return this.zoomed;
                   2219: }
                   2220: 
                   2221: KupuZoomTool.prototype.commandfunc = function(button, editor) {
                   2222:     /* Toggle zoom state */
                   2223:     var zoom = button.pressed;
                   2224:     this.zoomed = zoom;
                   2225: 
                   2226:     var zoomClass = 'kupu-fulleditor-zoomed';
                   2227:     var iframe = editor.getDocument().getEditable();
                   2228: 
                   2229:     var body = document.body;
                   2230:     var html = document.getElementsByTagName('html')[0];
                   2231:     if (zoom) {
                   2232:         html.style.overflow = 'hidden';
                   2233:         window.scrollTo(0, 0);
                   2234:         editor.setClass(zoomClass);
                   2235:         body.className += ' '+zoomClass;
                   2236:         this.onresize();
                   2237:     } else {
                   2238:         html.style.overflow = '';
                   2239:         var fulleditor = iframe.parentNode;
                   2240:         fulleditor.style.width = '';
                   2241:         body.className = body.className.replace(' '+zoomClass, '');
                   2242:         editor.clearClass(zoomClass);
                   2243: 
                   2244:         iframe.style.width = '';
                   2245:         iframe.style.height = '';
                   2246: 
                   2247:         var sourcetool = editor.getTool('sourceedittool');
                   2248:         var sourceArea = sourcetool?sourcetool.getSourceArea():null;
                   2249:         if (sourceArea) {
                   2250:             sourceArea.style.width = '';
                   2251:             sourceArea.style.height = '';
                   2252:         };
                   2253:     }
                   2254:     var doc = editor.getInnerDocument();
                   2255:     // Mozilla needs this. Yes, really!
                   2256:     doc.designMode=doc.designMode;
                   2257: 
                   2258:     window.scrollTo(0, iframe.offsetTop);
                   2259:     editor.focusDocument();
                   2260: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>