Annotation of kupu/silva/kupusilvatools.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: // $Id: kupusilvatools.js 14851 2005-07-21 11:39:15Z duncan $
12:
13: // a mapping from namespace to field names, here you can configure which
14: // metadata fields should be editable with the property editor (needs to
15: // be moved to somewhere in Silva or something?)
16: EDITABLE_METADATA = {
17: 'http://infrae.com/namespaces/metadata/silva-extra':
18: [['contactname', 'text', 0, 'Contact name'],
19: ['contactemail', 'text', 0, 'Contact email']
20: ],
21: 'http://infrae.com/namespaces/metadata/abstract':
22: [['author', 'text', 1, 'Presenting author'],
23: ['co_authors', 'textarea', 0, 'Co-author(s)']]
24: }
25:
26: function SilvaLinkTool() {
27: /* redefine the contextmenu elements */
28: };
29:
30: SilvaLinkTool.prototype = new LinkTool;
31:
32: SilvaLinkTool.prototype.createContextMenuElements = function(selNode, event) {
33: /* create the 'Create link' or 'Remove link' menu elements */
34: var ret = new Array();
35: var link = this.editor.getNearestParentOfType(selNode, 'a');
36: if (link) {
37: ret.push(new ContextMenuElement('Delete link', this.deleteLink, this));
38: } else {
39: ret.push(new ContextMenuElement('Create link', getLink, this));
40: };
41: return ret;
42: };
43:
44: function SilvaLinkToolBox(inputid, targetselectid, targetinputid, addbuttonid, updatebuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
45: /* create and edit links */
46:
47: this.input = getFromSelector(inputid);
48: this.targetselect = getFromSelector(targetselectid);
49: this.targetinput = getFromSelector(targetinputid);
50: this.addbutton = getFromSelector(addbuttonid);
51: this.updatebutton = getFromSelector(updatebuttonid);
52: this.delbutton = getFromSelector(delbuttonid);
53: this.toolboxel = getFromSelector(toolboxid);
54: this.plainclass = plainclass;
55: this.activeclass = activeclass;
56: };
57:
58: SilvaLinkToolBox.prototype = new LinkToolBox;
59:
60: SilvaLinkToolBox.prototype.initialize = function(tool, editor) {
61: this.tool = tool;
62: this.editor = editor;
63: addEventHandler(this.targetselect, 'change', this.selectTargetHandler, this);
64: addEventHandler(this.targetinput, 'change', this.selectTargetHandler, this);
65: addEventHandler(this.addbutton, 'click', this.createLinkHandler, this);
66: addEventHandler(this.updatebutton, 'click', this.createLinkHandler, this);
67: addEventHandler(this.delbutton, 'click', this.tool.deleteLink, this);
68: this.targetinput.style.display = 'none';
69: this.editor.logMessage('Link tool initialized');
70: };
71:
72: SilvaLinkToolBox.prototype.selectTargetHandler = function(event) {
73: var select = this.targetselect;
74: var input = this.targetinput;
75:
76: var selvalue = select.options[select.selectedIndex].value;
77: if (selvalue != 'input') {
78: input.style.display = 'none';
79: } else {
80: input.style.display = 'inline';
81: };
82: };
83:
84: SilvaLinkToolBox.prototype.createLinkHandler = function(event) {
85: var url = this.input.value;
86: var target = this.targetselect.options[this.targetselect.selectedIndex].value;
87: if (target == 'input') {
88: target = this.targetinput.value;
89: };
90: this.tool.createLink(url, 'link', null, target);
91: };
92:
93: SilvaLinkToolBox.prototype.updateState = function(selNode, event) {
94: var currnode = selNode;
95: var link = false;
96: var href = '';
97: while (currnode) {
98: if (currnode.nodeName.toLowerCase() == 'a') {
99: href = currnode.getAttribute('href');
100: if (href) {
101: if (this.toolboxel) {
102: this.toolboxel.className = this.activeclass;
103: };
104: this.input.value = href;
105: var target = currnode.getAttribute('target');
106: if (!target) {
107: this.targetselect.selectedIndex = 0;
108: this.targetinput.style.display = 'none';
109: } else {
110: var target_found = false;
111: for (var i=0; i < this.targetselect.options.length; i++) {
112: var option = this.targetselect.options[i];
113: if (option.value == target) {
114: this.targetselect.selectedIndex = i;
115: target_found = true;
116: break;
117: };
118: };
119: if (target_found) {
120: this.targetinput.value = '';
121: this.targetinput.style.display = 'none';
122: } else {
123: // XXX this is pretty hard-coded...
124: this.targetselect.selectedIndex = this.targetselect.options.length - 1;
125: this.targetinput.value = target;
126: this.targetinput.style.display = 'inline';
127: };
128: };
129: this.addbutton.style.display = 'none';
130: this.updatebutton.style.display = 'inline';
131: this.delbutton.style.display = 'inline';
132: return;
133: };
134: };
135: currnode = currnode.parentNode;
136: };
137: this.targetselect.selectedIndex = 0;
138: this.targetinput.value = '';
139: this.targetinput.style.display = 'none';
140: this.updatebutton.style.display = 'none';
141: this.delbutton.style.display = 'none';
142: this.addbutton.style.display = 'inline';
143: if (this.toolboxel) {
144: this.toolboxel.className = this.plainclass;
145: };
146: this.input.value = '';
147: };
148:
149: function SilvaImageTool(editelid, urlinputid, targetselectid, targetinputid,
150: hireslinkradioid, linklinkradioid, linkinputid,
151: alignselectid, titleinputid, toolboxid, plainclass,
152: activeclass) {
153: /* Silva specific image tool */
154: this.editel = getFromSelector(editelid);
155: this.urlinput = getFromSelector(urlinputid);
156: this.targetselect = getFromSelector(targetselectid);
157: this.targetinput = getFromSelector(targetinputid);
158: this.hireslinkradio = getFromSelector(hireslinkradioid);
159: this.linklinkradio = getFromSelector(linklinkradioid);
160: this.linkinput = getFromSelector(linkinputid);
161: this.alignselect = getFromSelector(alignselectid);
162: this.titleinput = getFromSelector(titleinputid);
163: this.toolboxel = getFromSelector(toolboxid);
164: this.plainclass = plainclass;
165: this.activeclass = activeclass;
166: }
167:
168: SilvaImageTool.prototype = new ImageTool;
169:
170: SilvaImageTool.prototype.initialize = function(editor) {
171: this.editor = editor;
172: addEventHandler(this.targetselect, 'change', this.setTarget, this);
173: addEventHandler(this.targetselect, 'change', this.selectTargetHandler, this);
174: addEventHandler(this.targetinput, 'change', this.setTarget, this);
175: addEventHandler(this.urlinput, 'change', this.setSrc, this);
176: addEventHandler(this.hireslinkradio, 'click', this.setHires, this);
177: addEventHandler(this.linklinkradio, 'click', this.setNoHires, this);
178: addEventHandler(this.linkinput, 'keypress', this.setLink, this);
179: addEventHandler(this.linkinput, 'change', this.setLink, this);
180: addEventHandler(this.alignselect, 'change', this.setAlign, this);
181: addEventHandler(this.titleinput, 'change', this.setTitle, this);
182: this.targetinput.style.display = 'none';
183: this.editor.logMessage('Image tool initialized');
184: };
185:
186: SilvaImageTool.prototype.createContextMenuElements = function(selNode, event) {
187: return new Array(new ContextMenuElement('Create image', getImage, this));
188: };
189:
190: SilvaImageTool.prototype.selectTargetHandler = function(event) {
191: var select = this.targetselect;
192: var input = this.targetinput;
193:
194: var selvalue = select.options[select.selectedIndex].value;
195: if (selvalue != 'input') {
196: input.style.display = 'none';
197: } else {
198: input.style.display = 'inline';
199: };
200: };
201:
202: SilvaImageTool.prototype.updateState = function(selNode, event) {
203: var image = this.editor.getNearestParentOfType(selNode, 'img');
204: if (image) {
205: this.editel.style.display = 'block';
206: var src = image.getAttribute('src');
207: this.urlinput.value = src;
208: var target = image.getAttribute('target');
209: if (!target) {
210: this.targetselect.selectedIndex = 0;
211: this.targetinput.style.display = 'none';
212: } else {
213: var target_found = false;
214: for (var i=0; i < this.targetselect.options.length; i++) {
215: var option = this.targetselect.options[i];
216: if (option.value == target) {
217: this.targetselect.selectedIndex = i;
218: target_found = true;
219: break;
220: };
221: };
222: if (target_found) {
223: this.targetinput.value = '';
224: this.targetinput.style.display = 'none';
225: } else {
226: this.targetselect.selectedIndex = this.targetselect.options.length - 1;
227: this.targetinput.value = target;
228: this.targetinput.style.display = 'inline';
229: };
230: };
231: var hires = image.getAttribute('link_to_hires') == '1';
232: if (!hires) {
233: var link = image.getAttribute('link');
234: this.linklinkradio.checked = 'selected';
235: this.linkinput.value = link == null ? '' : link;
236: } else {
237: this.hireslinkradio.checked = 'checked';
238: this.linkinput.value = '';
239: };
240: if (this.toolboxel) {
241: this.toolboxel.className = this.activeclass;
242: };
243: var align = image.getAttribute('alignment');
244: if (!align) {
245: align = 'left';
246: };
247: var title = image.getAttribute('title');
248: if (!title) {
249: title = '';
250: };
251: this.titleinput.value = title;
252: selectSelectItem(this.alignselect, align);
253: } else {
254: this.editel.style.display = 'none';
255: this.urlinput.value = '';
256: this.titleinput.value = '';
257: if (this.toolboxel) {
258: this.toolboxel.className = this.plainclass;
259: };
260: this.targetselect.selectedIndex = 0;
261: this.targetinput.value = '';
262: this.targetinput.style.display = 'none';
263: };
264: };
265:
266: SilvaImageTool.prototype.setTarget = function() {
267: var target = this.targetselect.options[this.targetselect.selectedIndex].value;
268: if (target == 'input') {
269: target = this.targetinput.value;
270: };
271: var selNode = this.editor.getSelectedNode();
272: var image = this.editor.getNearestParentOfType(selNode, 'img');
273: if (!image) {
274: this.editor.logMessage('No image selected!', 1);
275: };
276: image.setAttribute('target', target);
277: };
278:
279: SilvaImageTool.prototype.setSrc = function() {
280: var selNode = this.editor.getSelectedNode();
281: var img = this.editor.getNearestParentOfType(selNode, 'img');
282: if (!img) {
283: this.editor.logMessage('Not inside an image!', 1);
284: };
285:
286: var src = this.urlinput.value;
287: img.setAttribute('src', src);
288: this.editor.logMessage('Image updated');
289: };
290:
291: SilvaImageTool.prototype.setHires = function() {
292: var selNode = this.editor.getSelectedNode();
293: var image = this.editor.getNearestParentOfType(selNode, 'img');
294: if (!image) {
295: this.editor.logMessage('No image selected!', 1);
296: return;
297: };
298: image.setAttribute('link_to_hires', '1');
299: image.removeAttribute('link');
300: this.linkinput.value = '';
301: };
302:
303: SilvaImageTool.prototype.setNoHires = function() {
304: var selNode = this.editor.getSelectedNode();
305: var image = this.editor.getNearestParentOfType(selNode, 'img');
306: if (!image) {
307: this.editor.logMessage('Not inside an image!', 1);
308: return;
309: };
310: var link = this.linkinput.value;
311: image.setAttribute('link_to_hires', '0');
312: image.setAttribute('link', link);
313: this.linklinkradio.setAttribute('selected', 'selected');
314: };
315:
316: SilvaImageTool.prototype.setLink = function() {
317: var link = this.linkinput.value;
318: var selNode = this.editor.getSelectedNode();
319: var image = this.editor.getNearestParentOfType(selNode, 'img');
320: if (!image) {
321: this.editor.logMessage('No image selected!', 1);
322: return;
323: };
324: image.setAttribute('link', link);
325: image.setAttribute('link_to_hires', '0');
326: };
327:
328: SilvaImageTool.prototype.setTitle = function() {
329: var selNode = this.editor.getSelectedNode();
330: var image = this.editor.getNearestParentOfType(selNode, 'img');
331: if (!image) {
332: this.editor.logMessage('No image selected!', 1);
333: return;
334: };
335: var title = this.titleinput.value;
336: image.setAttribute('title', title);
337: };
338:
339: SilvaImageTool.prototype.setAlign = function() {
340: var selNode = this.editor.getSelectedNode();
341: var image = this.editor.getNearestParentOfType(selNode, 'img');
342: if (!image) {
343: this.editor.logMessage('Not inside an image', 1);
344: return;
345: };
346: var align = this.alignselect.options[this.alignselect.selectedIndex].value;
347: image.setAttribute('alignment', align);
348: };
349:
350: function SilvaTableTool() {
351: /* Silva specific table functionality
352: overrides most of the table functionality, required because Silva requires
353: a completely different format for tables
354: */
355:
356: this.createTable = function(rows, cols, makeHeader, tableclass) {
357: /* add a Silvs specific table, with an (optional) header with colspan */
358: var doc = this.editor.getInnerDocument();
359:
360: var table = doc.createElement('table');
361: table.style.width = "100%";
362: table.className = tableclass;
363:
364: var tbody = doc.createElement('tbody');
365:
366: if (makeHeader) {
367: this._addRowHelper(doc, tbody, 'th', -1, cols);
368: }
369:
370: for (var i=0; i < rows; i++) {
371: this._addRowHelper(doc, tbody, 'td', -1, cols);
372: }
373:
374: table.appendChild(tbody);
375:
376: var iterator = new NodeIterator(table);
377: var currnode = null;
378: var contentcell = null;
379: while (currnode = iterator.next()) {
380: var nodename = currnode.nodeName.toLowerCase();
381: if (nodename == 'td' || nodename == 'th') {
382: contentcell = currnode;
383: break;
384: };
385: };
386:
387: var selection = this.editor.getSelection();
388: var docfrag = selection.cloneContents();
389: var setcursoratend = false;
390: if (contentcell && docfrag.hasChildNodes()) {
391: while (contentcell.hasChildNodes()) {
392: contentcell.removeChild(contentcell.firstChild);
393: };
394:
395: while (docfrag.hasChildNodes()) {
396: contentcell.appendChild(docfrag.firstChild);
397: setcursoratend = true;
398: };
399: };
400: this.editor.insertNodeAtSelection(table);
401:
402: this._setTableCellHandlers(table);
403:
404: this.editor.logMessage('Table added');
405: };
406:
407: this.addTableRow = function() {
408: /* add a table row or header */
409: var currnode = this.editor.getSelectedNode();
410: var doc = this.editor.getInnerDocument();
411: var tbody = this.editor.getNearestParentOfType(currnode, 'tbody');
412: if (!tbody) {
413: this.editor.logMessage('No table found!', 1);
414: return;
415: }
416: var cols = this._countCells(tbody);
417: var currrow = this.editor.getNearestParentOfType(currnode, 'tr');
418: if (!currrow) {
419: this.editor.logMessage('Not inside a row!', 1);
420: return;
421: };
422: var index = this._getRowIndex(currrow) + 1;
423: // should check what to add as well
424: this._addRowHelper(doc, tbody, 'td', index, cols);
425:
426: this.editor.logMessage('Table row added');
427: };
428:
429: this.delTableRow = function() {
430: /* delete a table row or header */
431: var currnode = this.editor.getSelectedNode();
432: var currtr = this.editor.getNearestParentOfType(currnode, 'tr');
433:
434: if (!currtr) {
435: this.editor.logMessage('Not inside a row!', 1);
436: return;
437: };
438:
439: currtr.parentNode.removeChild(currtr);
440:
441: this.editor.logMessage('Table row removed');
442: };
443:
444: this.addTableColumn = function() {
445: /* add a table column */
446: var currnode = this.editor.getSelectedNode();
447: var doc = this.editor.getInnerDocument();
448: var body = this.editor.getNearestParentOfType(currnode, 'tbody');
449: if (!body) {
450: this.editor.logMessage('Not inside a table!');
451: return;
452: };
453: var currcell = this.editor.getNearestParentOfType(currnode, 'td');
454: if (!currcell) {
455: var currcell = this.editor.getNearestParentOfType(currnode, 'th');
456: if (!currcell) {
457: this.editor.logMessage('Not inside a row!', 1);
458: return;
459: } else {
460: var index = -1;
461: };
462: } else {
463: var index = this._getColIndex(currcell) + 1;
464: };
465: var numcells = this._countCells(body);
466: this._addColHelper(doc, body, index, numcells);
467:
468: this.editor.logMessage('Column added');
469: };
470:
471: this.delTableColumn = function() {
472: /* delete a column */
473: var currnode = this.editor.getSelectedNode();
474: var body = this.editor.getNearestParentOfType(currnode, 'tbody');
475: if (!body) {
476: this.editor.logMessage('Not inside a table body!', 1);
477: return;
478: }
479: var currcell = this.editor.getNearestParentOfType(currnode, 'td');
480: if (!currcell) {
481: currcell = this.editor.getNearestParentOfType(currnode, 'th');
482: if (!currcell) {
483: this.editor.logMessage('Not inside a cell!');
484: return;
485: };
486: var index = -1;
487: } else {
488: var index = this._getColIndex(currcell);
489: };
490:
491: this._delColHelper(body, index);
492:
493: this.editor.logMessage('Column deleted');
494: };
495:
496: this.setColumnWidths = function(widths) {
497: /* sets relative column widths */
498: var selNode = this.editor.getSelectedNode();
499: var table = this.editor.getNearestParentOfType(selNode, 'table');
500:
501: // first remove all current width settings from the table
502: var iterator = new NodeIterator(table);
503: var currnode = null;
504: while (currnode = iterator.next()) {
505: if (currnode.nodeName.toLowerCase() == 'td') {
506: if (currnode.getAttribute('width')) {
507: currnode.removeAttribute('width');
508: } else if (currnode.style.width) {
509: delete currnode.style.width;
510: };
511: };
512: };
513:
514: var silva_column_info = new Array();
515: widths = widths.split(',');
516: for (var i=0; i < widths.length; i++) {
517: widths[i] = widths[i].strip();
518: silva_column_info.push('C:' + widths[i]);
519: widths[i] = parseInt(widths[i]);
520: };
521: silva_column_info = silva_column_info.join(' ');
522: table.setAttribute('silva_column_info', silva_column_info);
523:
524: // now convert the relative widths to percentages
525: // first find the first row containing cells
526: var totalunits = 0;
527: for (var i=0; i < widths.length; i++) {
528: totalunits += widths[i];
529: };
530: var iterator = new NodeIterator(table);
531: var currnode = null;
532: var row = null;
533: while (currnode = iterator.next()) {
534: if (currnode.nodeName.toLowerCase() == 'td') {
535: row = currnode.parentNode;
536: break;
537: };
538: };
539: var iterator = new NodeIterator(row);
540: var percent_per_unit = 100.0 / totalunits;
541: var currcell = null;
542: for (var i=0; i < widths.length; i++) {
543: while (currcell = iterator.next()) {
544: if (currcell.nodeName.toLowerCase() == 'td') {
545: currcell.setAttribute('width', '' + (widths[i] * percent_per_unit) + '%');
546: break;
547: };
548: };
549: };
550: };
551:
552: this.getColumnWidths = function(table) {
553: var silvacolinfo = table.getAttribute('silva_column_info');
554: var widths = new Array();
555: if (!silvacolinfo) {
556: var body = null;
557: var iterator = new NodeIterator(table);
558: var body = iterator.next();
559: while (body.nodeName.toLowerCase() != 'tbody') {
560: body = iterator.next();
561: };
562: var numcols = this._countCells(body);
563: for (var i=0; i < numcols; i++) {
564: widths.push(1);
565: };
566: } else {
567: silvacolinfo = silvacolinfo.split(' ');
568: for (var i=0; i < silvacolinfo.length; i++) {
569: var pair = silvacolinfo[i].split(':');
570: widths.push(parseInt(pair[1]));
571: };
572: widths = this._factorWidths(widths);
573: };
574: return widths;
575: };
576:
577: this._factorWidths = function(widths) {
578: var highest = 0;
579: for (var i=0; i < widths.length; i++) {
580: if (widths[i] > highest) {
581: highest = widths[i];
582: };
583: };
584: var factor = 1;
585: for (var i=0; i < highest; i++) {
586: var testnum = highest - i;
587: var isfactor = true;
588: for (var j=0; j < widths.length; j++) {
589: if (widths[j] % testnum != 0) {
590: isfactor = false;
591: break;
592: };
593: };
594: if (isfactor) {
595: factor = testnum;
596: break;
597: };
598: };
599: if (factor > 1) {
600: for (var i=0; i < widths.length; i++) {
601: widths[i] = widths[i] / factor;
602: };
603: };
604: return widths;
605: };
606:
607: this._addRowHelper = function(doc, body, celltype, index, numcells) {
608: /* actually adds a row to the table */
609: var row = doc.createElement('tr');
610:
611: // fill the row with cells
612: if (celltype == 'td') {
613: for (var i=0; i < numcells; i++) {
614: var cell = doc.createElement(celltype);
615: var nbsp = doc.createTextNode("\u00a0");
616: cell.appendChild(nbsp);
617: row.appendChild(cell);
618: }
619: } else if (celltype == 'th') {
620: var cell = doc.createElement(celltype);
621: cell.setAttribute('colSpan', numcells);
622: var nbsp = doc.createTextNode("\u00a0");
623: cell.appendChild(nbsp);
624: row.appendChild(cell);
625: }
626:
627: // now append it to the tbody
628: var rows = this._getAllRows(body);
629: if (index == -1 || index >= rows.length) {
630: body.appendChild(row);
631: } else {
632: var nextrow = rows[index];
633: body.insertBefore(row, nextrow);
634: }
635:
636: return row;
637: };
638:
639: this._addColHelper = function(doc, body, index, numcells) {
640: /* actually adds a column to a table */
641: var rows = this._getAllRows(body);
642: for (var i=0; i < rows.length; i++) {
643: var row = rows[i];
644: var cols = this._getAllColumns(row);
645: var col = cols[0];
646: if (col.nodeName.toLowerCase() == 'th') {
647: var colspan = col.getAttribute('colSpan');
648: if (colspan) {
649: colspan = parseInt(colspan);
650: } else {
651: colspan = 1;
652: }
653: col.setAttribute('colSpan', colspan + 1);
654: } else {
655: var cell = doc.createElement('td');
656: var nbsp = doc.createTextNode('\u00a0');
657: cell.appendChild(nbsp);
658: if (index == -1 || index >= rows.length) {
659: row.appendChild(cell);
660: } else {
661: row.insertBefore(cell, cols[index]);
662: };
663: };
664: };
665: };
666:
667: this._delColHelper = function(body, index) {
668: /* actually delete all cells in a column */
669: var rows = this._getAllRows(body);
670: for (var i=0; i < rows.length; i++) {
671: var row = rows[i];
672: var cols = this._getAllColumns(row);
673: if (cols[0].nodeName.toLowerCase() == 'th') {
674: // is a table header, so reduce colspan
675: var th = cols[0];
676: var colspan = th.getAttribute('colSpan');
677: if (!colspan || colspan == '1') {
678: body.removeChild(row);
679: } else {
680: colspan = parseInt(colspan);
681: th.setAttribute('colSpan', colspan - 1);
682: };
683: } else {
684: // is a table cell row, remove one
685: if (index > -1) {
686: row.removeChild(cols[index]);
687: } else {
688: row.removeChild(cols[cols.length - 1]);
689: }
690: }
691: };
692: };
693:
694: this._getRowIndex = function(row) {
695: /* get the current rowindex */
696: var rowindex = 0;
697: var prevrow = row.previousSibling;
698: while (prevrow) {
699: if (prevrow.nodeName.toLowerCase() == 'tr') {
700: rowindex++;
701: };
702: prevrow = prevrow.previousSibling;
703: };
704: return rowindex;
705: };
706:
707: this._countCells = function(body) {
708: /* get the current column index */
709: var numcols = 0;
710: var cols = this._getAllColumns(this._getAllRows(body)[0]);
711: for (var i=0; i < cols.length; i++) {
712: var node = cols[i];
713: if (node.nodeName.toLowerCase() == 'th') {
714: var colspan = node.getAttribute('colSpan');
715: if (colspan) {
716: colspan = parseInt(colspan);
717: } else {
718: colspan = 1;
719: };
720: numcols += colspan;
721: } else {
722: numcols++;
723: };
724: };
725: return numcols;
726: };
727:
728: this._getAllRows = function(body) {
729: /* returns an Array of all available rows */
730: var rows = new Array();
731: for (var i=0; i < body.childNodes.length; i++) {
732: var node = body.childNodes[i];
733: if (node.nodeName.toLowerCase() == 'tr') {
734: rows.push(node);
735: };
736: };
737: return rows;
738: };
739:
740: this._getAllColumns = function(row) {
741: /* returns an Array of all columns in a row */
742: var cols = new Array();
743: for (var i=0; i < row.childNodes.length; i++) {
744: var node = row.childNodes[i];
745: if (node.nodeName.toLowerCase() == 'td' ||
746: node.nodeName.toLowerCase() == 'th') {
747: cols.push(node);
748: };
749: };
750: return cols;
751: };
752: }
753:
754: SilvaTableTool.prototype = new TableTool;
755:
756: function SilvaTableToolBox(addtabledivid, edittabledivid, newrowsinputid,
757: newcolsinputid, makeheaderinputid, classselectid, alignselectid, widthinputid,
758: addtablebuttonid, addrowbuttonid, delrowbuttonid, addcolbuttonid, delcolbuttonid,
759: fixbuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
760: /* Silva specific table functionality
761: overrides most of the table functionality, required because Silva requires
762: a completely different format for tables
763: */
764:
765: this.addtablediv = getFromSelector(addtabledivid);
766: this.edittablediv = getFromSelector(edittabledivid);
767: this.newrowsinput = getFromSelector(newrowsinputid);
768: this.newcolsinput = getFromSelector(newcolsinputid);
769: this.makeheaderinput = getFromSelector(makeheaderinputid);
770: this.classselect = getFromSelector(classselectid);
771: this.alignselect = getFromSelector(alignselectid);
772: this.widthinput = getFromSelector(widthinputid);
773: this.addtablebutton = getFromSelector(addtablebuttonid);
774: this.addrowbutton = getFromSelector(addrowbuttonid);
775: this.delrowbutton = getFromSelector(delrowbuttonid);
776: this.addcolbutton = getFromSelector(addcolbuttonid);
777: this.delcolbutton = getFromSelector(delcolbuttonid);
778: this.fixbutton = getFromSelector(fixbuttonid);
779: this.delbutton = getFromSelector(delbuttonid);
780: this.toolboxel = getFromSelector(toolboxid);
781: this.plainclass = plainclass;
782: this.activeclass = activeclass;
783:
784: this.initialize = function(tool, editor) {
785: /* attach the event handlers */
786: this.tool = tool;
787: this.editor = editor;
788: addEventHandler(this.addtablebutton, "click", this.addTable, this);
789: addEventHandler(this.addrowbutton, "click", this.tool.addTableRow, this.tool);
790: addEventHandler(this.delrowbutton, "click", this.tool.delTableRow, this.tool);
791: addEventHandler(this.addcolbutton, "click", this.tool.addTableColumn, this.tool);
792: addEventHandler(this.delcolbutton, "click", this.tool.delTableColumn, this.tool);
793: addEventHandler(this.fixbutton, "click", this.fixTable, this);
794: addEventHandler(this.delbutton, "click", this.tool.delTable, this);
795: addEventHandler(this.alignselect, "change", this.setColumnAlign, this);
796: addEventHandler(this.classselect, "change", this.setTableClass, this);
797: addEventHandler(this.widthinput, "change", this.setColumnWidths, this);
798: this.addtablediv.style.display = "block";
799: this.edittablediv.style.display = "none";
800: this.editor.logMessage('Table tool initialized');
801: };
802:
803: this.updateState = function(selNode) {
804: /* update the state (add/edit) and update the pulldowns (if required) */
805: var table = this.editor.getNearestParentOfType(selNode, 'table');
806: if (table) {
807: this.addtablediv.style.display = "none";
808: this.edittablediv.style.display = "block";
809: var td = this.editor.getNearestParentOfType(selNode, 'td');
810: if (!td) {
811: td = this.editor.getNearestParentOfType(selNode, 'th');
812: this.widthinput.value = '';
813: } else {
814: this.widthinput.value = this.tool.getColumnWidths(table);
815: };
816: if (td) {
817: var align = td.getAttribute('align');
818: if (this.editor.config.use_css) {
819: align = td.style.textAlign;
820: };
821: selectSelectItem(this.alignselect, align);
822: };
823: selectSelectItem(this.classselect, table.className);
824: if (this.toolboxel) {
825: this.toolboxel.className = this.activeclass;
826: };
827: } else {
828: this.edittablediv.style.display = "none";
829: this.addtablediv.style.display = "block";
830: this.alignselect.selectedIndex = 0;
831: this.classselect.selectedIndex = 0;
832: if (this.toolboxel) {
833: this.toolboxel.className = this.plainclass;
834: };
835: };
836: };
837:
838: this.addTable = function() {
839: /* add a Silvs specific table, with an (optional) header with colspan */
840: var rows = parseInt(this.newrowsinput.value);
841: var cols = parseInt(this.newcolsinput.value);
842: var makeHeader = this.makeheaderinput.checked;
843: var classchooser = getFromSelector("kupu-table-classchooser-add");
844: var tableclass = this.classselect.options[this.classselect.selectedIndex].value;
845: this.tool.createTable(rows, cols, makeHeader, tableclass);
846: };
847:
848: this.setTableClass = function() {
849: var cls = this.classselect.options[this.classselect.selectedIndex].value;
850: this.tool.setTableClass(cls);
851: };
852:
853: this.setColumnWidths = function() {
854: var widths = this.widthinput.value;
855: this.tool.setColumnWidths(widths);
856: };
857:
858: this.fixTable = function(event) {
859: /* fix the table so it is Silva (and this tool) compliant */
860: // since this can be quite a nasty creature we can't just use the
861: // helper methods
862:
863: // first we create a new tbody element
864: var currnode = this.editor.getSelectedNode();
865: var table = this.editor.getNearestParentOfType(currnode, 'TABLE');
866: if (!table) {
867: this.editor.logMessage('Not inside a table!');
868: return;
869: };
870: var doc = this.editor.getInnerDocument();
871: var tbody = doc.createElement('tbody');
872:
873: var allowed_classes = new Array('plain', 'grid', 'list', 'listing', 'data');
874: if (!allowed_classes.contains(table.getAttribute('class'))) {
875: table.setAttribute('class', 'plain');
876: };
877:
878: table.setAttribute('cellpadding', '0');
879: table.setAttribute('cellspacing', '0');
880:
881: // now get all the rows of the table, the rows can either be
882: // direct descendants of the table or inside a 'tbody', 'thead'
883: // or 'tfoot' element
884: var rows = new Array();
885: var parents = new Array('thead', 'tbody', 'tfoot');
886: for (var i=0; i < table.childNodes.length; i++) {
887: var node = table.childNodes[i];
888: if (node.nodeName.toLowerCase() == 'tr') {
889: rows.push(node);
890: } else if (parents.contains(node.nodeName.toLowerCase())) {
891: for (var j=0; j < node.childNodes.length; j++) {
892: var inode = node.childNodes[j];
893: if (inode.nodeName.toLowerCase() == 'tr') {
894: rows.push(inode);
895: };
896: };
897: };
898: };
899:
900: // now find out how many cells our rows should have
901: var numcols = 0;
902: for (var i=0; i < rows.length; i++) {
903: var row = rows[i];
904: var currnumcols = 0;
905: for (var j=0; j < row.childNodes.length; j++) {
906: var node = row.childNodes[j];
907: if (node.nodeName.toLowerCase() == 'td' ||
908: node.nodeName.toLowerCase() == 'th') {
909: var colspan = 1;
910: if (node.getAttribute('colSpan')) {
911: colspan = parseInt(node.getAttribute('colSpan'));
912: };
913: currnumcols += colspan;
914: };
915: };
916: if (currnumcols > numcols) {
917: numcols = currnumcols;
918: };
919: };
920:
921: // now walk through all rows to clean them up
922: for (var i=0; i < rows.length; i++) {
923: var row = rows[i];
924: var newrow = doc.createElement('tr');
925: var currcolnum = 0;
926: var inhead = -1;
927: while (row.childNodes.length > 0) {
928: var node = row.childNodes[0];
929: if (node.nodeName.toLowerCase() == 'td') {
930: if (inhead == -1) {
931: inhead = 0;
932: node.setAttribute('colSpan', '1');
933: };
934: } else if (node.nodeName.toLowerCase() == 'th') {
935: if (inhead == -1) {
936: inhead = 1;
937: newrow.appendChild(node);
938: node.setAttribute('colSpan', '1');
939: node.setAttribute('rowSpan', '1');
940: continue;
941: } else if (inhead == 0) {
942: var td = doc.createElement('td');
943: while (node.childNodes.length) {
944: td.appendChild(node.childNodes[0]);
945: };
946: row.removeChild(node);
947: node = td;
948: };
949: } else {
950: row.removeChild(node);
951: continue;
952: };
953: node.setAttribute('rowspan', '1');
954: if (inhead) {
955: while (node.childNodes.length) {
956: newrow.childNodes[0].appendChild(node.childNodes[0]);
957: };
958: var colspan = node.getAttribute('colSpan');
959: if (colspan) {
960: colspan = parseInt(colspan);
961: } else {
962: colspan = 1;
963: }
964: var current_colspan = parseInt(newrow.childNodes[0].getAttribute('colSpan'));
965: newrow.childNodes[0].setAttribute('colSpan', (current_colspan + colspan).toString());
966: row.removeChild(node);
967: } else {
968: node.setAttribute('colSpan', 1);
969: node.setAttribute('rowSpan', 1);
970: newrow.appendChild(node);
971: };
972: };
973: if (newrow.childNodes.length) {
974: tbody.appendChild(newrow);
975: };
976: };
977:
978: // now make sure all rows have the correct length
979: for (var i=0; i < tbody.childNodes.length; i++) {
980: var row = tbody.childNodes[i];
981: if (row.childNodes.length && row.childNodes[0].nodeName.toLowerCase() == 'th') {
982: row.childNodes[0].setAttribute('colSpan', numcols);
983: } else {
984: while (row.childNodes.length < numcols) {
985: var td = doc.createElement('td');
986: var nbsp = doc.createTextNode('\u00a0');
987: td.appendChild(nbsp);
988: row.appendChild(td);
989: };
990: };
991: };
992:
993: // now remove all the old stuff from the table and add the new tbody
994: var tlength = table.childNodes.length;
995: for (var i=0; i < tlength; i++) {
996: table.removeChild(table.childNodes[0]);
997: };
998: table.appendChild(tbody);
999:
1000: this.editor.getDocument().getWindow().focus();
1001:
1002: this.editor.logMessage('Table cleaned up');
1003: };
1004:
1005: this._fixAllTables = function() {
1006: /* fix all the tables in the document at once */
1007: return;
1008: var tables = this.editor.getInnerDocument().getElementsByTagName('table');
1009: for (var i=0; i < tables.length; i++) {
1010: this.fixTable(tables[i]);
1011: };
1012: };
1013: }
1014:
1015: SilvaTableToolBox.prototype = new TableToolBox;
1016:
1017: function SilvaIndexTool(inputid, addbuttonid, updatebuttonid, deletebuttonid, toolboxid, plainclass, activeclass) {
1018: /* a tool to manage index items (named anchors) for Silva */
1019: this.input = getFromSelector(inputid);
1020: this.addbutton = getFromSelector(addbuttonid);
1021: this.updatebutton = getFromSelector(updatebuttonid);
1022: this.deletebutton = getFromSelector(deletebuttonid);
1023: this.toolboxel = getFromSelector(toolboxid);
1024: this.plainclass = plainclass;
1025: this.activeclass = activeclass;
1026:
1027: this.initialize = function(editor) {
1028: /* attach the event handlers */
1029: this.editor = editor;
1030: addEventHandler(this.input, 'blur', this.updateIndex, this);
1031: addEventHandler(this.addbutton, 'click', this.addIndex, this);
1032: addEventHandler(this.updatebutton, 'click', this.updateIndex, this);
1033: addEventHandler(this.deletebutton, 'click', this.deleteIndex, this);
1034: if (this.editor.getBrowserName() == 'IE') {
1035: // need to catch some additional events for IE
1036: addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnIndex, this);
1037: addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnIndex, this);
1038: };
1039: addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnIndex, this);
1040: this.updatebutton.style.display = 'none';
1041: this.deletebutton.style.display = 'none';
1042: };
1043:
1044: this.addIndex = function(event) {
1045: /* create an index */
1046: var name = this.input.value;
1047: var currnode = this.editor.getSelectedNode();
1048: var indexel = this.editor.getNearestParentOfType(currnode, 'A');
1049:
1050: if (indexel && indexel.getAttribute('href')) {
1051: this.editor.logMessage('Can not add index items in anchors');
1052: return;
1053: };
1054:
1055: if (!indexel) {
1056: var doc = this.editor.getDocument();
1057: if (!name) {
1058: var selection = this.editor.getSelection();
1059: var cloned = selection.cloneContents();
1060: var iterator = new NodeIterator(cloned);
1061: var name = '';
1062: var currnode = null;
1063: while (currnode = iterator.next()) {
1064: if (currnode.nodeValue) {
1065: name += currnode.nodeValue;
1066: };
1067: };
1068: if (name) {
1069: this.input.value = name;
1070: };
1071: };
1072: var docel = doc.getDocument();
1073: indexel = docel.createElement('a');
1074: var text = docel.createTextNode('[' + name + ']');
1075: indexel.appendChild(text);
1076: indexel = this.editor.insertNodeAtSelection(indexel, true);
1077: indexel.className = 'index';
1078: };
1079:
1080: indexel.setAttribute('name', name);
1081: var sel = this.editor.getSelection();
1082: sel.collapse(true);
1083: this.editor.logMessage('Index added');
1084: };
1085:
1086: this.updateIndex = function(event) {
1087: /* update an existing index */
1088: var currnode = this.editor.getSelectedNode();
1089: var indexel = this.editor.getNearestParentOfType(currnode, 'A');
1090: if (!indexel) {
1091: return;
1092: };
1093:
1094: if (indexel && indexel.getAttribute('href')) {
1095: this.editor.logMessage('Can not add an index element inside a link!');
1096: return;
1097: };
1098:
1099: var name = this.input.value;
1100: indexel.setAttribute('name', name);
1101: while (indexel.hasChildNodes()) {
1102: indexel.removeChild(indexel.firstChild);
1103: };
1104: var text = this.editor.getInnerDocument().createTextNode('[' + name + ']')
1105: indexel.appendChild(text);
1106: this.editor.logMessage('Index modified');
1107: };
1108:
1109: this.deleteIndex = function() {
1110: var selNode = this.editor.getSelectedNode();
1111: var a = this.editor.getNearestParentOfType(selNode, 'a');
1112: if (!a || a.getAttribute('href')) {
1113: this.editor.logMessage('Not inside an index element!');
1114: return;
1115: };
1116: a.parentNode.removeChild(a);
1117: this.editor.logMessage('Index element removed');
1118: };
1119:
1120: this.handleKeyPressOnIndex = function(event) {
1121: var selNode = this.editor.getSelectedNode();
1122: var a = this.editor.getNearestParentOfType(selNode, 'a');
1123: if (!a || a.getAttribute('href')) {
1124: return;
1125: };
1126: var keyCode = event.keyCode;
1127: if (keyCode == 8 || keyCode == 46) {
1128: a.parentNode.removeChild(a);
1129: } else if (keyCode == 9 || keyCode == 39) {
1130: var next = a.nextSibling;
1131: while (next && next.nodeName.toLowerCase() == 'br') {
1132: next = next.nextSibling;
1133: };
1134: if (!next) {
1135: var doc = this.editor.getInnerDocument();
1136: next = doc.createTextNode('\xa0');
1137: a.parentNode.appendChild(next);
1138: };
1139: var selection = this.editor.getSelection();
1140: // XXX I fear I'm working around bugs here... because of a bug in
1141: // selection.moveStart() I can't use the same codepath in IE as in Moz
1142: if (this.editor.getBrowserName() == 'IE') {
1143: selection.selectNodeContents(a);
1144: // XXX are we depending on a bug here? shouldn't we move the
1145: // selection one place to get out of the anchor? it works,
1146: // but seems wrong...
1147: selection.collapse(true);
1148: } else {
1149: selection.selectNodeContents(next);
1150: selection.collapse();
1151: var selection = this.editor.getSelection();
1152: };
1153: this.editor.updateState();
1154: };
1155: if (event.preventDefault) {
1156: event.preventDefault();
1157: } else {
1158: event.returnValue = false;
1159: };
1160: return false;
1161: };
1162:
1163: this.updateState = function(selNode) {
1164: var indexel = this.editor.getNearestParentOfType(selNode, 'A');
1165: if (indexel && !indexel.getAttribute('href')) {
1166: if (this.toolboxel) {
1167: this.toolboxel.className = this.activeclass;
1168: };
1169: this.input.value = indexel.getAttribute('name');
1170: this.addbutton.style.display = 'none';
1171: this.updatebutton.style.display = 'inline';
1172: this.deletebutton.style.display = 'inline';
1173: } else {
1174: if (this.toolboxel) {
1175: this.toolboxel.className = this.plainclass;
1176: };
1177: this.input.value = '';
1178: this.updatebutton.style.display = 'none';
1179: this.deletebutton.style.display = 'none';
1180: this.addbutton.style.display = 'inline';
1181: };
1182: };
1183:
1184: this.createContextMenuElements = function(selNode, event) {
1185: var indexel = this.editor.getNearestParentOfType(selNode, 'A');
1186: if (indexel && !indexel.getAttribute('href')) {
1187: return new Array(new ContextMenuElement('Delete index', this.deleteIndex, this));
1188: } else {
1189: return new Array();
1190: };
1191: };
1192: };
1193:
1194: SilvaIndexTool.prototype = new KupuTool;
1195:
1196: function SilvaTocTool(depthselectid, addbuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
1197: this.depthselect = getFromSelector(depthselectid);
1198: this.addbutton = getFromSelector(addbuttonid);
1199: this.delbutton = getFromSelector(delbuttonid);
1200: this.toolbox = getFromSelector(toolboxid);
1201: this.plainclass = plainclass;
1202: this.activeclass = activeclass;
1203: this._inside_toc = false;
1204:
1205: this.initialize = function(editor) {
1206: this.editor = editor;
1207: addEventHandler(this.addbutton, 'click', this.addOrUpdateToc, this);
1208: addEventHandler(this.depthselect, 'change', this.updateToc, this);
1209: addEventHandler(this.delbutton, 'click', this.deleteToc, this);
1210: addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnToc, this);
1211: if (this.editor.getBrowserName() == 'IE') {
1212: addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnToc, this);
1213: addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnToc, this);
1214: };
1215: };
1216:
1217: this.handleKeyPressOnToc = function(event) {
1218: if (!this._inside_toc) {
1219: return;
1220: };
1221: var keyCode = event.keyCode;
1222: if (keyCode == 8 || keyCode == 46) {
1223: var selNode = this.editor.getSelectedNode();
1224: var toc = this.getNearestToc(selNode);
1225: toc.parentNode.removeChild(toc);
1226: };
1227: if (keyCode == 13 || keyCode == 9 || keyCode == 39) {
1228: var selNode = this.editor.getSelectedNode();
1229: var toc = this.getNearestToc(selNode);
1230: var doc = this.editor.getInnerDocument();
1231: var selection = this.editor.getSelection();
1232: if (toc.nextSibling) {
1233: var sibling = toc.nextSibling;
1234: selection.selectNodeContents(toc.nextSibling);
1235: selection.collapse();
1236: } else {
1237: var parent = toc.parentNode;
1238: var p = doc.createElement('p');
1239: parent.appendChild(p);
1240: var text = doc.createTextNode('\xa0');
1241: p.appendChild(text);
1242: selection.selectNodeContents(p);
1243: };
1244: this._inside_toc = false;
1245: };
1246: if (event.preventDefault) {
1247: event.preventDefault();
1248: } else {
1249: event.returnValue = false;
1250: };
1251: };
1252:
1253: this.updateState = function(selNode, event) {
1254: var toc = this.getNearestToc(selNode);
1255: if (toc) {
1256: var depth = toc.getAttribute('toc_depth');
1257: selectSelectItem(this.depthselect, depth);
1258: this.addbutton.style.display = 'none';
1259: this.delbutton.style.display = 'inline';
1260: this._inside_toc = true;
1261: if (this.toolbox) {
1262: this.toolbox.className = this.activeclass;
1263: };
1264: } else {
1265: this.depthselect.selectedIndex = 0;
1266: this.delbutton.style.display = 'none';
1267: this.addbutton.style.display = 'inline';
1268: this._inside_toc = false;
1269: if (this.toolbox) {
1270: this.toolbox.className = this.plainclass;
1271: };
1272: };
1273: };
1274:
1275: this.addOrUpdateToc = function(event, depth) {
1276: var selNode = this.editor.getSelectedNode();
1277: var depth = depth ? depth : this.depthselect.options[this.depthselect.selectedIndex].value;
1278: var toc = this.getNearestToc(selNode);
1279: var doc = this.editor.getInnerDocument();
1280: var toctext = this.getTocText(depth);
1281: if (toc) {
1282: // there's already a toc, just update the depth
1283: toc.setAttribute('toc_depth', depth);
1284: while (toc.hasChildNodes()) {
1285: toc.removeChild(toc.firstChild);
1286: };
1287: toc.appendChild(doc.createTextNode(toctext));
1288: } else {
1289: // create a new toc
1290: var div = doc.createElement('div');
1291: div.setAttribute('toc_depth', depth);
1292: div.setAttribute('is_toc', 1);
1293: div.className = 'toc';
1294: var text = doc.createTextNode(toctext);
1295: div.appendChild(text);
1296: this.editor.insertNodeAtSelection(div);
1297: };
1298: };
1299:
1300: this.createDefaultToc = function() {
1301: // XXX nasty workaround, entering null as the event...
1302: this.addOrUpdateToc(null, '-1');
1303: };
1304:
1305: this.updateToc = function() {
1306: var selNode = this.editor.getSelectedNode();
1307: var toc = this.getNearestToc(selNode);
1308: if (toc) {
1309: var depth = this.depthselect.options[this.depthselect.selectedIndex].value;
1310: var toctext = this.getTocText(depth);
1311: toc.setAttribute('toc_depth', depth);
1312: while (toc.hasChildNodes()) {
1313: toc.removeChild(toc.firstChild);
1314: };
1315: doc = this.editor.getInnerDocument();
1316: toc.appendChild(doc.createTextNode(toctext));
1317: };
1318: };
1319:
1320: this.deleteToc = function() {
1321: var selNode = this.editor.getSelectedNode();
1322: var toc = this.getNearestToc(selNode);
1323: if (!toc) {
1324: this.editor.logMessage('Not inside a toc!', 1);
1325: return;
1326: };
1327: toc.parentNode.removeChild(toc);
1328: };
1329:
1330: this.getNearestToc = function(selNode) {
1331: var currnode = selNode;
1332: while (currnode) {
1333: if (currnode.nodeName.toLowerCase() == 'div' &&
1334: currnode.getAttribute('is_toc')) {
1335: return currnode;
1336: };
1337: currnode = currnode.parentNode;
1338: };
1339: return false;
1340: };
1341:
1342: this.createContextMenuElements = function(selNode, event) {
1343: /* create the 'Delete TOC' menu elements */
1344: var ret = new Array();
1345: if (this.getNearestToc(selNode)) {
1346: ret.push(new ContextMenuElement('Delete TOC', this.deleteToc, this));
1347: } else {
1348: ret.push(new ContextMenuElement('Create TOC', this.createDefaultToc, this));
1349: };
1350: return ret;
1351: };
1352:
1353: this.getTocText = function(depth) {
1354: var toctext = 'Table of Contents ';
1355: switch (depth) {
1356: case '-1':
1357: toctext += '(unlimited levels)';
1358: break;
1359: case '1':
1360: toctext += '(1 level)';
1361: break;
1362: default:
1363: toctext += '(' + depth + ' levels)';
1364: break;
1365: };
1366: return toctext;
1367: };
1368: };
1369:
1370: SilvaTocTool.prototype = new KupuTool;
1371:
1372: function SilvaAbbrTool(abbrradioid, acronymradioid, radiocontainerid, titleinputid,
1373: addbuttonid, updatebuttonid, delbuttonid,
1374: toolboxid, plainclass, activeclass) {
1375: /* tool to manage citation elements */
1376: this.abbrradio = getFromSelector(abbrradioid);
1377: this.acronymradio = getFromSelector(acronymradioid);
1378: this.radiocontainer = getFromSelector(radiocontainerid);
1379: this.titleinput = getFromSelector(titleinputid);
1380: this.addbutton = getFromSelector(addbuttonid);
1381: this.updatebutton = getFromSelector(updatebuttonid);
1382: this.delbutton = getFromSelector(delbuttonid);
1383: this.toolbox = getFromSelector(toolboxid);
1384: this.plainclass = plainclass;
1385: this.activeclass = activeclass;
1386:
1387: this.initialize = function(editor) {
1388: this.editor = editor;
1389: addEventHandler(this.addbutton, 'click', this.addElement, this);
1390: addEventHandler(this.updatebutton, 'click', this.updateElement, this);
1391: addEventHandler(this.delbutton, 'click', this.deleteElement, this);
1392:
1393: this.updatebutton.style.display = 'none';
1394: this.delbutton.style.display = 'none';
1395: };
1396:
1397: this.updateState = function(selNode, event) {
1398: var element = this.getNearestAbbrAcronym(selNode);
1399: if (element) {
1400: this.addbutton.style.display = 'none';
1401: this.updatebutton.style.display = 'inline';
1402: this.delbutton.style.display = 'inline';
1403: this.titleinput.value = element.getAttribute('title');
1404: this.radiocontainer.style.display = 'none';
1405: if (this.toolbox) {
1406: this.toolbox.className = this.activeclass;
1407: };
1408: } else {
1409: this.addbutton.style.display = 'inline';
1410: this.updatebutton.style.display = 'none';
1411: this.delbutton.style.display = 'none';
1412: this.titleinput.value = '';
1413: if (this.editor.getBrowserName() == 'IE' || this.radiocontainer.nodeName.toLowerCase() != 'tr') {
1414: this.radiocontainer.style.display = 'block';
1415: } else {
1416: this.radiocontainer.style.display = 'table-row';
1417: };
1418: if (this.toolbox) {
1419: this.toolbox.className = this.plainclass;
1420: };
1421: };
1422: };
1423:
1424: this.getNearestAbbrAcronym = function(selNode) {
1425: var current = selNode;
1426: while (current && current.nodeType != 9) {
1427: if (current.nodeType == 1) {
1428: var nodeName = current.nodeName.toLowerCase();
1429: if (nodeName == 'abbr' || nodeName == 'acronym') {
1430: return current;
1431: };
1432: };
1433: current = current.parentNode;
1434: };
1435: };
1436:
1437: this.addElement = function() {
1438: var type = this.abbrradio.checked ? 'abbr' : 'acronym';
1439: var doc = this.editor.getInnerDocument();
1440: var selNode = this.editor.getSelectedNode();
1441: if (this.getNearestAbbrAcronym(selNode)) {
1442: this.editor.logMessage('Can not nest abbr and acronym elements');
1443: return;
1444: };
1445: var element = doc.createElement(type);
1446: element.setAttribute('title', this.titleinput.value);
1447:
1448: var selection = this.editor.getSelection();
1449: var docfrag = selection.cloneContents();
1450: var placecursoratend = false;
1451: if (docfrag.hasChildNodes()) {
1452: for (var i=0; i < docfrag.childNodes.length; i++) {
1453: element.appendChild(docfrag.childNodes[i]);
1454: };
1455: placecursoratend = true;
1456: } else {
1457: var text = doc.createTextNode('\xa0');
1458: element.appendChild(text);
1459: };
1460: this.editor.insertNodeAtSelection(element, 1);
1461: var selection = this.editor.getSelection();
1462: selection.collapse(placecursoratend);
1463: this.editor.getDocument().getWindow().focus();
1464: var selNode = selection.getSelectedNode();
1465: this.editor.updateState(selNode);
1466: this.editor.logMessage('Element ' + type + ' added');
1467: };
1468:
1469: this.updateElement = function() {
1470: var selNode = this.editor.getSelectedNode();
1471: var element = this.getNearestAbbrAcronym(selNode);
1472: if (!element) {
1473: this.editor.logMessage('Not inside an abbr or acronym element!', 1);
1474: return;
1475: };
1476: var title = this.titleinput.value;
1477: element.setAttribute('title', title);
1478: this.editor.logMessage('Updated ' + element.nodeName.toLowerCase() + ' element');
1479: };
1480:
1481: this.deleteElement = function() {
1482: var selNode = this.editor.getSelectedNode();
1483: var element = this.getNearestAbbrAcronym(selNode);
1484: if (!element) {
1485: this.editor.logMessage('Not inside an abbr or acronym element!', 1);
1486: return;
1487: };
1488: element.parentNode.removeChild(element);
1489: this.editor.logMessage('Deleted ' + element.nodeName.toLowerCase() + ' deleted');
1490: };
1491: };
1492:
1493: SilvaAbbrTool.prototype = new KupuTool;
1494:
1495: function SilvaCitationTool(authorinputid, sourceinputid, addbuttonid, updatebuttonid, delbuttonid,
1496: toolboxid, plainclass, activeclass) {
1497: /* tool to manage citation elements */
1498: this.authorinput = getFromSelector(authorinputid);
1499: this.sourceinput = getFromSelector(sourceinputid);
1500: this.addbutton = getFromSelector(addbuttonid);
1501: this.updatebutton = getFromSelector(updatebuttonid);
1502: this.delbutton = getFromSelector(delbuttonid);
1503: this.toolbox = getFromSelector(toolboxid);
1504: this.plainclass = plainclass;
1505: this.activeclass = activeclass;
1506: this._inside_citation = false;
1507:
1508: this.initialize = function(editor) {
1509: this.editor = editor;
1510: addEventHandler(this.addbutton, 'click', this.addCitation, this);
1511: addEventHandler(this.updatebutton, 'click', this.updateCitation, this);
1512: addEventHandler(this.delbutton, 'click', this.deleteCitation, this);
1513: if (editor.getBrowserName() == 'IE') {
1514: addEventHandler(editor.getInnerDocument(), 'keyup', this.cancelEnterPress, this);
1515: addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnCitation, this);
1516: } else {
1517: addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnCitation, this);
1518: };
1519:
1520: this.updatebutton.style.display = 'none';
1521: this.delbutton.style.display = 'none';
1522: };
1523:
1524: this.cancelEnterPress = function(event) {
1525: if (!this._inside_citation || (event.keyCode != 13 && event.keyCode != 9)) {
1526: return;
1527: };
1528: if (event.preventDefault) {
1529: event.preventDefault();
1530: } else {
1531: event.returnValue = false;
1532: };
1533: };
1534:
1535: this.handleKeyPressOnCitation = function(event) {
1536: if (!this._inside_citation) {
1537: return;
1538: };
1539: var keyCode = event.keyCode;
1540: var citation = this.getNearestCitation(this.editor.getSelectedNode());
1541: var doc = this.editor.getInnerDocument();
1542: var selection = this.editor.getSelection();
1543: if (keyCode == 13 && this.editor.getBrowserName() == 'IE') {
1544: var br = doc.createElement('br');
1545: var currnode = selection.getSelectedNode();
1546: selection.replaceWithNode(br);
1547: selection.selectNodeContents(br);
1548: selection.collapse(true);
1549: event.returnValue = false;
1550: } else if (keyCode == 9) {
1551: var next = citation.nextSibling;
1552: if (!next) {
1553: next = doc.createElement('p');
1554: next.appendChild(doc.createTextNode('\xa0'));
1555: citation.parentNode.appendChild(next);
1556: };
1557: selection.selectNodeContents(next);
1558: selection.collapse();
1559: if (event.preventDefault) {
1560: event.preventDefault();
1561: };
1562: event.returnValue = false;
1563: this._inside_citation = false;
1564: };
1565: };
1566:
1567: this.updateState = function(selNode, event) {
1568: var citation = this.getNearestCitation(selNode);
1569: if (citation) {
1570: this.addbutton.style.display = 'none';
1571: this.updatebutton.style.display = 'inline';
1572: this.delbutton.style.display = 'inline';
1573: this.authorinput.value = citation.getAttribute('author');
1574: this.sourceinput.value = citation.getAttribute('source');
1575: this._inside_citation = true;
1576: if (this.toolbox) {
1577: this.toolbox.className = this.activeclass;
1578: };
1579: } else {
1580: this.addbutton.style.display = 'inline';
1581: this.updatebutton.style.display = 'none';
1582: this.delbutton.style.display = 'none';
1583: this.authorinput.value = '';
1584: this.sourceinput.value = '';
1585: this._inside_citation = false;
1586: if (this.toolbox) {
1587: this.toolbox.className = this.plainclass;
1588: };
1589: };
1590: };
1591:
1592: this.addCitation = function() {
1593: var selNode = this.editor.getSelectedNode();
1594: var citation = this.getNearestCitation(selNode);
1595: if (citation) {
1596: this.editor.logMessage('Nested citations are not allowed!');
1597: return;
1598: };
1599: var author = this.authorinput.value;
1600: var source = this.sourceinput.value;
1601: var doc = this.editor.getInnerDocument();
1602: var div = doc.createElement('div');
1603: div.className = 'citation';
1604: div.setAttribute('author', author);
1605: div.setAttribute('source', source);
1606: div.setAttribute('is_citation', '1');
1607: var selection = this.editor.getSelection();
1608: var docfrag = selection.cloneContents();
1609: var placecursoratend = false;
1610: if (docfrag.hasChildNodes()) {
1611: for (var i=0; i < docfrag.childNodes.length; i++) {
1612: div.appendChild(docfrag.childNodes[i]);
1613: };
1614: placecursoratend = true;
1615: } else {
1616: var text = doc.createTextNode('\xa0');
1617: div.appendChild(text);
1618: };
1619: this.editor.insertNodeAtSelection(div, 1);
1620: var selection = this.editor.getSelection();
1621: selection.collapse(placecursoratend);
1622: this.editor.getDocument().getWindow().focus();
1623: var selNode = selection.getSelectedNode();
1624: this.editor.updateState(selNode);
1625: };
1626:
1627: this.updateCitation = function() {
1628: var selNode = this.editor.getSelectedNode();
1629: var citation = this.getNearestCitation(selNode);
1630: if (!citation) {
1631: this.editor.logMessage('Not inside a citation element!');
1632: return;
1633: };
1634: citation.setAttribute('author', this.authorinput.value);
1635: citation.setAttribute('source', this.sourceinput.value);
1636: };
1637:
1638: this.deleteCitation = function() {
1639: var selNode = this.editor.getSelectedNode();
1640: var citation = this.getNearestCitation(selNode);
1641: if (!citation) {
1642: this.editor.logMessage('Not inside citation element!');
1643: return;
1644: };
1645: citation.parentNode.removeChild(citation);
1646: };
1647:
1648: this.getNearestCitation = function(selNode) {
1649: var currnode = selNode;
1650: while (currnode) {
1651: if (currnode.nodeName.toLowerCase() == 'div' &&
1652: currnode.getAttribute('is_citation')) {
1653: return currnode;
1654: };
1655: currnode = currnode.parentNode;
1656: };
1657: return false;
1658: };
1659:
1660: this.createContextMenuElements = function(selNode, event) {
1661: /* create the 'Delete citation' menu elements */
1662: var ret = new Array();
1663: if (this.getNearestCitation(selNode)) {
1664: ret.push(new ContextMenuElement('Delete cite', this.deleteCitation, this));
1665: };
1666: return ret;
1667: };
1668: };
1669:
1670: SilvaCitationTool.prototype = new KupuTool;
1671:
1672: function SilvaExternalSourceTool(idselectid, formcontainerid, addbuttonid, cancelbuttonid,
1673: updatebuttonid, delbuttonid, toolboxid, plainclass, activeclass) {
1674: this.idselect = getFromSelector(idselectid);
1675: this.formcontainer = getFromSelector(formcontainerid);
1676: this.addbutton = getFromSelector(addbuttonid);
1677: this.cancelbutton = getFromSelector(cancelbuttonid);
1678: this.updatebutton = getFromSelector(updatebuttonid);
1679: this.delbutton = getFromSelector(delbuttonid);
1680: this.toolbox = getFromSelector(toolboxid);
1681: this.plainclass = plainclass;
1682: this.activeclass = activeclass;
1683:
1684: this._editing = false;
1685: this._url = null;
1686: this._id = null;
1687: this._form = null;
1688: this._insideExternalSource = false;
1689:
1690: // store the base url, this will be prepended to the id to form the url to
1691: // get the codesource from (Zope's acquisition will make sure it ends up on
1692: // the right object)
1693: var urlparts = document.location.toString().split('/')
1694: this._baseurl = urlparts.slice(0, urlparts.length - 2).join('/');
1695:
1696: this.initialize = function(editor) {
1697: this.editor = editor;
1698: addEventHandler(this.addbutton, 'click', this.startExternalSourceAddEdit, this);
1699: addEventHandler(this.cancelbutton, 'click', this.resetTool, this);
1700: addEventHandler(this.updatebutton, 'click', this.startExternalSourceAddEdit, this);
1701: addEventHandler(this.delbutton, 'click', this.delExternalSource, this);
1702: addEventHandler(editor.getInnerDocument(), 'keypress', this.handleKeyPressOnExternalSource, this);
1703: if (this.editor.getBrowserName() == 'IE') {
1704: addEventHandler(editor.getInnerDocument(), 'keydown', this.handleKeyPressOnExternalSource, this);
1705: addEventHandler(editor.getInnerDocument(), 'keyup', this.handleKeyPressOnExternalSource, this);
1706: };
1707:
1708: // search for a special serialized identifier of the current document
1709: // which is used to send to the ExternalSource element when sending
1710: // requests so the ExternalSources know their context
1711: this.docref = null;
1712: var metas = this.editor.getInnerDocument().getElementsByTagName('meta');
1713: for (var i=0; i < metas.length; i++) {
1714: var meta = metas[i];
1715: if (meta.getAttribute('name') == 'docref') {
1716: this.docref = meta.getAttribute('content');
1717: };
1718: };
1719:
1720: this.updatebutton.style.display = 'none';
1721: this.delbutton.style.display = 'none';
1722: this.cancelbutton.style.display = 'none';
1723: };
1724:
1725: this.updateState = function(selNode) {
1726: var extsource = this.getNearestExternalSource(selNode);
1727: if (extsource) {
1728: this._insideExternalSource = true;
1729: selectSelectItem(this.idselect, extsource.getAttribute('source_id'));
1730: this.addbutton.style.display = 'none';
1731: this.cancelbutton.style.display = 'none';
1732: this.updatebutton.style.display = 'inline';
1733: this.delbutton.style.display = 'inline';
1734: this.startExternalSourceUpdate(extsource);
1735: if (this.toolbox) {
1736: this.toolbox.className = this.activeclass;
1737: };
1738: } else {
1739: this._insideExternalSource = false;
1740: this.resetTool();
1741: if (this.toolbox) {
1742: this.toolbox.className = this.plainclass;
1743: };
1744: };
1745: };
1746:
1747: this.handleKeyPressOnExternalSource = function(event) {
1748: if (!this._insideExternalSource) {
1749: return;
1750: };
1751: var keyCode = event.keyCode;
1752: var selNode = this.editor.getSelectedNode();
1753: var div = this.getNearestExternalSource(selNode);
1754: var doc = this.editor.getInnerDocument();
1755: if (keyCode == 13 || keyCode == 9 || keyCode == 39) {
1756: if (div.nextSibling) {
1757: var selection = this.editor.getSelection();
1758: selection.selectNodeContents(div.nextSibling);
1759: selection.collapse();
1760: } else {
1761: var p = doc.createElement('p');
1762: var nbsp = doc.createTextNode('\xa0');
1763: p.appendChild(nbsp);
1764: div.parentNode.appendChild(p);
1765: var selection = this.editor.getSelection();
1766: selection.selectNodeContents(p);
1767: selection.collapse();
1768: };
1769: this._insideExternalSource = false;
1770: } else if (keyCode == 8) {
1771: var selectnode = div.nextSibling;
1772: if (!selectnode) {
1773: selectnode = doc.createElement('p');
1774: selectnode.appendChild(doc.createTextNode('\xa0'));
1775: doc.appendChild(selectnode);
1776: };
1777: var selection = this.editor.getSelection();
1778: selection.selectNodeContents(selectnode);
1779: div.parentNode.removeChild(div);
1780: selection.collapse();
1781: };
1782: if (event.preventDefault) {
1783: event.preventDefault();
1784: } else {
1785: event.returnValue = false;
1786: };
1787: };
1788:
1789: this.getUrlAndContinue = function(id, handler) {
1790: if (id == this._id) {
1791: // return cached
1792: handler.call(this, this._url);
1793: return;
1794: };
1795: var request = new getXMLHttpRequest();
1796: request.open('GET',
1797: this._baseurl + '/edit/get_extsource_url?id=' + id, true);
1798: var callback = new ContextFixer(function() {
1799: if (request.readyState == 4) {
1800: var url = request.responseText;
1801: this._id = id;
1802: this._url = url;
1803: handler.call(this, url);
1804: };
1805: }, this);
1806: request.onreadystatechange = callback.execute;
1807: request.send('');
1808: };
1809:
1810: this.startExternalSourceAddEdit = function() {
1811: // get the appropriate form and display it
1812: if (!this._editing) {
1813: var id = this.idselect.options[this.idselect.selectedIndex].value;
1814: this.getUrlAndContinue(id, this._continueStartExternalSourceEdit);
1815: } else {
1816: // validate the data and take further actions
1817: var formdata = this._gatherFormData();
1818: var doc = window.document;
1819: var request = new XMLHttpRequest();
1820: request.open('POST', this._url + '/validate_form_to_request', true);
1821: var callback = new ContextFixer(this._addExternalSourceIfValidated, request, this);
1822: request.onreadystatechange = callback.execute;
1823: request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
1824: request.send(formdata);
1825: };
1826: };
1827:
1828: this._continueStartExternalSourceEdit = function(url) {
1829: url = url + '/get_rendered_form_for_editor?docref=' + this.docref;
1830: var request = new XMLHttpRequest();
1831: request.open('GET', url, true);
1832: var callback = new ContextFixer(this._addFormToTool, request, this);
1833: request.onreadystatechange = callback.execute;
1834: request.send(null);
1835: while (this.formcontainer.hasChildNodes()) {
1836: this.formcontainer.removeChild(this.formcontainer.firstChild);
1837: };
1838: var text = document.createTextNode('Loading...');
1839: this.formcontainer.appendChild(text);
1840: this.updatebutton.style.display = 'none';
1841: this.cancelbutton.style.display = 'inline';
1842: this.addbutton.style.display = 'inline';
1843: this._editing = true;
1844: };
1845:
1846: this.startExternalSourceUpdate = function(extsource) {
1847: var id = extsource.getAttribute('source_id');
1848: this.getUrlAndContinue(id, this._continueStartExternalSourceUpdate);
1849: };
1850:
1851: this._continueStartExternalSourceUpdate = function(url) {
1852: url = url + '/get_rendered_form_for_editor';
1853: var formdata = this._gatherFormDataFromElement();
1854: formdata += '&docref=' + this.docref;
1855: var request = new XMLHttpRequest();
1856: request.open('POST', url, true);
1857: request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
1858: var callback = new ContextFixer(this._addFormToTool, request, this);
1859: request.onreadystatechange = callback.execute;
1860: request.send(formdata);
1861: this._editing = true;
1862: while (this.formcontainer.hasChildNodes()) {
1863: this.formcontainer.removeChild(this.formcontainer.firstChild);
1864: };
1865: var text = document.createTextNode('Loading...');
1866: this.formcontainer.appendChild(text);
1867: };
1868:
1869: this._addFormToTool = function(object) {
1870: if (this.readyState == 4) {
1871: while (object.formcontainer.hasChildNodes()) {
1872: object.formcontainer.removeChild(object.formcontainer.firstChild);
1873: };
1874: // XXX Somehow appending the XML to the form using DOM doesn't
1875: // work correctly, it looks like the elements aren't HTMLElements
1876: // but XML elements, don't know how to fix now so I'll use string
1877: // insertion for now, needless to say it should be changed to DOM
1878: // manipulation asap...
1879: // XXX why is this.responseXML.documentElement.xml sometimes 'undefined'?
1880: object.formcontainer.innerHTML = this.responseText;
1881: object.idselect.style.display = 'none';
1882: // the formcontainer will contain a table with a form
1883: var form = null;
1884: var iterator = new NodeIterator(object.formcontainer);
1885: while (form == null) {
1886: var next = iterator.next();
1887: if (next.nodeName.toLowerCase() == 'form') {
1888: form = next;
1889: };
1890: };
1891: object._form = form;
1892: };
1893: };
1894:
1895: this._addExternalSourceIfValidated = function(object) {
1896: if (this.readyState == 4) {
1897: if (this.status == '200') {
1898: // success, add the external source element to the document
1899: var selNode = object.editor.getSelectedNode();
1900: var currsource = object.getNearestExternalSource(selNode);
1901: var doc = object.editor.getInnerDocument();
1902:
1903: var extsource = doc.createElement('div');
1904: extsource.setAttribute('source_id', object._id);
1905: var header = doc.createElement('h4');
1906: extsource.appendChild(header);
1907: extsource.className = 'externalsource';
1908: var metatype = 'Silva Code Source'; // a default just in case
1909: for (var i=0; i < this.responseXML.documentElement.childNodes.length; i++) {
1910: var child = this.responseXML.documentElement.childNodes[i];
1911: if (child.nodeName.toLowerCase() == 'parameter') {
1912: var key = child.getAttribute('key');
1913: var value = '';
1914: for (var j=0; j < child.childNodes.length; j++) {
1915: value += child.childNodes[j].nodeValue;
1916: };
1917: if (key == 'metatype') {
1918: metatype = value;
1919: continue;
1920: };
1921: extsource.setAttribute(key, value);
1922: var textel = doc.createTextNode('Key: ' + key + ', value: ' + value.toString());
1923: extsource.appendChild(textel);
1924: extsource.appendChild(doc.createElement('br'));
1925: };
1926: };
1927: var htext = doc.createTextNode(metatype + ' \xab' + object._id + '\xbb');
1928: header.insertBefore(htext, header.firstChild);
1929: extsource.appendChild(doc.createElement('br'));
1930: if (!currsource) {
1931: object.editor.insertNodeAtSelection(extsource);
1932: } else {
1933: currsource.parentNode.replaceChild(extsource, currsource);
1934: var selection = object.editor.getSelection();
1935: selection.selectNodeContents(extsource);
1936: selection.collapse(true);
1937: };
1938: object.resetTool();
1939: object.editor.updateState();
1940: } else if (this.status == '400') {
1941: // failure, provide some feedback and return to the form
1942: alert('Form could not be validated, error message: ' + this.responseText);
1943: } else {
1944: alert('POST failed with unhandled status ' + this.status);
1945: throw('Error handling POST, server returned ' + this.status + ' HTTP status code');
1946: };
1947: };
1948: };
1949:
1950: this.delExternalSource = function() {
1951: var selNode = this.editor.getSelectedNode();
1952: var source = this.getNearestExternalSource(selNode);
1953: if (!source) {
1954: this.editor.logMessage('Not inside external source!', 1);
1955: return;
1956: };
1957: var nextsibling = source.nextSibling;
1958: source.parentNode.removeChild(source);
1959: if (nextsibling) {
1960: var selection = this.editor.getSelection();
1961: selection.selectNodeContents(nextsibling);
1962: selection.collapse();
1963: };
1964: };
1965:
1966: this.resetTool = function() {
1967: while (this.formcontainer.hasChildNodes()) {
1968: this.formcontainer.removeChild(this.formcontainer.firstChild);
1969: };
1970: this.idselect.style.display = 'inline';
1971: this.addbutton.style.display = 'inline';
1972: this.cancelbutton.style.display = 'none';
1973: this.updatebutton.style.display = 'none';
1974: this.delbutton.style.display = 'none';
1975: //this.editor.updateState();
1976: this._editing = false;
1977: };
1978:
1979: this._gatherFormData = function() {
1980: /* walks through the form and creates a POST body */
1981: // XXX we may want to turn this into a helper function, since it's
1982: // quite useful outside of this object I reckon
1983: var form = this._form;
1984: if (!form) {
1985: this.editor.logMessage('Not currently editing');
1986: return;
1987: };
1988: // first place all data into a dict, convert to a string later on
1989: var data = {};
1990: for (var i=0; i < form.elements.length; i++) {
1991: var child = form.elements[i];
1992: var elname = child.nodeName.toLowerCase();
1993: if (elname == 'input') {
1994: var name = child.getAttribute('name');
1995: var type = child.getAttribute('type');
1996: if (!type || type == 'text' || type == 'hidden' || type == 'password') {
1997: data[name] = child.value;
1998: } else if (type == 'checkbox' || type == 'radio') {
1999: if (child.checked) {
2000: if (data[name]) {
2001: if (typeof data[name] == typeof('')) {
2002: var value = new Array(data[name]);
2003: value.push(child.value);
2004: data[name] = value;
2005: } else {
2006: data[name].push(child.value);
2007: };
2008: } else {
2009: data[name] = child.value;
2010: };
2011: };
2012: };
2013: } else if (elname == 'textarea') {
2014: data[child.getAttribute('name')] = child.value;
2015: } else if (elname == 'select') {
2016: var name = child.getAttribute('name');
2017: var multiple = child.getAttribute('multiple');
2018: if (!multiple) {
2019: data[name] = child.options[child.selectedIndex].value;
2020: } else {
2021: var value = new Array();
2022: for (var i=0; i < child.options.length; i++) {
2023: if (child.options[i].checked) {
2024: value.push(options[i].value);
2025: };
2026: if (value.length > 1) {
2027: data[name] = value;
2028: } else if (value.length) {
2029: data[name] = value[0];
2030: };
2031: };
2032: };
2033: };
2034: };
2035:
2036: // now we should turn it into a query string
2037: var ret = new Array();
2038: for (var key in data) {
2039: var value = data[key];
2040: // XXX does IE5 support encodeURIComponent?
2041: ret.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
2042: };
2043:
2044: return ret.join("&");
2045: };
2046:
2047: this._gatherFormDataFromElement = function() {
2048: var selNode = this.editor.getSelectedNode();
2049: var source = this.getNearestExternalSource(selNode);
2050: if (!source) {
2051: return '';
2052: };
2053: var ret = new Array();
2054: for (var i=0; i < source.attributes.length; i++) {
2055: var attr = source.attributes[i];
2056: var name = attr.nodeName;
2057: var value = attr.nodeValue;
2058: if (name != 'class' && name != 'source_id' && name != 'id') {
2059: ret.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
2060: };
2061: };
2062: return ret.join('&');
2063: };
2064:
2065: this.getNearestExternalSource = function(selNode) {
2066:
2067: var currnode = selNode;
2068: while (currnode) {
2069: if (currnode.nodeName.toLowerCase() == 'div' && currnode.className == 'externalsource') {
2070: return currnode;
2071: };
2072: currnode = currnode.parentNode;
2073: };
2074: };
2075: };
2076:
2077: SilvaExternalSourceTool.prototype = new KupuTool;
2078:
2079: function SilvaKupuUI(textstyleselectid) {
2080: this.tsselect = getFromSelector(textstyleselectid);
2081:
2082: this.updateState = function(selNode) {
2083: /* set the text-style pulldown */
2084:
2085: // first get the nearest style
2086: var styles = {}; // use an object here so we can use the 'in' operator later on
2087: for (var i=0; i < this.tsselect.options.length; i++) {
2088: // XXX we should cache this
2089: styles[this.tsselect.options[i].value] = i;
2090: }
2091:
2092: // search the list of nodes like in the original one, break if we encounter a match,
2093: // this method does some more than the original one since it can handle commands in
2094: // the form of '<style>|<classname>' next to the plain '<style>' commands
2095: var currnode = selNode;
2096: var index = -1;
2097: while (index==-1 && currnode) {
2098: var nodename = currnode.nodeName.toLowerCase();
2099: for (var style in styles) {
2100: if (style.indexOf('|') < 0) {
2101: // simple command
2102: if (nodename == style.toLowerCase() && !currnode.className) {
2103: index = styles[style];
2104: break;
2105: };
2106: } else {
2107: // command + classname
2108: var tuple = style.split('|');
2109: if (nodename == tuple[0].toLowerCase() && currnode.className == tuple[1]) {
2110: index = styles[style];
2111: break;
2112: };
2113: };
2114: };
2115: currnode = currnode.parentNode;
2116: }
2117: this.tsselect.selectedIndex = Math.max(index,0);
2118: };
2119:
2120: this.setTextStyle = function(style) {
2121: /* parse the argument into a type and classname part
2122:
2123: generate a block element accordingly
2124: */
2125: // XXX somehow this method always gets called twice... I would
2126: // really like to know why, but can't find it right now and don't
2127: // have time for a full investigation, so fiddle-fixed it this
2128: // way. Needless to say this needs some investigation at some point...
2129: if (this._cancel_update) {
2130: this._cancel_update = false;
2131: return;
2132: };
2133:
2134: var classname = "";
2135: var eltype = style;
2136: if (style.indexOf('|') > -1) {
2137: style = style.split('|');
2138: eltype = style[0];
2139: classname = style[1];
2140: };
2141:
2142: var command = eltype;
2143: // first create the element, then find it and set the classname
2144: if (this.editor.getBrowserName() == 'IE') {
2145: command = '<' + eltype + '>';
2146: };
2147: this.editor.getDocument().execCommand('formatblock', command);
2148:
2149: // now get a reference to the element just added
2150: var selNode = this.editor.getSelectedNode();
2151: var el = this.editor.getNearestParentOfType(selNode, eltype);
2152:
2153: // now set the classname
2154: if (classname) {
2155: el.className = classname;
2156: el.setAttribute('silva_type', classname);
2157: };
2158: this._cancel_update = true;
2159: this.editor.updateState();
2160: this.editor.getDocument().getWindow().focus();
2161: };
2162: };
2163:
2164: SilvaKupuUI.prototype = new KupuUI;
2165:
2166: function SilvaPropertyTool(tablerowid) {
2167: /* a simple tool to edit metadata fields
2168:
2169: the fields' contents are stored in Silva's metadata sets
2170: */
2171: this.tablerow = document.getElementById(tablerowid);
2172: this.table = this.tablerow.parentNode;
2173: while (!this.table.nodeName.toLowerCase() == 'table') {
2174: this.table = this.table.parentNode;
2175: };
2176: // remove current content from the fields
2177: var tds = this.tablerow.getElementsByTagName('td');
2178: for (var i=0; i < tds.length; i++) {
2179: while (tds[i].hasChildNodes()) {
2180: tds[i].removeChild(tds[i].childNodes[0]);
2181: };
2182: };
2183: };
2184:
2185: SilvaPropertyTool.prototype = new KupuTool;
2186:
2187: SilvaPropertyTool.prototype.initialize = function(editor) {
2188: this.editor = editor;
2189:
2190: // walk through all metadata fields and expose them to the user
2191: var metas = this.editor.getInnerDocument().getElementsByTagName('meta');
2192: for (var i=0; i < metas.length; i++) {
2193: var meta = metas[i];
2194: var name = meta.getAttribute('name');
2195: if (!name) {
2196: // http-equiv type
2197: continue;
2198: };
2199: var rowcopy = this.tablerow.cloneNode(true);
2200: var tag = this.parseFormElIntoRow(meta, rowcopy);
2201: if (tag) {
2202: this.tablerow.parentNode.appendChild(tag);
2203: };
2204: };
2205: // throw away the original row: we don't need it anymore...
2206: this.tablerow.parentNode.removeChild(this.tablerow);
2207: };
2208:
2209: SilvaPropertyTool.prototype.parseFormElIntoRow = function(metatag, tablerow) {
2210: /* render a field in the properties tool according to a metadata tag
2211:
2212: returns some false value if the meta tag should not be editable
2213: */
2214: var scheme = metatag.getAttribute('scheme');
2215: if (!scheme || !(scheme in EDITABLE_METADATA)) {
2216: return;
2217: };
2218: var name = metatag.getAttribute('name');
2219: var namespace = metatag.getAttribute('scheme');
2220: var nametypes = EDITABLE_METADATA[scheme];
2221: var type = 'text';
2222: var mandatory = false;
2223: var namefound = false;
2224: var fieldtitle = '';
2225: for (var i=0; i < nametypes.length; i++) {
2226: var nametype = nametypes[i];
2227: var elname = nametype[0];
2228: var type = nametype[1];
2229: var mandatory = nametype[2];
2230: var fieldtitle = nametype[3];
2231: if (elname == name) {
2232: namefound = true;
2233: break;
2234: };
2235: };
2236: if (!namefound) {
2237: return;
2238: };
2239:
2240: var titlefield = document.createElement('span');
2241: var title = document.createTextNode(fieldtitle);
2242: titlefield.appendChild(title);
2243: tablerow.getElementsByTagName('td')[0].appendChild(titlefield);
2244: titlefield.className = 'metadata-field';
2245:
2246: var input = null;
2247: var value = metatag.getAttribute('content');
2248: var parentvalue = metatag.getAttribute('parentcontent');
2249: if (type == 'text') {
2250: input = document.createElement('input');
2251: input.value = value;
2252: input.setAttribute('type', 'text');
2253: } else if (type == 'textarea') {
2254: input = document.createElement('textarea');
2255: var content = document.createTextNode(value);
2256: input.appendChild(content);
2257: };
2258: input.setAttribute('name', name);
2259: input.setAttribute('namespace', namespace);
2260: input.className = 'metadata-input';
2261: if (mandatory) {
2262: input.setAttribute('mandatory', 'true');
2263: };
2264: var td = tablerow.getElementsByTagName('td')[1]
2265: td.appendChild(input);
2266: if (parentvalue && parentvalue != '') {
2267: td.appendChild(document.createElement('br'));
2268: td.appendChild(document.createTextNode('acquired value:'));
2269: td.appendChild(document.createElement('br'));
2270: td.appendChild(document.createTextNode(parentvalue));
2271: };
2272:
2273: return tablerow;
2274: };
2275:
2276: SilvaPropertyTool.prototype.beforeSave = function() {
2277: /* save the metadata to the document */
2278: var doc = this.editor.getInnerDocument();
2279: var inputs = this.table.getElementsByTagName('input');
2280: var textareas = this.table.getElementsByTagName('textarea');
2281: var errors = [];
2282: var okay = [];
2283: for (var i=0; i < inputs.length; i++) {
2284: var input = inputs[i];
2285: if (!input.getAttribute('type') == 'text' || !input.getAttribute('namespace')) {
2286: continue;
2287: };
2288: var name = input.getAttribute('name');
2289: var scheme = input.getAttribute('namespace');
2290: var value = input.value;
2291: if (input.getAttribute('mandatory') && value.strip() == '') {
2292: errors.push(name);
2293: continue;
2294: };
2295: okay.push([name, scheme, value]);
2296: };
2297: for (var i=0; i < textareas.length; i++) {
2298: var textarea = textareas[i];
2299: var name = textarea.getAttribute('name');
2300: var scheme = textarea.getAttribute('namespace');
2301: var value = textarea.value;
2302: if (textarea.getAttribute('mandatory') && value.strip() == '') {
2303: errors.push(name);
2304: continue;
2305: };
2306: okay.push([name, scheme, value]);
2307: };
2308: if (errors.length) {
2309: throw('Error: fields ' + errors.join(', ') + ' are required but not filled in');
2310: };
2311: for (var i=0; i < okay.length; i++) {
2312: this._addMetaTag(doc, okay[i][0], okay[i][1], okay[i][2]);
2313: };
2314: };
2315:
2316: SilvaPropertyTool.prototype._addMetaTag = function(doc, name, scheme, value, parentvalue) {
2317: var head = doc.getElementsByTagName('head')[0];
2318: if (!head) {
2319: throw('The editable document *must* have a <head> element!');
2320: };
2321: // first find and delete the old one
2322: // XXX if only we'd have XPath...
2323: var metas = doc.getElementsByTagName('meta');
2324: for (var i=0; i < metas.length; i++) {
2325: var meta = metas[i];
2326: if (meta.getAttribute('name') == name &&
2327: meta.getAttribute('scheme') == scheme) {
2328: meta.parentNode.removeChild(meta);
2329: };
2330: };
2331: var tag = doc.createElement('meta');
2332: tag.setAttribute('name', name);
2333: tag.setAttribute('scheme', scheme);
2334: tag.setAttribute('content', value);
2335:
2336: head.appendChild(tag);
2337: };
2338:
2339: function SilvaCharactersTool(charselectid) {
2340: /* a tool to add non-standard characters */
2341: this._charselect = document.getElementById(charselectid);
2342: };
2343:
2344: SilvaCharactersTool.prototype = new KupuTool;
2345:
2346: SilvaCharactersTool.prototype.initialize = function(editor) {
2347: this.editor = editor;
2348: addEventHandler(this._charselect, 'change', this.addCharacter, this);
2349: var chars = this.editor.config.nonstandard_chars.split(' ');
2350: for (var i=0; i < chars.length; i++) {
2351: var option = document.createElement('option');
2352: option.value = chars[i];
2353: var text = document.createTextNode(chars[i]);
2354: option.appendChild(text);
2355: this._charselect.appendChild(option);
2356: };
2357: };
2358:
2359: SilvaCharactersTool.prototype.addCharacter = function() {
2360: var select = this._charselect;
2361: var c = select.options[select.selectedIndex].value;
2362: if (!c.strip()) {
2363: return;
2364: };
2365: var selection = this.editor.getSelection();
2366: var textnode = this.editor.getInnerDocument().createTextNode(c);
2367: var span = this.editor.getInnerDocument().createElement('span');
2368: span.appendChild(textnode);
2369: selection.replaceWithNode(span);
2370: var selection = this.editor.getSelection();
2371: selection.selectNodeContents(span);
2372: selection.moveEnd(1);
2373: selection.collapse(true);
2374: this.editor.logMessage('Character ' + c + ' inserted');
2375: this.editor.getDocument().getWindow().focus();
2376: select.selectedIndex = 0;
2377: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>