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>