Annotation of kupu/common/kupudrawers.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: kupudrawers.js 14575 2005-07-12 20:18:12Z duncan $
12:
13: function DrawerTool() {
14: /* a tool to open and fill drawers
15:
16: this tool has to (and should!) only be instantiated once
17: */
18: this.drawers = {};
19: this.current_drawer = null;
20:
21: this.initialize = function(editor) {
22: this.editor = editor;
23: this.isIE = this.editor.getBrowserName() == 'IE';
24: // this essentially makes the drawertool a singleton
25: window.drawertool = this;
26: };
27:
28: this.registerDrawer = function(id, drawer, editor) {
29: this.drawers[id] = drawer;
30: drawer.initialize(editor || this.editor, this);
31: };
32:
33: this.openDrawer = function(id) {
34: /* open a drawer */
35: if (this.current_drawer) {
36: this.closeDrawer();
37: };
38: var drawer = this.drawers[id];
39: if (this.isIE) {
40: drawer.editor._saveSelection();
41: }
42: drawer.createContent();
43: drawer.editor.suspendEditing();
44: this.current_drawer = drawer;
45: };
46:
47: this.updateState = function(selNode) {
48: };
49:
50: this.closeDrawer = function(button) {
51: if (!this.current_drawer) {
52: return;
53: };
54: this.current_drawer.hide();
55: this.current_drawer.editor.resumeEditing();
56: this.current_drawer = null;
57: };
58:
59: // this.getDrawerEnv = function(iframe_win) {
60: // var drawer = null;
61: // for (var id in this.drawers) {
62: // var ldrawer = this.drawers[id];
63: // // Note that we require drawers to provide us with an
64: // // element property!
65: // if (ldrawer.element.contentWindow == iframe_win) {
66: // drawer = ldrawer;
67: // };
68: // };
69: // if (!drawer) {
70: // this.editor.logMessage("Drawer not found", 1);
71: // return;
72: // };
73: // return {
74: // 'drawer': drawer,
75: // 'drawertool': this,
76: // 'tool': drawer.tool
77: // };
78: // };
79: };
80:
81: DrawerTool.prototype = new KupuTool;
82:
83: function Drawer(elementid, tool) {
84: /* base prototype for drawers */
85:
86: this.element = getFromSelector(elementid);
87: this.tool = tool;
88:
89: this.initialize = function(editor, drawertool) {
90: this.editor = editor;
91: this.drawertool = drawertool;
92: };
93:
94: this.createContent = function() {
95: /* fill the drawer with some content */
96: // here's where any intelligence and XSLT transformation and such
97: // is done
98: this.element.style.display = 'block';
99: this.focusElement();
100: };
101:
102: this.hide = function() {
103: this.element.style.display = 'none';
104: this.focussed = false;
105: };
106:
107: this.focusElement = function() {
108: // IE can focus the drawer element, but Mozilla needs more help
109: this.focussed = false;
110: var iterator = new NodeIterator(this.element);
111: var currnode = iterator.next();
112: while (currnode) {
113: if (currnode.tagName && (currnode.tagName.toUpperCase()=='BUTTON' ||
114: (currnode.tagName.toUpperCase()=='INPUT' && !(/nofocus/.test(currnode.className)))
115: )) {
116: this.focussed = true;
117: function focusit() {
118: currnode.focus();
119: }
120: timer_instance.registerFunction(this, focusit, 100);
121: return;
122: }
123: currnode = iterator.next();
124: }
125: }
126: };
127:
128: function LinkDrawer(elementid, tool, wrap) {
129: /* Link drawer */
130: this.element = getFromSelector(elementid);
131: this.tool = tool;
132: function wrap(id, tag) {
133: return '#'+this.element.id+' '+tag+'.'+id;
134: }
135: var input = getBaseTagClass(this.element, 'input', 'kupu-linkdrawer-input');
136: var preview = getBaseTagClass(this.element, 'iframe', 'kupu-linkdrawer-preview');
137:
138: this.createContent = function() {
139: /* display the drawer */
140: var currnode = this.editor.getSelectedNode();
141: var linkel = this.editor.getNearestParentOfType(currnode, 'a');
142: input.value = "";
143: this.preview();
144: if (linkel) {
145: input.value = linkel.getAttribute('href');
146: } else {
147: input.value = 'http://';
148: };
149: this.element.style.display = 'block';
150: this.focusElement();
151: };
152:
153: this.save = function() {
154: /* add or modify a link */
155: this.editor.resumeEditing();
156: var url = input.value;
157: var target = '_self';
158: if (this.target) target = this.target;
159: this.tool.createLink(url, null, null, target);
160: input.value = '';
161:
162: // XXX when reediting a link, the drawer does not close for
163: // some weird reason. BUG! Close the drawer manually until we
164: // find a fix:
165: this.drawertool.closeDrawer();
166: };
167:
168: this.preview = function() {
169: preview.src = input.value;
170: if (this.editor.getBrowserName() == 'IE') {
171: preview.width = "800";
172: preview.height = "365";
173: preview.style.zoom = "60%";
174: };
175: }
176: this.preview_loaded = function() {
177: if (input.value != preview.src) {
178: input.value = preview.src;
179: }
180: }
181: };
182:
183: LinkDrawer.prototype = new Drawer;
184:
185: function TableDrawer(elementid, tool) {
186: /* Table drawer */
187: this.element = getFromSelector(elementid);
188: this.tool = tool;
189:
190: this.addpanel = getBaseTagClass(this.element, 'div', 'kupu-tabledrawer-addtable');
191: this.editpanel = getBaseTagClass(this.element, 'div', 'kupu-tabledrawer-edittable');
192: var classselect = getBaseTagClass(this.element, 'select', 'kupu-tabledrawer-classchooser');
193: var alignselect = getBaseTagClass(this.element, 'select', 'kupu-tabledrawer-alignchooser');
194: var newrowsinput = getBaseTagClass(this.element, 'input', 'kupu-tabledrawer-newrows');
195: var newcolsinput = getBaseTagClass(this.element, 'input', 'kupu-tabledrawer-newcols');
196: var makeheadercheck = getBaseTagClass(this.element, 'input', 'kupu-tabledrawer-makeheader');
197:
198: this.createContent = function() {
199: var selNode = this.editor.getSelectedNode();
200: if (this.editor.config.table_classes) {
201: var classes = this.editor.config.table_classes['class'];
202: while (classselect.hasChildNodes()) {
203: classselect.removeChild(classselect.firstChild);
204: };
205: for (var i=0; i < classes.length; i++) {
206: var classname = classes[i];
207: var option = document.createElement('option');
208: var content = document.createTextNode(classname);
209: option.appendChild(content);
210: option.setAttribute('value', classname);
211: classselect.appendChild(option);
212: };
213: };
214:
215: var table = this.editor.getNearestParentOfType(selNode, 'table');
216:
217: if (!table) {
218: // show add table drawer
219: show = this.addpanel;
220: hide = this.editpanel;
221: } else {
222: // show edit table drawer
223: show = this.editpanel;
224: hide = this.addpanel;
225: var align = this.tool._getColumnAlign(selNode);
226: selectSelectItem(alignselect, align);
227: selectSelectItem(classselect, table.className);
228: };
229: hide.style.display = 'none';
230: show.style.display = 'block';
231: this.element.style.display = 'block';
232: this.focusElement();
233: };
234:
235: this.createTable = function() {
236: this.editor.resumeEditing();
237: var rows = newrowsinput.value;
238: var cols = newcolsinput.value;
239: var style = classselect.value;
240: var add_header = makeheadercheck.checked;
241: this.tool.createTable(parseInt(rows), parseInt(cols), add_header, style);
242: this.drawertool.closeDrawer();
243: };
244: this.delTableRow = function() {
245: this.editor.resumeEditing();
246: this.tool.delTableRow();
247: this.editor.suspendEditing();
248: };
249: this.addTableRow = function() {
250: this.editor.resumeEditing();
251: this.tool.addTableRow();
252: this.editor.suspendEditing();
253: };
254: this.delTableColumn = function() {
255: this.editor.resumeEditing();
256: this.tool.delTableColumn();
257: this.editor.suspendEditing();
258: };
259: this.addTableColumn = function() {
260: this.editor.resumeEditing();
261: this.tool.addTableColumn();
262: this.editor.suspendEditing();
263: };
264: this.fixTable = function() {
265: this.editor.resumeEditing();
266: this.tool.fixTable();
267: this.editor.suspendEditing();
268: };
269: this.fixAllTables = function() {
270: this.editor.resumeEditing();
271: this.tool.fixAllTables();
272: this.editor.suspendEditing();
273: };
274: this.setTableClass = function(className) {
275: this.editor.resumeEditing();
276: this.tool.setTableClass(className);
277: this.editor.suspendEditing();
278: };
279: this.setColumnAlign = function(align) {
280: this.editor.resumeEditing();
281: this.tool.setColumnAlign(align);
282: this.editor.suspendEditing();
283: };
284: };
285:
286: TableDrawer.prototype = new Drawer;
287:
288: function LibraryDrawer(tool, xsluri, libsuri, searchuri, baseelement) {
289: /* a drawer that loads XSLT and XML from the server
290: and converts the XML to XHTML for the drawer using the XSLT
291:
292: there are 2 types of XML file loaded from the server: the first
293: contains a list of 'libraries', partitions for the data items,
294: and the second a list of data items for a certain library
295:
296: all XML loading is done async, since sync loading can freeze Mozilla
297: */
298:
299: this.init = function(tool, xsluri, libsuri, searchuri, baseelement) {
300: /* This method is there to thin out the constructor and to be
301: able to inherit it in sub-prototypes. Don't confuse this
302: method with the component initializer (initialize()).
303: */
304: // these are used in the XSLT. Maybe they should be
305: // parameterized or something, but we depend on so many other
306: // things implicitly anyway...
307: this.drawerid = 'kupu-librarydrawer';
308: this.librariespanelid = 'kupu-librariespanel';
309: this.resourcespanelid = 'kupu-resourcespanel';
310: this.propertiespanelid = 'kupu-propertiespanel';
311:
312: if (baseelement) {
313: this.baseelement = getFromSelector(baseelement);
314: } else {
315: this.baseelement = getBaseTagClass(document.body, 'div', 'kupu-librarydrawer-parent');
316: }
317:
318: this.tool = tool;
319: this.element = document.getElementById(this.drawerid);
320: if (!this.element) {
321: var e = document.createElement('div');
322: e.id = this.drawerid;
323: e.className = 'kupu-drawer '+this.drawerid;
324: this.baseelement.appendChild(e);
325: this.element = e;
326: }
327: this.shared.xsluri = xsluri;
328: this.shared.libsuri = libsuri;
329: this.shared.searchuri = searchuri;
330:
331: // marker that gets set when a new image has been uploaded
332: this.shared.newimages = null;
333:
334: // the following vars will be available after this.initialize()
335: // has been called
336:
337: // this will be filled by this._libXslCallback()
338: this.shared.xsl = null;
339: // this will be filled by this.loadLibraries(), which is called
340: // somewhere further down the chain starting with
341: // this._libsXslCallback()
342: this.shared.xmldata = null;
343:
344: };
345: if (tool) {
346: this.init(tool, xsluri, libsuri, searchuri);
347: }
348:
349: this.initialize = function(editor, drawertool) {
350: this.editor = editor;
351: this.drawertool = drawertool;
352: this.selecteditemid = '';
353:
354: // load the xsl and the initial xml
355: var wrapped_callback = new ContextFixer(this._libsXslCallback, this);
356: this._loadXML(this.shared.xsluri, wrapped_callback.execute);
357: };
358:
359: /*** bootstrapping ***/
360:
361: this._libsXslCallback = function(dom) {
362: /* callback for when the xsl for the libs is loaded
363:
364: this is called on init and since the initial libs need
365: to be loaded as well (and everything is async with callbacks
366: so there's no way to wait until the XSL is loaded) this
367: will also make the first loadLibraries call
368: */
369: this.shared.xsl = dom;
370:
371: // Change by Paul to have cached xslt transformers for reuse of
372: // multiple transforms and also xslt params
373: try {
374: var xsltproc = new XSLTProcessor();
375: this.shared.xsltproc = xsltproc;
376: xsltproc.importStylesheet(dom);
377: xsltproc.setParameter("", "drawertype", this.drawertype);
378: xsltproc.setParameter("", "drawertitle", this.drawertitle);
379: xsltproc.setParameter("", "showupload", this.showupload);
380: if (this.editor.config.captions) {
381: xsltproc.setParameter("", "usecaptions", 'yes');
382: }
383: } catch(e) {
384: return; // No XSLT Processor, maybe IE 5.5?
385: }
386: };
387:
388: this.createContent = function() {
389: // Make sure the drawer XML is in the current Kupu instance
390: if (this.element.parentNode != this.baseelement) {
391: this.baseelement.appendChild(this.element);
392: }
393: // load the initial XML
394: if(!this.shared.xmldata) {
395: // Do a meaningful test to see if this is IE5.5 or some other
396: // editor-enabled version whose XML support isn't good enough
397: // for the drawers
398: if (!window.XSLTProcessor) {
399: alert("This function requires better XML support in your browser.");
400: return;
401: }
402: this.loadLibraries();
403: } else {
404: if (this.shared.newimages) {
405: this.reloadCurrent();
406: this.shared.newimages = null;
407: };
408: this.updateDisplay();
409: this.initialSelection();
410: };
411:
412: // display the drawer div
413: this.element.style.display = 'block';
414: };
415:
416: this._singleLibsXslCallback = function(dom) {
417: /* callback for then the xsl for single libs (items) is loaded
418:
419: nothing special needs to be called here, since initially the
420: items pane will be empty
421: */
422: this.singlelibxsl = dom;
423: };
424:
425: this.loadLibraries = function() {
426: /* load the libraries and display them in a redrawn drawer */
427: var wrapped_callback = new ContextFixer(this._libsContentCallback, this);
428: this._loadXML(this.shared.libsuri, wrapped_callback.execute);
429: };
430:
431: this._libsContentCallback = function(dom) {
432: /* this is called when the libs xml is loaded
433:
434: does the xslt transformation to set up or renew the drawer's full
435: content and adds the content to the drawer
436: */
437: this.shared.xmldata = dom;
438: this.shared.xmldata.setProperty("SelectionLanguage", "XPath");
439:
440: // replace whatever is in there with our stuff
441: this.updateDisplay(this.drawerid);
442: this.initialSelection();
443: };
444:
445: this.initialSelection = function() {
446: var libnode_path = '/libraries/library[@selected]';
447: var libnode = this.shared.xmldata.selectSingleNode(libnode_path);
448: if (libnode) {
449: var id = libnode.getAttribute('id');
450: this.selectLibrary(id);
451: }
452: }
453:
454: this.updateDisplay = function(id) {
455: /* (re-)transform XML and (re-)display the necessary part
456: */
457: if(!id) {
458: id = this.drawerid;
459: };
460: try {
461: this.shared.xsltproc.setParameter("", "showupload", this.showupload);
462: } catch(e) {};
463: var doc = this._transformXml();
464: var sourcenode = doc.selectSingleNode('//*[@id="'+id+'"]');
465: var targetnode = document.getElementById(id);
466: sourcenode = document.importNode(sourcenode, true);
467: Sarissa.copyChildNodes(sourcenode, targetnode);
468: if (!this.focussed) {
469: this.focusElement();
470: }
471:
472: if (this.editor.getBrowserName() == 'IE' && id == this.resourcespanelid) {
473: this.updateDisplay(this.drawerid);
474: };
475: };
476:
477: this.deselectActiveCollection = function() {
478: /* Deselect the currently active collection or library */
479: while (1) {
480: // deselect selected DOM node
481: var selected = this.shared.xmldata.selectSingleNode('//*[@selected]');
482: if (!selected) {
483: return;
484: };
485: selected.removeAttribute('selected');
486: };
487: };
488:
489: /*** Load a library ***/
490:
491: this.selectLibrary = function(id) {
492: /* unselect the currently selected lib and select a new one
493:
494: the selected lib (libraries pane) will have a specific CSS class
495: (selected)
496: */
497: // remove selection in the DOM
498: this.deselectActiveCollection();
499: // as well as visual selection in CSS
500: // XXX this is slow, but we can't do XPath, unfortunately
501: var divs = this.element.getElementsByTagName('div');
502: for (var i=0; i<divs.length; i++ ) {
503: if (divs[i].className == 'kupu-libsource-selected') {
504: divs[i].className = 'kupu-libsource';
505: };
506: };
507:
508: var libnode_path = '/libraries/library[@id="' + id + '"]';
509: var libnode = this.shared.xmldata.selectSingleNode(libnode_path);
510: libnode.setAttribute('selected', '1');
511:
512: var items_xpath = "items";
513: var items_node = libnode.selectSingleNode(items_xpath);
514:
515: if (items_node && !this.shared.newimages) {
516: // The library has already been loaded before or was
517: // already provided with an items list. No need to do
518: // anything except for displaying the contents in the
519: // middle pane. Newimages is set if we've lately
520: // added an image.
521: this.updateDisplay(this.resourcespanelid);
522: this.updateDisplay(this.propertiespanelid);
523: } else {
524: // We have to load the library from XML first.
525: var src_uri = libnode.selectSingleNode('src/text()').nodeValue;
526: src_uri = src_uri.strip(); // needs kupuhelpers.js
527: // Now load the library into the items pane. Since we have
528: // to load the XML, do this via a call back
529: var wrapped_callback = new ContextFixer(this._libraryContentCallback, this);
530: this._loadXML(src_uri, wrapped_callback.execute, null);
531: this.shared.newimages = null;
532: };
533: // instead of running the full transformations again we get a
534: // reference to the element and set the classname...
535: var newseldiv = document.getElementById(id);
536: newseldiv.className = 'kupu-libsource-selected';
537: };
538:
539: this._libraryContentCallback = function(dom, src_uri) {
540: /* callback for when a library's contents (item list) is loaded
541:
542: This is also used as he handler for reloading a standard
543: collection.
544: */
545: var libnode = this.shared.xmldata.selectSingleNode('//*[@selected]');
546: var itemsnode = libnode.selectSingleNode("items");
547: var newitemsnode = dom.selectSingleNode("//items");
548:
549: // IE does not support importNode on XML document nodes. As an
550: // evil hack, clonde the node instead.
551:
552: if (this.editor.getBrowserName() == 'IE') {
553: newitemsnode = newitemsnode.cloneNode(true);
554: } else {
555: newitemsnode = this.shared.xmldata.importNode(newitemsnode, true);
556: }
557: if (!itemsnode) {
558: // We're loading this for the first time
559: libnode.appendChild(newitemsnode);
560: } else {
561: // User has clicked reload
562: libnode.replaceChild(newitemsnode, itemsnode);
563: };
564: this.updateDisplay(this.resourcespanelid);
565: this.updateDisplay(this.propertiespanelid);
566: };
567:
568: /*** Load a collection ***/
569:
570: this.selectCollection = function(id) {
571: this.deselectActiveCollection();
572:
573: // First turn off current selection, if any
574: this.removeSelection();
575:
576: var leafnode_path = "//collection[@id='" + id + "']";
577: var leafnode = this.shared.xmldata.selectSingleNode(leafnode_path);
578:
579: // Case 1: We've already loaded the data, so we just need to
580: // refer to the data by id.
581: var loadedInNode = leafnode.getAttribute('loadedInNode');
582: if (loadedInNode) {
583: var collnode_path = "/libraries/collection[@id='" + loadedInNode + "']";
584: var collnode = this.shared.xmldata.selectSingleNode(collnode_path);
585: if (collnode) {
586: collnode.setAttribute('selected', '1');
587: this.updateDisplay(this.resourcespanelid);
588: this.updateDisplay(this.propertiespanelid);
589: return;
590: };
591: };
592:
593: // Case 2: We've already loaded the data, but there hasn't
594: // been a reference made yet. So, make one :)
595: uri = leafnode.selectSingleNode('uri/text()').nodeValue;
596: uri = (new String(uri)).strip(); // needs kupuhelpers.js
597: var collnode_path = "/libraries/collection/uri[text()='" + uri + "']/..";
598: var collnode = this.shared.xmldata.selectSingleNode(collnode_path);
599: if (collnode) {
600: id = collnode.getAttribute('id');
601: leafnode.setAttribute('loadedInNode', id);
602: collnode.setAttribute('selected', '1');
603: this.updateDisplay(this.resourcespanelid);
604: this.updateDisplay(this.propertiespanelid);
605: return;
606: };
607:
608: // Case 3: We've not loaded the data yet, so we need to load it
609: // this is just so we can find the leafnode much easier in the
610: // callback.
611: leafnode.setAttribute('selected', '1');
612: var src_uri = leafnode.selectSingleNode('src/text()').nodeValue;
613: src_uri = src_uri.strip(); // needs kupuhelpers.js
614: var wrapped_callback = new ContextFixer(this._collectionContentCallback, this);
615: this._loadXML(src_uri, wrapped_callback.execute, null);
616: };
617:
618: this._collectionContentCallback = function(dom, src_uri) {
619: // Unlike with libraries, we don't have to find a node to hook
620: // our results into (UNLESS we've hit the reload button, but
621: // that is handled in _libraryContentCallback anyway).
622: // We need to give the newly retrieved data a unique ID, we
623: // just use the time.
624: date = new Date();
625: time = date.getTime();
626:
627: // attach 'loadedInNode' attribute to leaf node so Case 1
628: // applies next time.
629: var leafnode = this.shared.xmldata.selectSingleNode('//*[@selected]');
630: leafnode.setAttribute('loadedInNode', time);
631: this.deselectActiveCollection()
632:
633: var collnode = dom.selectSingleNode('/collection');
634: collnode.setAttribute('id', time);
635: collnode.setAttribute('selected', '1');
636:
637: var libraries = this.shared.xmldata.selectSingleNode('/libraries');
638:
639: // IE does not support importNode on XML documet nodes
640: if (this.editor.getBrowserName() == 'IE') {
641: collnode = collnode.cloneNode(true);
642: } else {
643: collnode = this.shared.xmldata.importNode(collnode, true);
644: }
645: libraries.appendChild(collnode);
646: this.updateDisplay(this.resourcespanelid);
647: this.updateDisplay(this.propertiespanelid);
648: };
649:
650: /*** Reloading a collection or library ***/
651:
652: this.reloadCurrent = function() {
653: // Reload current collection or library
654: this.showupload = '';
655: var current = this.shared.xmldata.selectSingleNode('//*[@selected]');
656: // make sure we're dealing with a collection even though a
657: // resource might be selected
658: if (current.tagName == "resource") {
659: current.removeAttribute("selected");
660: current = current.parentNode;
661: current.setAttribute("selected", "1");
662: };
663: var src_node = current.selectSingleNode('src');
664: if (!src_node) {
665: // simply do nothing if the library cannot be reloaded. This
666: // is currently the case w/ search result libraries.
667: return;
668: };
669:
670: var src_uri = src_node.selectSingleNode('text()').nodeValue;
671:
672: src_uri = src_uri.strip(); // needs kupuhelpers.js
673:
674: var wrapped_callback = new ContextFixer(this._libraryContentCallback, this);
675: this._loadXML(src_uri, wrapped_callback.execute);
676: };
677:
678: this.removeSelection = function() {
679: // turn off current selection, if any
680: var oldselxpath = '/libraries/*[@selected]//resource[@selected]';
681: var oldselitem = this.shared.xmldata.selectSingleNode(oldselxpath);
682: if (oldselitem) {
683: oldselitem.removeAttribute("selected");
684: };
685: if (this.selecteditemid) {
686: var item = document.getElementById(this.selecteditemid);
687: if (item) {
688: var span = item.getElementsByTagName('span');
689: if (span.length > 0) {
690: span = span[0];
691: span.className = span.className.replace(' selected-item', '');
692: }
693: }
694: this.selecteditemid = '';
695: }
696: this.showupload = '';
697: }
698:
699: this.selectUpload = function() {
700: this.removeSelection();
701: this.showupload = 'yes';
702: this.updateDisplay(this.resourcespanelid);
703: this.updateDisplay(this.propertiespanelid);
704: }
705: /*** Selecting a resource ***/
706:
707: this.selectItem = function (item, id) {
708: /* select an item in the item pane, show the item's metadata */
709:
710: // First turn off current selection, if any
711: this.removeSelection();
712:
713: // Grab XML DOM node for clicked "resource" and mark it selected
714: var newselxpath = '/libraries/*[@selected]//resource[@id="' + id + '"]';
715: var newselitem = this.shared.xmldata.selectSingleNode(newselxpath);
716: newselitem.setAttribute("selected", "1");
717: //this.updateDisplay(this.resourcespanelid);
718: this.updateDisplay(this.propertiespanelid);
719:
720: // Don't want to reload the resource panel xml as it scrolls to
721: // the top.
722: var span = item.getElementsByTagName('span');
723: if (span.length > 0) {
724: span = span[0];
725: span.className += ' selected-item';
726: }
727: this.selecteditemid = id;
728: if (this.editor.getBrowserName() == 'IE') {
729: var ppanel = document.getElementById(this.propertiespanelid)
730: var height = ppanel.clientHeight;
731: if (height > ppanel.scrollHeight) height = ppanel.scrollHeight;
732: if (height < 260) height = 260;
733: document.getElementById(this.resourcespanelid).style.height = height+'px';
734: }
735: return;
736: }
737:
738:
739: this.search = function() {
740: /* search */
741: var searchvalue = getFromSelector('kupu-searchbox-input').value;
742: //XXX make search variable configurable
743: var body = 'SearchableText=' + escape(searchvalue);
744:
745: // the search uri might contain query parameters in HTTP GET
746: // style. We want to do a POST though, so find any possible
747: // parameters, trim them from the URI and append them to the
748: // POST body instead.
749: var chunks = this.shared.searchuri.split('?');
750: var searchuri = chunks[0];
751: if (chunks[1]) {
752: body += "&" + chunks[1];
753: };
754: var wrapped_callback = new ContextFixer(this._searchCallback, this);
755: this._loadXML(searchuri, wrapped_callback.execute, body);
756: };
757:
758: this._searchCallback = function(dom) {
759: var resultlib = dom.selectSingleNode("/library");
760:
761: var items = resultlib.selectNodes("items/*");
762: if (!items.length) {
763: alert("No results found.");
764: return;
765: };
766:
767: // we need to give the newly retrieved data a unique ID, we
768: // just use the time.
769: date = new Date();
770: time = date.getTime();
771: resultlib.setAttribute("id", time);
772:
773: // deselect the previous collection and mark the result
774: // library as selected
775: this.deselectActiveCollection();
776: resultlib.setAttribute("selected", "1");
777:
778: // now hook the result library into our DOM
779: if (this.editor.getBrowserName() == 'IE') {
780: resultlib = resultlib.cloneNode(true);
781: } else {
782: this.shared.xmldata.importNode(resultlib, true);
783: }
784: var libraries = this.shared.xmldata.selectSingleNode("/libraries");
785: libraries.appendChild(resultlib);
786:
787: this.updateDisplay(this.drawerid);
788: var newseldiv = getFromSelector(time);
789: newseldiv.className = 'selected';
790: };
791:
792: this.save = function() {
793: /* save the element, should be implemented on subclasses */
794: throw "Not yet implemented";
795: };
796:
797: /*** Auxiliary methods ***/
798:
799: this._transformXml = function() {
800: /* transform this.shared.xmldata to HTML using this.shared.xsl and return it */
801: var doc = Sarissa.getDomDocument();
802: var result = this.shared.xsltproc.transformToDocument(this.shared.xmldata);
803: return result;
804: };
805:
806: this._loadXML = function(uri, callback, body) {
807: /* load the XML from a uri
808:
809: calls callback with one arg (the XML DOM) when done
810: the (optional) body arg should contain the body for the request
811: */
812: var xmlhttp = new XMLHttpRequest();
813: var method = 'GET';
814: if (body) {
815: method = 'POST';
816: } else {
817: // be sure that body is null and not an empty string or
818: // something
819: body = null;
820: };
821: xmlhttp.open(method, uri, true);
822: // use ContextFixer to wrap the Sarissa callback, both for isolating
823: // the 'this' problem and to be able to pass in an extra argument
824: // (callback)
825: var wrapped_callback = new ContextFixer(this._sarissaCallback, xmlhttp,
826: callback, uri);
827: xmlhttp.onreadystatechange = wrapped_callback.execute;
828: if (method == "POST") {
829: // by default, we would send a 'text/xml' request, which
830: // is a dirty lie; explicitly set the content type to what
831: // a web server expects from a POST.
832: xmlhttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
833: };
834: xmlhttp.send(body);
835: };
836:
837: this._sarissaCallback = function(user_callback, uri) {
838: /* callback for Sarissa
839: when the callback is called because the data's ready it
840: will get the responseXML DOM and call user_callback
841: with the DOM as the first argument and the uri loaded
842: as the second
843:
844: note that this method should be called in the context of an
845: xmlhttp object
846: */
847: var errmessage = 'Error loading XML: ';
848: if (uri) {
849: errmessage = 'Error loading ' + uri + ':';
850: };
851: if (this.readyState == 4) {
852: if (this.status && this.status != 200) {
853: alert(errmessage + this.status);
854: throw "Error loading XML";
855: };
856: var dom = this.responseXML;
857: user_callback(dom, uri);
858: };
859: };
860: };
861:
862: LibraryDrawer.prototype = new Drawer;
863: LibraryDrawer.prototype.shared = {}; // Shared data
864:
865: function ImageLibraryDrawer(tool, xsluri, libsuri, searchuri, baseelement) {
866: /* a specific LibraryDrawer for images */
867:
868: this.drawertitle = "Insert Image";
869: this.drawertype = "image";
870: this.showupload = '';
871: if (tool) {
872: this.init(tool, xsluri, libsuri, searchuri, baseelement);
873: }
874:
875:
876: // upload, on submit/insert press
877: this.uploadImage = function() {
878: var form = document.kupu_upload_form;
879: if (!form || form.node_prop_image.value=='') return;
880:
881: if (form.node_prop_caption.value == "") {
882: alert("Please enter a title for the image you are uploading");
883: return;
884: };
885:
886: var targeturi = this.shared.xmldata.selectSingleNode('/libraries/*[@selected]/uri/text()').nodeValue
887: document.kupu_upload_form.action = targeturi + "/kupuUploadImage";
888: document.kupu_upload_form.submit();
889: };
890:
891: // called for example when no permission to upload for some reason
892: this.cancelUpload = function(msg) {
893: var s = this.shared.xmldata.selectSingleNode('/libraries/*[@selected]');
894: s.removeAttribute("selected");
895: this.updateDisplay();
896: if (msg != '') {
897: alert(msg);
898: };
899: };
900:
901: // called by onLoad within document sent by server
902: this.finishUpload = function(url) {
903: this.editor.resumeEditing();
904: var imgclass = 'image-inline';
905: if (this.editor.config.captions) {
906: imgclass += " captioned";
907: };
908: this.tool.createImage(url, null, imgclass);
909: this.shared.newimages = 1;
910: this.drawertool.closeDrawer();
911: };
912:
913:
914: this.save = function() {
915: this.editor.resumeEditing();
916: /* create an image in the iframe according to collected data
917: from the drawer */
918: var selxpath = '//resource[@selected]';
919: var selnode = this.shared.xmldata.selectSingleNode(selxpath);
920:
921: // If no image resource is selected, check for upload
922: if (!selnode) {
923: var uploadbutton = this.shared.xmldata.selectSingleNode("/libraries/*[@selected]//uploadbutton");
924: if (uploadbutton) {
925: this.uploadImage();
926: };
927: return;
928: };
929:
930: var uri = selnode.selectSingleNode('uri/text()').nodeValue;
931: uri = uri.strip(); // needs kupuhelpers.js
932: var alt = getFromSelector('image_alt').value;
933:
934: var radios = document.getElementsByName('image-align');
935: for (var i = 0; i < radios.length; i++) {
936: if (radios[i].checked) {
937: var imgclass = radios[i].value;
938: };
939: };
940:
941: var caption = document.getElementsByName('image-caption');
942: if (caption && caption.length>0 && caption[0].checked) {
943: imgclass += " captioned";
944: };
945:
946: this.tool.createImage(uri, alt, imgclass);
947: this.drawertool.closeDrawer();
948: };
949: };
950:
951: ImageLibraryDrawer.prototype = new LibraryDrawer;
952: ImageLibraryDrawer.prototype.shared = {}; // Shared data
953:
954: function LinkLibraryDrawer(tool, xsluri, libsuri, searchuri, baseelement) {
955: /* a specific LibraryDrawer for links */
956:
957: this.drawertitle = "Insert Link";
958: this.drawertype = "link";
959: this.showupload = '';
960: if (tool) {
961: this.init(tool, xsluri, libsuri, searchuri, baseelement);
962: }
963:
964: this.save = function() {
965: this.editor.resumeEditing();
966: /* create a link in the iframe according to collected data
967: from the drawer */
968: var selxpath = '//resource[@selected]';
969: var selnode = this.shared.xmldata.selectSingleNode(selxpath);
970: if (!selnode) {
971: return;
972: };
973:
974: var uri = selnode.selectSingleNode('uri/text()').nodeValue;
975: uri = uri.strip(); // needs kupuhelpers.js
976: var title = '';
977: title = selnode.selectSingleNode('title/text()').nodeValue;
978: title = title.strip();
979:
980: // XXX requiring the user to know what link type to enter is a
981: // little too much I think. (philiKON)
982: var type = null;
983: var name = getFromSelector('link_name').value;
984: var target = null;
985: if (getFromSelector('link_target') && getFromSelector('link_target').value != '')
986: target = getFromSelector('link_target').value;
987:
988: this.tool.createLink(uri, type, name, target, title);
989: this.drawertool.closeDrawer();
990: };
991: };
992:
993: LinkLibraryDrawer.prototype = new LibraryDrawer;
994: LinkLibraryDrawer.prototype.shared = {}; // Shared data
995:
996: /* Function to suppress enter key in drawers */
997: function HandleDrawerEnter(event, clickid) {
998: var key;
999: event = event || window.event;
1000: key = event.which || event.keyCode;
1001:
1002: if (key==13) {
1003: if (clickid) {
1004: var button = document.getElementById(clickid);
1005: if (button) {
1006: button.click();
1007: }
1008: }
1009: event.cancelBubble = true;
1010: if (event.stopPropogation) event.stopPropogation();
1011:
1012: return false;
1013: }
1014: return true;
1015: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>