Annotation of kupuMPIWG/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>