You are viewing a plain text version of this content. The canonical link for it is here.
Posted to graffito-commits@incubator.apache.org by cl...@apache.org on 2005/02/17 23:57:31 UTC

svn commit: r154214 [5/10] - in incubator/graffito/trunk/applications: ./ browser/ browser/src/ browser/src/java/ browser/src/java/org/ browser/src/java/org/apache/ browser/src/java/org/apache/portals/ browser/src/java/org/apache/portals/graffito/ browser/src/java/org/apache/portals/graffito/portlets/ browser/src/java/org/apache/portals/graffito/portlets/resources/ browser/src/java/org/apache/portals/graffito/portlets/util/ browser/src/java/org/apache/portals/graffito/servlets/ browser/src/java/org/apache/portals/graffito/util/ browser/src/webapp/ browser/src/webapp/WEB-INF/ browser/src/webapp/WEB-INF/tabs/ browser/src/webapp/WEB-INF/velocity/ browser/src/webapp/WEB-INF/view/ browser/src/webapp/WEB-INF/view/document/ browser/src/webapp/WEB-INF/view/folder/ browser/src/webapp/WEB-INF/view/security/ browser/src/webapp/kupu/ browser/src/webapp/kupu/kupudrawers/ browser/src/webapp/kupu/kupudrawers/logos/ browser/src/webapp/kupu/kupuimages/ browser/src/webapp/kupu/kupupopups/

Added: incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucnftable.js
URL: http://svn.apache.org/viewcvs/incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucnftable.js?view=auto&rev=154214
==============================================================================
--- incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucnftable.js (added)
+++ incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucnftable.js Thu Feb 17 15:57:09 2005
@@ -0,0 +1,135 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2004 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ *
+ *****************************************************************************/
+
+// $Id: kupubasetools.js 6120 2004-08-22 23:23:42Z roku $
+
+TableTool.prototype.setTableRowRepeat = function() {
+    var selNode = this.editor.getSelectedNode();
+    var row = this.editor.getNearestParentOfType(selNode, 'tr');
+    if (!row) {
+        this.editor.logMessage('Not inside a row!', 1);
+        return;
+    };
+    row.setAttribute('repeatable', 'repeatable');
+    row.className = 'repeatable';
+    this.editor.logMessage('Row repeated');
+    this.updateState(selNode);
+};
+
+TableTool.prototype.delTableRowRepeat = function() {
+    var selNode = this.editor.getSelectedNode();
+    var row = this.editor.getNearestParentOfType(selNode, 'tr');
+    if (!row) {
+        this.editor.logMessage('Not inside a row!', 1);
+        return;
+    };
+    row.removeAttribute('repeatable');
+    row.className = '';
+    row.removeAttribute('class');
+    this.editor.logMessage('Row repeat turned off');
+    this.updateState(selNode);
+};
+
+function CNFTableToolBox(addtabledivid, edittabledivid, newrowsinputid, 
+                    newcolsinputid, makeheaderinputid, classselectid, alignselectid, addtablebuttonid,
+                    addrowbuttonid, delrowbuttonid, setrowrepeatbuttonid, delrowrepeatbuttonid,
+                    addcolbuttonid, delcolbuttonid, fixbuttonid,
+                    fixallbuttonid, toolboxid, plainclass, activeclass) {
+
+    this.addtablediv = document.getElementById(addtabledivid);
+    this.edittablediv = document.getElementById(edittabledivid);
+    this.newrowsinput = document.getElementById(newrowsinputid);
+    this.newcolsinput = document.getElementById(newcolsinputid);
+    this.makeheaderinput = document.getElementById(makeheaderinputid);
+    this.classselect = document.getElementById(classselectid);
+    this.alignselect = document.getElementById(alignselectid);
+    this.addtablebutton = document.getElementById(addtablebuttonid);
+    this.addrowbutton = document.getElementById(addrowbuttonid);
+    this.delrowbutton = document.getElementById(delrowbuttonid);
+    this.setrowrepeatbutton = document.getElementById(setrowrepeatbuttonid);
+    this.delrowrepeatbutton = document.getElementById(delrowrepeatbuttonid);
+    this.addcolbutton = document.getElementById(addcolbuttonid);
+    this.delcolbutton = document.getElementById(delcolbuttonid);
+    this.fixbutton = document.getElementById(fixbuttonid);
+    this.fixallbutton = document.getElementById(fixallbuttonid);
+    this.toolboxel = document.getElementById(toolboxid);
+    this.plainclass = plainclass;
+    this.activeclass = activeclass;
+
+    this.initialize = function(tool, editor) {
+        /* attach the event handlers */
+        this.tool = tool;
+        this.editor = editor;
+        // build the select list of table classes if configured
+        if (this.editor.config.table_classes) {
+            var classes = this.editor.config.table_classes['class'];
+            while (this.classselect.hasChildNodes()) {
+                this.classselect.removeChild(this.classselect.firstChild);
+            };
+            for (var i=0; i < classes.length; i++) {
+                var classname = classes[i];
+                var option = document.createElement('option');
+                var content = document.createTextNode(classname);
+                option.appendChild(content);
+                option.setAttribute('value', classname);
+                this.classselect.appendChild(option);
+            };
+        };
+        addEventHandler(this.addtablebutton, "click", this.addTable, this);
+        addEventHandler(this.addrowbutton, "click", this.tool.addTableRow, this.tool);
+        addEventHandler(this.delrowbutton, "click", this.tool.delTableRow, this.tool);
+        addEventHandler(this.setrowrepeatbutton, "click", this.tool.setTableRowRepeat, this.tool);
+        addEventHandler(this.delrowrepeatbutton, "click", this.tool.delTableRowRepeat, this.tool);
+        addEventHandler(this.addcolbutton, "click", this.tool.addTableColumn, this.tool);
+        addEventHandler(this.delcolbutton, "click", this.tool.delTableColumn, this.tool);
+        addEventHandler(this.alignselect, "change", this.setColumnAlign, this);
+        addEventHandler(this.classselect, "change", this.setTableClass, this);
+        addEventHandler(this.fixbutton, "click", this.tool.fixTable, this.tool);
+        addEventHandler(this.fixallbutton, "click", this.tool.fixAllTables, this.tool);
+        this.addtablediv.style.display = "block";
+        this.edittablediv.style.display = "none";
+        this.editor.logMessage('Table tool initialized');
+    };
+
+    this.updateState = function(selNode) {
+        /* update the state (add/edit) and update the pulldowns (if required) */
+        var table = this.editor.getNearestParentOfType(selNode, 'table');
+        if (table) {
+            this.addtablediv.style.display = "none";
+            this.edittablediv.style.display = "block";
+
+            var align = this.tool._getColumnAlign(selNode);
+            selectSelectItem(this.alignselect, align);
+            selectSelectItem(this.classselect, table.className);
+            if (this.toolboxel) {
+                this.toolboxel.className = this.activeclass;
+            };
+            var row = this.editor.getNearestParentOfType(selNode, 'tr');
+            var isRepeatable = row.getAttribute('repeatable');
+            if (isRepeatable) {
+                this.setrowrepeatbutton.style.display = 'none';
+                this.delrowrepeatbutton.style.display = 'inline';
+            } else {
+                this.setrowrepeatbutton.style.display = 'inline';
+                this.delrowrepeatbutton.style.display = 'none';
+            };
+        } else {
+            this.edittablediv.style.display = "none";
+            this.addtablediv.style.display = "block";
+            this.alignselect.selectedIndex = 0;
+            this.classselect.selectedIndex = 0;
+            if (this.toolboxel) {
+                this.toolboxel.className = this.plainclass;
+            };
+        };
+    };
+};
+
+CNFTableToolBox.prototype = new TableToolBox;

Added: incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontentfilters.js
URL: http://svn.apache.org/viewcvs/incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontentfilters.js?view=auto&rev=154214
==============================================================================
--- incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontentfilters.js (added)
+++ incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontentfilters.js Thu Feb 17 15:57:09 2005
@@ -0,0 +1,605 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2004 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ *
+ *****************************************************************************/
+
+// $Id: kupucontentfilters.js 7950 2004-12-21 13:19:30Z duncan $
+
+
+//----------------------------------------------------------------------------
+// 
+// ContentFilters
+//
+//  These are (or currently 'this is') filters for HTML cleanup and 
+//  conversion. Kupu filters should be classes that should get registered to
+//  the editor using the registerFilter method with 2 methods: 'initialize'
+//  and 'filter'. The first will be called with the editor as its only
+//  argument and the latter with a reference to the ownerdoc (always use 
+//  that to create new nodes and such) and the root node of the HTML DOM as 
+//  its arguments.
+//
+//----------------------------------------------------------------------------
+
+function NonXHTMLTagFilter() {
+    /* filter out non-XHTML tags*/
+    
+    // A mapping from element name to whether it should be left out of the 
+    // document entirely. If you want an element to reappear in the resulting 
+    // document *including* it's contents, add it to the mapping with a 1 value.
+    // If you want an element not to appear but want to leave it's contents in 
+    // tact, add it to the mapping with a 0 value. If you want an element and
+    // it's contents to be removed from the document, don't add it.
+    if (arguments.length) {
+        // allow an optional filterdata argument
+        this.filterdata = arguments[0];
+    } else {
+        // provide a default filterdata dict
+        this.filterdata = {'html': 1,
+                            'body': 1,
+                            'head': 1,
+                            'title': 1,
+                            
+                            'a': 1,
+                            'abbr': 1,
+                            'acronym': 1,
+                            'address': 1,
+                            'b': 1,
+                            'base': 1,
+                            'blockquote': 1,
+                            'br': 1,
+                            'caption': 1,
+                            'cite': 1,
+                            'code': 1,
+                            'col': 1,
+                            'colgroup': 1,
+                            'dd': 1,
+                            'dfn': 1,
+                            'div': 1,
+                            'dl': 1,
+                            'dt': 1,
+                            'em': 1,
+                            'h1': 1,
+                            'h2': 1,
+                            'h3': 1,
+                            'h4': 1,
+                            'h5': 1,
+                            'h6': 1,
+                            'h7': 1,
+                            'i': 1,
+                            'img': 1,
+                            'kbd': 1,
+                            'li': 1,
+                            'link': 1,
+                            'meta': 1,
+                            'ol': 1,
+                            'p': 1,
+                            'pre': 1,
+                            'q': 1,
+                            'samp': 1,
+                            'script': 1,
+                            'span': 1,
+                            'strong': 1,
+                            'style': 1,
+                            'sub': 1,
+                            'sup': 1,
+                            'table': 1,
+                            'tbody': 1,
+                            'td': 1,
+                            'tfoot': 1,
+                            'th': 1,
+                            'thead': 1,
+                            'tr': 1,
+                            'ul': 1,
+                            'u': 1,
+                            'var': 1,
+
+                            // even though they're deprecated we should leave
+                            // font tags as they are, since Kupu sometimes
+                            // produces them itself.
+                            'font': 1,
+                            'center': 0
+                            };
+    };
+                        
+    this.initialize = function(editor) {
+        /* init */
+        this.editor = editor;
+    };
+
+    this.filter = function(ownerdoc, htmlnode) {
+        return this._filterHelper(ownerdoc, htmlnode);
+    };
+
+    this._filterHelper = function(ownerdoc, node) {
+        /* filter unwanted elements */
+        if (node.nodeType == 3) {
+            return ownerdoc.createTextNode(node.nodeValue);
+        } else if (node.nodeType == 4) {
+            return ownerdoc.createCDATASection(node.nodeValue);
+        };
+        // create a new node to place the result into
+        // XXX this can be severely optimized by doing stuff inline rather 
+        // than on creating new elements all the time!
+        var newnode = ownerdoc.createElement(node.nodeName);
+        // copy the attributes
+        for (var i=0; i < node.attributes.length; i++) {
+            var attr = node.attributes[i];
+            newnode.setAttribute(attr.nodeName, attr.nodeValue);
+        };
+        for (var i=0; i < node.childNodes.length; i++) {
+            var child = node.childNodes[i];
+            var nodeType = child.nodeType;
+            var nodeName = child.nodeName.toLowerCase();
+            if (nodeType == 3 || nodeType == 4) {
+                newnode.appendChild(this._filterHelper(ownerdoc, child));
+            };
+            if (nodeName in this.filterdata && this.filterdata[nodeName]) {
+                newnode.appendChild(this._filterHelper(ownerdoc, child));
+            } else if (nodeName in this.filterdata) {
+                for (var j=0; j < child.childNodes.length; j++) {
+                    newnode.appendChild(this._filterHelper(ownerdoc, child.childNodes[j]));
+                };
+            };
+        };
+        return newnode;
+    };
+};
+
+//-----------------------------------------------------------------------------
+//
+// XHTML validation support
+//
+// This class is the XHTML 1.0 transitional DTD expressed as Javascript
+// data structures.
+//
+function XhtmlValidation(editor) {
+    // Support functions
+    this.Set = function(ary) {
+        if (typeof(ary)==typeof('')) ary = [ary];
+        if (ary instanceof Array) {
+            for (var i = 0; i < ary.length; i++) {
+                this[ary[i]] = 1;
+            }
+        }
+        else {
+            for (var v in ary) { // already a set?
+                this[v] = 1;
+            }
+        }
+    }
+
+    this._exclude = function(array, exceptions) {
+        var ex;
+        if (exceptions.split) {
+            ex = exceptions.split("|");
+        } else {
+            ex = exceptions;
+        }
+        var exclude = new this.Set(ex);
+        var res = [];
+        for (var k=0; k < array.length;k++) {
+            if (!exclude[array[k]]) res.push(array[k]);
+        }
+        return res;
+    }
+    this.setAttrFilter = function(attributes, filter) {
+        for (var j = 0; j < attributes.length; j++) {
+            var attr = attributes[j];
+            this.attrFilters[attr] = filter || this._defaultCopyAttribute;
+        }
+    }
+
+    this.setTagAttributes = function(tags, attributes) {
+        for (var j = 0; j < tags.length; j++) {
+            this.tagAttributes[tags[j]] = attributes;
+        }
+    }
+
+    // define some new attributes for existing tags
+    this.includeTagAttributes = function(tags, attributes) {
+        for (var j = 0; j < tags.length; j++) {
+            var tag = tags[j];
+            this.tagAttributes[tag] = this.tagAttributes[tag].concat(attributes);
+        }
+    }
+
+    this.excludeTagAttributes = function(tags, attributes) {
+        var bad = new this.Set(attributes);
+        var tagset = new this.Set(tags);
+        for (var tag in tagset) {
+            var val = this.tagAttributes[tag];
+            for (var i = val.length; i >= 0; i--) {
+                if (bad[val[i]]) {
+                    val = val.concat(); // Copy
+                    val.splice(i,1);
+                }
+            }
+            this.tagAttributes[tag] = val;
+        }
+    }
+
+    this.excludeTags = function(badtags) {
+        if (typeof(badtags)==typeof('')) badtags = [badtags];
+        for (var i = 0; i < badtags.length; i++) {
+            delete this.tagAttributes[badtags[i]];
+        }
+    }
+
+    this.excludeAttributes = function(badattrs) {
+        this.excludeTagAttributes(this.tagAttributes, badattrs);
+        for (var i = 0; i < badattrs.length; i++) {
+            delete this.attrFilters[badattrs[i]];
+        }
+    }
+    if (editor.getBrowserName()=="IE") {
+        this._getTagName = function(htmlnode) {
+            var nodename = htmlnode.nodeName.toLowerCase();
+            if (htmlnode.scopeName && htmlnode.scopeName != "HTML") {
+                nodename = htmlnode.scopeName+':'+nodename;
+            }
+            return nodename;
+        }
+    } else {
+        this._getTagName = function(htmlnode) {
+            return htmlnode.nodeName.toLowerCase();
+        }
+    };
+
+    // Supporting declarations
+    this.elements = new function(validation) {
+        // A list of all attributes
+        this.attributes = [
+            'abbr','accept','accept-charset','accesskey','action','align','alink',
+            'alt','archive','axis','background','bgcolor','border','cellpadding',
+            'cellspacing','char','charoff','charset','checked','cite','class',
+            'classid','clear','code','codebase','codetype','color','cols','colspan',
+            'compact','content','coords','data','datetime','declare','defer','dir',
+            'disabled','enctype','face','for','frame','frameborder','halign','headers',
+            'height','href','hreflang','hspace','http-equiv','id','ismap','label',
+            'lang','language','link','longdesc','marginheight','marginwidth',
+            'maxlength','media','method','multiple','name','nohref','noshade','nowrap',
+            'object','onblur','onchange','onclick','ondblclick','onfocus','onkeydown',
+            'onkeypress','onkeyup','onload','onmousedown','onmousemove','onmouseout',
+            'onmouseover','onmouseup','onreset','onselect','onsubmit','onunload',
+            'profile','prompt','readonly','rel','rev','rows','rowspan','rules',
+            'scheme','scope','scrolling','selected','shape','size','span','src',
+            'standby','start','style','summary','tabindex','target','text','title',
+            'type','usemap','valign','value','valuetype','vlink','vspace','width',
+            'xml:lang','xml:space','xmlns'];
+
+        // Core attributes
+        this.coreattrs = ['id', 'title', 'style', 'class'];
+        this.i18n = ['lang', 'dir', 'xml:lang'];
+        // All event attributes are here but commented out so we don't
+        // have to remove them later.
+        this.events = []; // 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup'.split('|');
+        this.focusevents = []; // ['onfocus','onblur']
+        this.loadevents = []; // ['onload', 'onunload']
+        this.formevents = []; // ['onsubmit','onreset']
+        this.inputevents = [] ; // ['onselect', 'onchange']
+        this.focus = ['accesskey', 'tabindex'].concat(this.focusevents);
+        this.attrs = [].concat(this.coreattrs, this.i18n, this.events);
+
+        // entities
+        this.special_extra = ['object','applet','img','map','iframe'];
+        this.special_basic=['br','span','bdo'];
+        this.special = [].concat(this.special_basic, this.special_extra);
+        this.fontstyle_extra = ['big','small','font','basefont'];
+        this.fontstyle_basic = ['tt','i','b','u','s','strike'];
+        this.fontstyle = [].concat(this.fontstyle_basic, this.fontstyle_extra);
+        this.phrase_extra = ['sub','sup'];
+        this.phrase_basic=[
+                          'em','strong','dfn','code','q',
+                          'samp','kbd','var', 'cite','abbr','acronym'];
+        this.inline_forms = ['input','select','textarea','label','button'];
+        this.misc_inline = ['ins','del'];
+        this.misc = ['noscript'].concat(this.misc_inline);
+        this.inline = ['a'].concat(this.special, this.fontstyle, this.phrase, this.inline_forms);
+
+        this.Inline = ['#PCDATA'].concat(this.inline, this.misc_inline);
+
+        this.heading = ['h1','h2','h3','h4','h5','h6'];
+        this.lists = ['ul','ol','dl','menu','dir'];
+        this.blocktext = ['pre','hr','blockquote','address','center','noframes'];
+        this.block = ['p','div','isindex','fieldset','table'].concat(
+                     this.heading, this.lists, this.blocktext);
+
+        this.Flow = ['#PCDATA','form'].concat(this.block, this.inline);
+    }(this);
+
+    this._commonsetting = function(self, names, value) {
+        for (var n = 0; n < names.length; n++) {
+            self[names[n]] = value;
+        }
+    }
+    
+    // The tagAttributes class returns all valid attributes for a tag,
+    // e.g. a = this.tagAttributes.head
+    // a.head -> [ 'lang', 'xml:lang', 'dir', 'id', 'profile' ]
+    this.tagAttributes = new function(el, validation) {
+        this.title = el.i18n.concat('id');
+        this.html = this.title.concat('xmlns');
+        this.head = this.title.concat('profile');
+        this.base = ['id', 'href', 'target'];
+        this.meta =  this.title.concat('http-equiv','name','content', 'scheme');
+        this.link = el.attrs.concat('charset','href','hreflang','type', 'rel','rev','media','target');
+        this.style = this.title.concat('type','media','title', 'xml:space');
+        this.script = ['id','charset','type','language','src','defer', 'xml:space'];
+        this.iframe = [
+                      'longdesc','name','src','frameborder','marginwidth',
+                      'marginheight','scrolling','align','height','width'].concat(el.coreattrs);
+        this.body = ['background','bgcolor','text','link','vlink','alink'].concat(el.attrs, el.loadevents);
+        validation._commonsetting(this,
+                                  ['p','div'].concat(el.heading),
+                                  ['align'].concat(el.attrs));
+        this.dl = this.dir = this.menu = el.attrs.concat('compact');
+        this.ul = this.menu.concat('type');
+        this.ol = this.ul.concat('start');
+        this.li = el.attrs.concat('type','value');
+        this.hr = el.attrs.concat('align','noshade','size','width');
+        this.pre = el.attrs.concat('width','xml:space');
+        this.blockquote = this.q = el.attrs.concat('cite');
+        this.ins = this.del = this.blockquote.concat('datetime');
+        this.a = el.attrs.concat(el.focus,'charset','type','name','href','hreflang','rel','rev','shape','coords','target');
+        this.bdo = el.coreattrs.concat(el.events, 'lang','xml:lang','dir');
+        this.br = el.coreattrs.concat('clear');
+        validation._commonsetting(this,
+                                  ['noscript','noframes','dt', 'dd', 'address','center','span','em', 'strong', 'dfn','code',
+                                  'samp','kbd','var','cite','abbr','acronym','sub','sup','tt',
+                                  'i','b','big','small','u','s','strike', 'fieldset'],
+                                  el.attrs);
+
+        this.basefont = ['id','size','color','face'];
+        this.font = el.coreattrs.concat(el.i18n, 'size','color','face');
+        this.object = el.attrs.concat('declare','classid','codebase','data','type','codetype','archive','standby','height','width','usemap','name','tabindex','align','border','hspace','vspace');
+        this.param = ['id','name','value','valuetype','type'];
+        this.applet = el.coreattrs.concat('codebase','archive','code','object','alt','name','width','height','align','hspace','vspace');
+        this.img = el.attrs.concat('src','alt','name','longdesc','height','width','usemap','ismap','align','border','hspace','vspace');
+        this.map = this.title.concat('title','name', 'style', 'class', el.events);
+        this.area = el.attrs.concat('shape','coords','href','nohref','alt','target', el.focus);
+        this.form = el.attrs.concat('action','method','name','enctype',el.formevents,'accept','accept-charset','target');
+        this.label = el.attrs.concat('for','accesskey', el.focusevents);
+        this.input = el.attrs.concat('type','name','value','checked','disabled','readonly','size','maxlength','src','alt','usemap',el.input,'accept','align', el.focus);
+        this.select = el.attrs.concat('name','size','multiple','disabled','tabindex', el.focusevents,el.input);
+        this.optgroup = el.attrs.concat('disabled','label');
+        this.option = el.attrs.concat('selected','disabled','label','value');
+        this.textarea = el.attrs.concat('name','rows','cols','disabled','readonly', el.inputevents, el.focus);
+        this.legend = el.attrs.concat('accesskey','align');
+        this.button = el.attrs.concat('name','value','type','disabled',el.focus);
+        this.isindex = el.coreattrs.concat('prompt', el.i18n);
+        this.table = el.attrs.concat('summary','width','border','frame','rules','cellspacing','cellpadding','align','bgcolor');
+        this.caption = el.attrs.concat('align');
+        this.col = this.colgroup = el.attrs.concat('span','width','halign','char','charoff','valign');
+        this.thead =  el.attrs.concat('halign','char','charoff','valign');
+        this.tfoot = this.tbody = this.thead;
+        this.tr = this.thead.concat('bgcolor');
+        this.td = this.th = this.tr.concat('abbr','axis','headers','scope','rowspan','colspan','nowrap','width','height');
+    }(this.elements, this);
+
+    // State array. For each tag identifies what it can contain.
+    // I'm not attempting to check the order or number of contained
+    // tags (yet).
+    this.States = new function(el, validation) {
+
+        var here = this;
+        function setStates(tags, value) {
+            var valset = new validation.Set(value);
+
+            for (var i = 0; i < tags.length; i++) {
+                here[tags[i]] = valset;
+            }
+        }
+        
+        setStates(['html'], ['head','body']);
+        setStates(['head'], ['title','base','script','style', 'meta','link','object','isindex']);
+        setStates([
+            'base', 'meta', 'link', 'hr', 'param', 'img', 'area', 'input',
+            'br', 'basefont', 'isindex', 'col',
+            ], []);
+
+        setStates(['title','style','script','option','textarea'], ['#PCDATA']);
+        setStates([ 'noscript', 'iframe', 'noframes', 'body', 'div',
+            'li', 'dd', 'blockquote', 'center', 'ins', 'del', 'td', 'th',
+            ], el.Flow);
+
+        setStates(el.heading, el.Inline);
+        setStates([ 'p', 'dt', 'address', 'span', 'bdo', 'caption',
+            'em', 'strong', 'dfn','code','samp','kbd','var',
+            'cite','abbr','acronym','q','sub','sup','tt','i',
+            'b','big','small','u','s','strike','font','label',
+            'legend'], el.Inline);
+
+        setStates(['ul', 'ol', 'menu', 'dir', 'ul', ], ['li']);
+        setStates(['dl'], ['dt','dd']);
+        setStates(['pre'], validation._exclude(el.Inline, "img|object|applet|big|small|sub|sup|font|basefont"));
+        setStates(['a'], validation._exclude(el.Inline, "a"));
+        setStates(['applet', 'object'], ['#PCDATA', 'param','form'].concat(el.block, el.inline, el.misc));
+        setStates(['map'], ['form', 'area'].concat(el.block, el.misc));
+        setStates(['form'], validation._exclude(el.Flow, ['form']));
+        setStates(['select'], ['optgroup','option']);
+        setStates(['optgroup'], ['option']);
+        setStates(['fieldset'], ['#PCDATA','legend','form'].concat(el.block,el.inline,el.misc));
+        setStates(['button'], validation._exclude(el.Flow, ['a','form','iframe'].concat(el.inline_forms)));
+        setStates(['table'], ['caption','col','colgroup','thead','tfoot','tbody','tr']);
+        setStates(['thead', 'tfoot', 'tbody'], ['tr']);
+        setStates(['colgroup'], ['col']);
+        setStates(['tr'], ['th','td']);
+    }(this.elements, this);
+
+    // Permitted elements for style.
+    this.styleWhitelist = new this.Set(['text-align', 'list-style-type']);
+    this.classBlacklist = new this.Set(['MsoNormal', 'MsoTitle', 'MsoHeader', 'MsoFootnoteText',
+        'Bullet1', 'Bullet2']);
+
+    this.classFilter = function(value) {
+        var classes = value.split(' ');
+        var filtered = [];
+        for (var i = 0; i < classes.length; i++) {
+            var c = classes[i];
+            if (c && !this.classBlacklist[c]) {
+                filtered.push(c);
+            }
+        }
+        return filtered.join(' ');
+    }
+    this._defaultCopyAttribute = function(name, htmlnode, xhtmlnode) {
+        var val = htmlnode.getAttribute(name);
+        if (val) xhtmlnode.setAttribute(name, val);
+    }
+    // Set up filters for attributes.
+    this.attrFilters = new function(validation, editor) {
+        var attrs = validation.elements.attributes;
+        for (var i=0; i < attrs.length; i++) {
+            this[attrs[i]] = validation._defaultCopyAttribute;
+        }
+        this['class'] = function(name, htmlnode, xhtmlnode) {
+            var val = htmlnode.getAttribute('class');
+            if (val) val = validation.classFilter(val);
+            if (val) xhtmlnode.setAttribute('class', val);
+        }
+        if (editor.getBrowserName()=="IE") {
+            this['class'] = function(name, htmlnode, xhtmlnode) {
+                var val = htmlnode.className;
+                if (val) val = validation.classFilter(val);
+                if (val) xhtmlnode.setAttribute('class', val);
+            }
+            this['http-equiv'] = function(name, htmlnode, xhtmlnode) {
+                var val = htmlnode.httpEquiv;
+                if (val) xhtmlnode.setAttribute('http-equiv', val);
+            }
+            this['xml:lang'] = this['xml:space'] = function(name, htmlnode, xhtmlnode) {
+                try {
+                    var val = htmlnode.getAttribute(name);
+                    if (val) xhtmlnode.setAttribute(name, val);
+                } catch(e) {
+                }
+            }
+        }
+        this.rowspan = this.colspan = function(name, htmlnode, xhtmlnode) {
+            var val = htmlnode.getAttribute(name);
+            if (val && val != '1') xhtmlnode.setAttribute(name, val);
+        }
+        this.style = function(name, htmlnode, xhtmlnode) {
+            var val = htmlnode.style.cssText;
+            if (val) {
+                var styles = val.split(/; */);
+                for (var i = styles.length; i >= 0; i--) if (styles[i]) {
+                    var parts = /^([^:]+): *(.*)$/.exec(styles[i]);
+                    var name = parts[1].toLowerCase();
+                    if (validation.styleWhitelist[name]) {
+                        styles[i] = name+': '+parts[2];
+                    } else {
+                        styles.splice(i,1); // delete
+                    }
+                }
+                if (styles[styles.length-1]) styles.push('');
+                val = styles.join('; ').strip();
+            }
+            if (val) xhtmlnode.setAttribute('style', val);
+        }
+    }(this, editor);
+
+    // Exclude unwanted tags.
+    this.excludeTags(['center']);
+
+    if (editor.config && editor.config.htmlfilter) {
+        this.filterStructure = editor.config.htmlfilter.filterstructure;
+        
+        var exclude = editor.config.htmlfilter;
+        if (exclude.a)
+            this.excludeAttributes(exclude.a);
+        if (exclude.t)
+            this.excludeTags(exclude.t);
+        if (exclude.c) {
+            var c = exclude.c;
+            if (!c.length) c = [c];
+            for (var i = 0; i < c.length; i++) {
+                this.excludeTagAttributes(c[i].t, c[i].a);
+            }
+        }
+        if (exclude.style) {
+            var s = exclude.style;
+            for (var i = 0; i < s.length; i++) {
+                this.styleWhitelist[s[i]] = 1;
+            }
+        }
+        if (exclude['class']) {
+            var c = exclude['class'];
+            for (var i = 0; i < c.length; i++) {
+                this.classBlacklist[c[i]] = 1;
+            }
+        }
+    };
+
+    // Copy all valid attributes from htmlnode to xhtmlnode.
+    this._copyAttributes = function(htmlnode, xhtmlnode, valid) {
+        for (var i = 0; i < valid.length; i++) {
+            var name = valid[i];
+            var filter = this.attrFilters[name];
+            if (filter) filter(name, htmlnode, xhtmlnode);
+        }
+    }
+
+    this._convertToSarissaNode = function(ownerdoc, htmlnode, xhtmlparent) {
+        return this._convertNodes(ownerdoc, htmlnode, xhtmlparent, new this.Set(['html']));
+    };
+    
+    this._convertNodes = function(ownerdoc, htmlnode, xhtmlparent, permitted) {
+        var name, parentnode = xhtmlparent;
+        var nodename = this._getTagName(htmlnode);
+        var nostructure = !this.filterstructure;
+
+        // TODO: This permits valid tags anywhere. it should use the state
+        // table in xhtmlvalid to only permit tags where the XHTML DTD
+        // says they are valid.
+        var validattrs = this.tagAttributes[nodename];
+        if (validattrs && (nostructure || permitted[nodename])) {
+            try {
+                var xhtmlnode = ownerdoc.createElement(nodename);
+                parentnode = xhtmlnode;
+            } catch (e) { };
+
+            if (validattrs && xhtmlnode)
+                this._copyAttributes(htmlnode, xhtmlnode, validattrs);
+        }
+
+        var kids = htmlnode.childNodes;
+        if (kids && /base|meta|link|hr|param|img|area|input|br|basefont|isindex|col/.exec(nodename)) {
+            kids = []; // IE bug: base can think it has children
+        }
+        var permittedChildren = this.States[parentnode.tagName] || permitted;
+
+        if (kids.length == 0) {
+            if (htmlnode.text && htmlnode.text != "" &&
+                (nostructure || permittedChildren['#PCDATA'])) {
+                var text = htmlnode.text;
+                var tnode = ownerdoc.createTextNode(text);
+                parentnode.appendChild(tnode);
+            }
+        } else {
+            for (var i = 0; i < kids.length; i++) {
+                var kid = kids[i];
+                if (kid.nodeType == 1) {
+                    var newkid = this._convertNodes(ownerdoc, kid, parentnode, permittedChildren);
+                    if (newkid != null) {
+                        parentnode.appendChild(newkid);
+                    };
+                } else if (kid.nodeType == 3) {
+                    if (nostructure || permittedChildren['#PCDATA'])
+                        parentnode.appendChild(ownerdoc.createTextNode(kid.nodeValue));
+                } else if (kid.nodeType == 4) {
+                    if (nostructure || permittedChildren['#PCDATA'])
+                        parentnode.appendChild(ownerdoc.createCDATASection(kid.nodeValue));
+                }
+            }
+        } 
+        return xhtmlnode;
+    };
+}
+
+

Added: incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontextmenu.js
URL: http://svn.apache.org/viewcvs/incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontextmenu.js?view=auto&rev=154214
==============================================================================
--- incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontextmenu.js (added)
+++ incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupucontextmenu.js Thu Feb 17 15:57:09 2005
@@ -0,0 +1,201 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2004 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ *
+ *****************************************************************************/
+
+// $Id: kupucontextmenu.js 7629 2004-11-23 17:05:25Z duncan $
+
+
+//----------------------------------------------------------------------------
+// ContextMenu
+//----------------------------------------------------------------------------
+
+function ContextMenu() {
+    /* the contextmenu */
+    this.contextmenu = null;
+    this.seperator = 1;
+
+    this.initialize = function(editor) {
+        /* set the event handlers and such */
+        this.editor = editor;
+        // needs some work since it won't work for more than one editor
+        addEventHandler(this.editor.getInnerDocument(), "contextmenu", this.createContextMenu, this);
+        //addEventHandler(editor.getInnerDocument(), "focus", this.hideContextMenu, this);
+        addEventHandler(document, "focus", this.hideContextMenu, this);
+        addEventHandler(editor.getInnerDocument(), "mousedown", this.hideContextMenu, this);
+        addEventHandler(document, "mousedown", this.hideContextMenu, this);
+    };
+
+    this.createContextMenu = function(event) {
+        /* Create and show the context menu 
+        
+            The method will ask all tools for any (optional) elements they
+            want to add the menu and when done render it
+        */
+        if (event.stopPropagation) {
+            event.stopPropagation();
+        };
+        event.returnValue = false;
+        if (this.editor.getBrowserName() == 'IE') {
+            this.editor._saveSelection();
+        };
+        // somehow Mozilla on Windows seems to generate the oncontextmenu event
+        // several times on each rightclick, here's a workaround
+        if (this.editor.getBrowserName() == 'Mozilla' && this.contextmenu) {
+            return false;
+        };
+        this.hideContextMenu();
+        var selNode = this.editor.getSelectedNode();
+        var elements = new Array();
+        for (var id in this.editor.tools) {
+            var tool = this.editor.tools[id];
+            // alas, some people seem to want backward compatibility ;)
+            if (tool.createContextMenuElements) {
+                var els = tool.createContextMenuElements(selNode, event);
+                elements = elements.concat(els);
+            };
+        };
+        // remove the last seperator
+        this._createNewContextMenu(elements, event);
+        this.last_event = event;
+        return false;
+    };
+
+    this.hideContextMenu = function(event) {
+        /* remove the context menu from view */
+        if (this.contextmenu) {
+            try {
+                window.document.getElementsByTagName('body')[0].removeChild(this.contextmenu);
+            } catch (e) {
+                // after some commands, the contextmenu will be removed by 
+                // the browser, ignore those cases
+            };
+            this.contextmenu = null;
+        };
+    };
+
+    this._createNewContextMenu = function(elements, event) {
+        /* add the elements to the contextmenu and show it */
+        var doc = window.document;
+        var menu = doc.createElement('div');
+        menu.contentEditable = false;
+        menu.designMode = 'Off';
+        this._setMenuStyle(menu);
+        for (var i=0; i < elements.length; i++) {
+            var element = elements[i];
+            if (element !== this.seperator) {
+                var div = doc.createElement('div');
+                div.style.width = '100%';
+                var label = doc.createTextNode('\u00a0' + element.label);
+                div.appendChild(label);
+                menu.appendChild(div);
+                // set a reference to the div on the element
+                element.element = div;
+                addEventHandler(div, "mousedown", element.action, element.context);
+                addEventHandler(div, "mouseover", element.changeOverStyle, element);
+                addEventHandler(div, "mouseout", element.changeNormalStyle, element);
+                addEventHandler(div, "mouseup", this.hideContextMenu, this);
+            } else {
+                var hr = doc.createElement('hr');
+                menu.appendChild(hr);
+            };
+        };
+        // now move the menu to the right position
+        var iframe = this.editor.getDocument().getEditable();
+        var left = 0;
+        var top = 0;
+        if (this.editor.getBrowserName() == 'IE') {
+            var orgnode = event.srcElement;
+            left = event.clientX;
+            top = event.clientY;
+            var currnode = iframe;
+            while (currnode) {
+                left += currnode.offsetLeft + currnode.clientLeft;
+                top += currnode.offsetTop + currnode.clientTop;
+                currnode = currnode.offsetParent;
+            };
+        } else {
+            left = event.pageX;
+            top = event.pageY;
+            var body = this.editor.getInnerDocument().body;
+            left -= body.scrollLeft;
+            top -= body.scrollTop;
+            var node = iframe;
+            while (node) {
+                left += node.offsetLeft;
+                top += node.offsetTop;
+                node = node.offsetParent;
+            }
+        };
+        //var clienttop = event.clientY;
+        /*if (clienttop > (parseInt(this.editor.getDocument().getWindow().innerHeight) - 
+                    (parseInt(menu.style.lineHeight) * elements.length))) {
+            top -= parseInt(menu.style.lineHeight) * elements.length;
+        };*/
+        menu.style.left = left + 'px';
+        menu.style.top = top + 'px';
+        menu.style.visibility = 'visible';
+        addEventHandler(menu, 'focus', function() {this.blur}, menu)
+        doc.getElementsByTagName('body')[0].appendChild(menu);
+        this.contextmenu = menu;
+    };
+    
+    this._setMenuStyle = function(menu) {
+        /* set the styles for the menu
+
+            to change the menu style, override this method
+        */
+        menu.style.position = 'absolute';
+        menu.style.backgroundColor = 'white';
+        menu.style.fontFamily = 'Verdana, Arial, Helvetica, sans-serif';
+        menu.style.fontSize = '12px';
+        menu.style.lineHeight = '16px';
+        menu.style.borderWidth = '1px';
+        menu.style.borderStyle = 'solid';
+        menu.style.borderColor = 'black';
+        menu.style.cursor = 'default';
+        menu.style.width = "8em";
+    };
+
+    this._showOriginalMenu = function(event) {
+        window.document.dispatchEvent(this._last_event);
+    };
+};
+
+function ContextMenuElement(label, action, context) {
+    /* context menu element struct
+    
+        should be returned (optionally in a list) by the tools' 
+        createContextMenuElements methods
+    */
+    this.label = label; // the text shown in the menu
+    this.action = action; // a reference to the method that should be called
+    this.context = context; // a reference to the object on which the method
+                            //  is defined
+    this.element = null; // the contextmenu machinery will add a reference 
+                            // to the element here
+
+    this.changeOverStyle = function(event) {
+        /* set the background of the element to 'mouseover' style
+
+            override only for the prototype, not for individual elements
+            so every element looks the same
+        */
+        this.element.style.backgroundColor = 'blue';
+    };
+
+    this.changeNormalStyle = function(event) {
+        /* set the background of the element back to 'normal' style
+
+            override only for the prototype, not for individual elements
+            so every element looks the same
+        */
+        this.element.style.backgroundColor = 'white';
+    };
+};
+

Added: incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers.js
URL: http://svn.apache.org/viewcvs/incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers.js?view=auto&rev=154214
==============================================================================
--- incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers.js (added)
+++ incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers.js Thu Feb 17 15:57:09 2005
@@ -0,0 +1,895 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2004 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ * 
+ *****************************************************************************/
+
+// $Id: kupudrawers.js 7887 2004-12-16 16:15:29Z duncan $
+
+function DrawerTool() {
+    /* a tool to open and fill drawers
+
+        this tool has to (and should!) only be instantiated once
+    */
+    this.drawers = {};
+    this.current_drawer = null;
+    
+    this.initialize = function(editor) {
+        this.editor = editor;
+        // this essentially makes the drawertool a singleton
+        window.drawertool = this;
+    };
+
+    this.registerDrawer = function(id, drawer) {
+        this.drawers[id] = drawer;
+        drawer.initialize(this.editor, this);
+    };
+
+    this.openDrawer = function(id) {
+        /* open a drawer */
+        if (this.current_drawer) {
+            this.closeDrawer();
+        };
+        if (this.editor.getBrowserName() == 'IE') {
+            this.editor._saveSelection();
+        }
+        var drawer = this.drawers[id];
+        drawer.createContent();
+        this.current_drawer = drawer;
+    };
+
+    this.updateState = function(selNode) {
+        if (this.current_drawer) {
+            this.closeDrawer();
+        };
+    };
+
+    this.closeDrawer = function() {
+        if (!this.current_drawer) {
+            return;
+        };
+        this.current_drawer.hide();
+        this.current_drawer = null;
+    };
+
+    this.getDrawerEnv = function(iframe_win) {
+        var drawer = null;
+        for (var id in this.drawers) {
+            var ldrawer = this.drawers[id];
+            // Note that we require drawers to provide us with an
+            // element property!
+            if (ldrawer.element.contentWindow == iframe_win) {
+                drawer = ldrawer;
+            };
+        };
+        if (!drawer) {
+            this.editor.logMessage("Drawer not found", 1);
+            return;
+        };
+        return {
+            'drawer': drawer,
+            'drawertool': this,
+            'tool': drawer.tool
+        };
+    };
+};
+
+DrawerTool.prototype = new KupuTool;
+
+function Drawer(elementid, tool) {
+    /* base prototype for drawers */
+
+    this.element = document.getElementById(elementid);
+    this.tool = tool;
+    
+    this.initialize = function(editor, drawertool) {
+        this.editor = editor;
+        this.drawertool = drawertool;
+    };
+    
+    this.createContent = function() {
+        /* fill the drawer with some content */
+        // here's where any intelligence and XSLT transformation and such 
+        // is done
+        this.element.style.display = 'block';
+        if (this.editor.getBrowserName() == 'IE') {
+            this.element.focus();
+        }
+    };
+
+    this.hide = function() {
+        this.element.style.display = 'none';
+    };
+};
+
+function LinkDrawer(elementid, tool) {
+    /* Link drawer */
+    this.element = document.getElementById(elementid);
+    this.tool = tool;
+
+    this.createContent = function() {
+        /* display the drawer */
+        var currnode = this.editor.getSelectedNode();
+        var linkel = this.editor.getNearestParentOfType(currnode, 'a');
+        var input = document.getElementById('kupu-linkdrawer-input');
+        input.value = "";
+        this.preview();
+        if (linkel) {
+            input.value = linkel.getAttribute('href');
+        } else {
+            input.value = 'http://';
+        };
+        this.element.style.display = 'block';
+        if (this.editor.getBrowserName() == 'IE') {
+            this.element.focus();
+        }
+    };
+
+    this.save = function() {
+        /* add or modify a link */
+        var input = document.getElementById('kupu-linkdrawer-input');
+        var url = input.value;
+        var target = '_self';
+        if (this.target) target = this.target;
+        this.tool.createLink(url, null, null, target);
+        input.value = '';
+
+        // XXX when reediting a link, the drawer does not close for
+        // some weird reason. BUG! Close the drawer manually until we
+        // find a fix:
+        this.drawertool.closeDrawer();
+    };
+    
+    this.preview = function() {
+        var input = document.getElementById('kupu-linkdrawer-input');
+        var preview = document.getElementById('kupu-linkdrawer-preview');
+        preview.src = input.value;
+        if (this.editor.getBrowserName() == 'IE') {
+            preview.width = "800";
+            preview.height = "365";
+            preview.style.zoom = "60%";
+        };
+    }
+    this.preview_loaded = function() {
+        var input = document.getElementById('kupu-linkdrawer-input');
+        var preview = document.getElementById('kupu-linkdrawer-preview');
+        if (input.value  != preview.src) {
+            input.value = preview.src;
+        }
+    }
+};
+
+LinkDrawer.prototype = new Drawer;
+
+function TableDrawer(elementid, tool) {
+    /* Table drawer */
+    this.element = document.getElementById(elementid);
+    this.tool = tool;
+
+    this.addpanelid = 'kupu-tabledrawer-addtable';
+    this.editpanelid = 'kupu-tabledrawer-edittable';
+
+    this.addpanel = document.getElementById(this.addpanelid);
+    this.editpanel = document.getElementById(this.editpanelid);
+
+    this.createContent = function() {
+        var selNode = this.editor.getSelectedNode();
+        if (this.editor.config.table_classes) {
+            var classselect = document.getElementById('kupu-tabledrawer-classchooser');
+            var classes = this.editor.config.table_classes['class'];
+            while (classselect.hasChildNodes()) {
+                classselect.removeChild(classselect.firstChild);
+            };
+            for (var i=0; i < classes.length; i++) {
+                var classname = classes[i];
+                var option = document.createElement('option');
+                var content = document.createTextNode(classname);
+                option.appendChild(content);
+                option.setAttribute('value', classname);
+                classselect.appendChild(option);
+            };
+        };
+        
+        var table = this.editor.getNearestParentOfType(selNode, 'table');
+
+        if (!table) {
+            // show add table drawer
+            show = this.addpanel;
+            hide = this.editpanel;
+        } else {
+            // show edit table drawer
+            show = this.editpanel;
+            hide = this.addpanel;
+            var align = this.tool._getColumnAlign(selNode);
+            var alignselect = document.getElementById('kupu-tabledrawer-alignchooser');
+            selectSelectItem(alignselect, align);
+            var classselect = document.getElementById('kupu-tabledrawer-classchooser');
+            selectSelectItem(classselect, table.className);
+        };
+        hide.style.display = 'none';
+        show.style.display = 'block';
+        this.element.style.display = 'block';
+        if (this.editor.getBrowserName() == 'IE') {
+            this.element.focus();
+        }
+    };
+
+    this.createTable = function() {
+        var rows = document.getElementById('kupu-tabledrawer-newrows').value;
+        var cols = document.getElementById('kupu-tabledrawer-newcols').value;
+        var style = document.getElementById('kupu-tabledrawer-classchooser').value;
+        var add_header = document.getElementById('kupu-tabledrawer-makeheader').checked;
+        this.tool.createTable(parseInt(rows), parseInt(cols), add_header, style);
+        this.drawertool.closeDrawer();
+    };
+};
+
+TableDrawer.prototype = new Drawer;
+
+function LibraryDrawer(tool, xsluri, libsuri, searchuri) {
+    /* a drawer that loads XSLT and XML from the server 
+       and converts the XML to XHTML for the drawer using the XSLT
+
+       there are 2 types of XML file loaded from the server: the first
+       contains a list of 'libraries', partitions for the data items, 
+       and the second a list of data items for a certain library
+
+       all XML loading is done async, since sync loading can freeze Mozilla
+    */
+
+    this.init = function(tool, xsluri, libsuri, searchuri) {
+        /* This method is there to thin out the constructor and to be
+           able to inherit it in sub-prototypes. Don't confuse this
+           method with the component initializer (initialize()).
+        */
+        // these are used in the XSLT. Maybe they should be
+        // parameterized or something, but we depend on so many other
+        // things implicitly anyway...
+        this.drawerid = 'kupu-librarydrawer';
+        this.librariespanelid = 'kupu-librariespanel';
+        this.resourcespanelid = 'kupu-resourcespanel';
+        this.propertiespanelid = 'kupu-propertiespanel';
+
+        this.tool = tool;
+        this.element = document.getElementById(this.drawerid);
+        this.xsluri = xsluri;
+        this.libsuri = libsuri;
+        this.searchuri = searchuri;
+        
+        // marker that gets set when a new image has been uploaded
+        this.newimages = null;
+
+        // the following vars will be available after this.initialize()
+        // has been called
+    
+        // this will be filled by this._libXslCallback()
+        this.xsl = null;
+        // this will be filled by this.loadLibraries(), which is called 
+        // somewhere further down the chain starting with 
+        // this._libsXslCallback()
+        this.xmldata = null;
+
+    };
+    this.init(tool, xsluri, libsuri, searchuri);
+
+    this.initialize = function(editor, drawertool) {
+        this.editor = editor;
+        this.drawertool = drawertool;
+
+        // load the xsl and the initial xml
+        var wrapped_callback = new ContextFixer(this._libsXslCallback, this);
+        this._loadXML(this.xsluri, wrapped_callback.execute);
+    };
+
+    /*** bootstrapping ***/
+
+    this._libsXslCallback = function(dom) {
+        /* callback for when the xsl for the libs is loaded
+        
+            this is called on init and since the initial libs need
+            to be loaded as well (and everything is async with callbacks
+            so there's no way to wait until the XSL is loaded) this
+            will also make the first loadLibraries call
+        */
+        this.xsl = dom;
+
+        // Change by Paul to have cached xslt transformers for reuse of 
+        // multiple transforms and also xslt params
+        try {
+            this.xsltproc = new XSLTProcessor();
+            this.xsltproc.importStylesheet(dom);
+            this.xsltproc.setParameter("", "drawertype", this.drawertype);
+            this.xsltproc.setParameter("", "drawertitle", this.drawertitle);
+            this.xsltproc.setParameter("", "showupload", this.showupload);
+            if (this.editor.config.captions) {
+                this.xsltproc.setParameter("", "usecaptions", 'yes');
+            }
+        } catch(e) {
+            return; // No XSLT Processor, maybe IE 5.5?
+        }
+    };
+
+    this.createContent = function() {
+        // load the initial XML
+        if(!this.xmldata) {
+            // Do a meaningful test to see if this is IE5.5 or some other 
+            // editor-enabled version whose XML support isn't good enough 
+            // for the drawers
+            if (!Sarissa.IS_ENABLED_XSLTPROC) {
+               alert("This function requires better XML support in your browser.");
+               return;
+            }
+            this.loadLibraries();
+        } else {
+            if (this.newimages) {
+                this.reloadCurrent();
+                this.newimages = null;
+            };
+            this.updateDisplay();
+        };
+
+        // display the drawer div
+        this.element.style.display = 'block';
+        if (this.editor.getBrowserName() == 'IE') {
+            this.element.focus();
+        }
+    };
+
+    this._singleLibsXslCallback = function(dom) {
+        /* callback for then the xsl for single libs (items) is loaded
+
+            nothing special needs to be called here, since initially the
+            items pane will be empty
+        */
+        this.singlelibxsl = dom;
+    };
+
+    this.loadLibraries = function() {
+        /* load the libraries and display them in a redrawn drawer */
+        var wrapped_callback = new ContextFixer(this._libsContentCallback, this);
+        this._loadXML(this.libsuri, wrapped_callback.execute);
+    };
+
+    this._libsContentCallback = function(dom) {
+        /* this is called when the libs xml is loaded
+
+            does the xslt transformation to set up or renew the drawer's full
+            content and adds the content to the drawer
+        */
+        this.xmldata = dom;
+        this.xmldata.setProperty("SelectionLanguage", "XPath");
+
+        // replace whatever is in there with our stuff
+        this.updateDisplay(this.drawerid);
+    };
+
+    this.updateDisplay = function(id) {
+      /* (re-)transform XML and (re-)display the necessary part
+       */
+        if(!id) {
+            id = this.drawerid;
+        };
+        try {
+            this.xsltproc.setParameter("", "showupload", this.showupload);
+        } catch(e) {};
+        var doc = this._transformXml();
+        var sourcenode = doc.selectSingleNode('//*[@id="'+id+'"]');
+        var targetnode = document.getElementById(id);
+        this._replaceNodeContents(document, targetnode, sourcenode);
+
+        if (this.editor.getBrowserName() == 'IE' && id == this.resourcespanelid) {
+            this.updateDisplay(this.drawerid);
+        };
+    };
+
+    this.deselectActiveCollection = function() {
+        /* Deselect the currently active collection or library */
+        while (1) {
+            // deselect selected DOM node
+            var selected = this.xmldata.selectSingleNode('//*[@selected]');
+            if (!selected) {
+                return;
+            };
+            selected.removeAttribute('selected');
+        };
+    };
+
+    /*** Load a library ***/
+
+    this.selectLibrary = function(id) {
+        /* unselect the currently selected lib and select a new one
+
+            the selected lib (libraries pane) will have a specific CSS class 
+            (selected)
+        */
+        // remove selection in the DOM
+        this.deselectActiveCollection();
+        // as well as visual selection in CSS
+        // XXX this is slow, but we can't do XPath, unfortunately
+        var divs = this.element.getElementsByTagName('div');
+        for (var i=0; i<divs.length; i++ ) {
+          if (divs[i].className == 'kupu-libsource-selected') {
+            divs[i].className = 'kupu-libsource';
+          };
+        };
+
+        var libnode_path = '/libraries/library[@id="' + id + '"]';
+        var libnode = this.xmldata.selectSingleNode(libnode_path);
+        libnode.setAttribute('selected', '1');
+
+        var items_xpath = "items";
+        var items_node = libnode.selectSingleNode(items_xpath);
+        
+        if (items_node && !this.newimages) {
+            // The library has already been loaded before or was
+            // already provided with an items list. No need to do
+            // anything except for displaying the contents in the
+            // middle pane. Newimages is set if we've lately
+            // added an image.
+            this.updateDisplay(this.resourcespanelid);
+            this.updateDisplay(this.propertiespanelid);
+        } else {
+            // We have to load the library from XML first.
+            var src_uri = libnode.selectSingleNode('src/text()').nodeValue;
+            src_uri = src_uri.strip(); // needs kupuhelpers.js
+            // Now load the library into the items pane. Since we have
+            // to load the XML, do this via a call back
+            var wrapped_callback = new ContextFixer(this._libraryContentCallback, this);
+            this._loadXML(src_uri, wrapped_callback.execute, null);
+            this.newimages = null;
+        };
+        // instead of running the full transformations again we get a 
+        // reference to the element and set the classname...
+        var newseldiv = document.getElementById(id);
+        newseldiv.className = 'kupu-libsource-selected';
+    };
+
+    this._libraryContentCallback = function(dom, src_uri) {
+        /* callback for when a library's contents (item list) is loaded
+
+        This is also used as he handler for reloading a standard
+        collection.
+        */
+        var libnode = this.xmldata.selectSingleNode('//*[@selected]');
+        var itemsnode = libnode.selectSingleNode("items");
+        var newitemsnode = dom.selectSingleNode("//items");
+
+        // IE does not support importNode on XML document nodes. As an
+        // evil hack, clonde the node instead.
+
+        if (this.editor.getBrowserName() == 'IE') {
+            newitemsnode = newitemsnode.cloneNode(true);
+        } else {
+            newitemsnode = this.xmldata.importNode(newitemsnode, true);
+        }
+        if (!itemsnode) {
+            // We're loading this for the first time
+            libnode.appendChild(newitemsnode);
+        } else {
+            // User has clicked reload
+            libnode.replaceChild(newitemsnode, itemsnode);
+        };
+        this.updateDisplay(this.resourcespanelid);
+        this.updateDisplay(this.propertiespanelid);
+    };
+
+    /*** Load a collection ***/
+
+    this.selectCollection = function(id) {
+        this.deselectActiveCollection();
+
+        // First turn off current selection, if any
+        this.removeSelection();
+        
+        var leafnode_path = "//collection[@id='" + id + "']";
+        var leafnode = this.xmldata.selectSingleNode(leafnode_path);
+
+        // Case 1: We've already loaded the data, so we just need to
+        // refer to the data by id.
+        var loadedInNode = leafnode.getAttribute('loadedInNode');
+        if (loadedInNode) {
+            var collnode_path = "/libraries/collection[@id='" + loadedInNode + "']";
+            var collnode = this.xmldata.selectSingleNode(collnode_path);
+            if (collnode) {
+                collnode.setAttribute('selected', '1');
+                this.updateDisplay(this.resourcespanelid);
+                this.updateDisplay(this.propertiespanelid);
+                return;
+            };
+        };
+
+        // Case 2: We've already loaded the data, but there hasn't
+        // been a reference made yet. So, make one :)
+        uri = leafnode.selectSingleNode('uri/text()').nodeValue;
+        uri = (new String(uri)).strip(); // needs kupuhelpers.js
+        var collnode_path = "/libraries/collection/uri[text()='" + uri + "']/..";
+        var collnode = this.xmldata.selectSingleNode(collnode_path);
+        if (collnode) {
+            id = collnode.getAttribute('id');
+            leafnode.setAttribute('loadedInNode', id);
+            collnode.setAttribute('selected', '1');
+            this.updateDisplay(this.resourcespanelid);
+            this.updateDisplay(this.propertiespanelid);
+            return;
+        };
+
+        // Case 3: We've not loaded the data yet, so we need to load it
+        // this is just so we can find the leafnode much easier in the
+        // callback.
+        leafnode.setAttribute('selected', '1');
+        var src_uri = leafnode.selectSingleNode('src/text()').nodeValue;
+        src_uri = src_uri.strip(); // needs kupuhelpers.js
+        var wrapped_callback = new ContextFixer(this._collectionContentCallback, this);
+        this._loadXML(src_uri, wrapped_callback.execute, null);
+    };
+
+    this._collectionContentCallback = function(dom, src_uri) {
+        // Unlike with libraries, we don't have to find a node to hook
+        // our results into (UNLESS we've hit the reload button, but
+        // that is handled in _libraryContentCallback anyway).
+        // We need to give the newly retrieved data a unique ID, we
+        // just use the time.
+        date = new Date();
+        time = date.getTime();
+
+        // attach 'loadedInNode' attribute to leaf node so Case 1
+        // applies next time.
+        var leafnode = this.xmldata.selectSingleNode('//*[@selected]');
+        leafnode.setAttribute('loadedInNode', time);
+        this.deselectActiveCollection()
+
+        var collnode = dom.selectSingleNode('/collection');
+        collnode.setAttribute('id', time);
+        collnode.setAttribute('selected', '1');
+
+        var libraries = this.xmldata.selectSingleNode('/libraries');
+
+        // IE does not support importNode on XML documet nodes
+        if (this.editor.getBrowserName() == 'IE') {
+            collnode = collnode.cloneNode(true);
+        } else {
+            collnode = this.xmldata.importNode(collnode, true);
+        }
+        libraries.appendChild(collnode);
+        this.updateDisplay(this.resourcespanelid);
+        this.updateDisplay(this.propertiespanelid);
+    };
+
+    /*** Reloading a collection or library ***/
+
+    this.reloadCurrent = function() {
+        // Reload current collection or library
+        this.showupload = '';
+        var current = this.xmldata.selectSingleNode('//*[@selected]');
+        // make sure we're dealing with a collection even though a
+        // resource might be selected
+        if (current.tagName == "resource") {
+            current.removeAttribute("selected");
+            current = current.parentNode;
+            current.setAttribute("selected", "1");
+        };
+        var src_node = current.selectSingleNode('src');
+        if (!src_node) {
+            // simply do nothing if the library cannot be reloaded. This
+            // is currently the case w/ search result libraries.
+            return;
+        };
+
+        var src_uri = src_node.selectSingleNode('text()').nodeValue;
+        
+        src_uri = src_uri.strip(); // needs kupuhelpers.js
+
+        var wrapped_callback = new ContextFixer(this._libraryContentCallback, this);
+        this._loadXML(src_uri, wrapped_callback.execute);
+    };
+
+    this.removeSelection = function() {
+        // turn off current selection, if any
+        var oldselxpath = '/libraries/*[@selected]//resource[@selected]';
+        var oldselitem = this.xmldata.selectSingleNode(oldselxpath);
+        if (oldselitem) {
+            oldselitem.removeAttribute("selected");
+        };
+        this.showupload = '';
+    }
+
+    this.selectUpload = function() {
+        this.removeSelection();
+        this.showupload = 'yes';
+        this.updateDisplay(this.resourcespanelid);
+        this.updateDisplay(this.propertiespanelid);
+    }
+    /*** Selecting a resource ***/
+
+    this.selectItem = function (id) {
+        /* select an item in the item pane, show the item's metadata */
+
+        // First turn off current selection, if any
+        this.removeSelection();
+        
+        // Grab XML DOM node for clicked "resource" and mark it selected
+        var newselxpath = '/libraries/*[@selected]//resource[@id="' + id + '"]';
+        var newselitem = this.xmldata.selectSingleNode(newselxpath);
+        newselitem.setAttribute("selected", "1");
+
+        this.updateDisplay(this.resourcespanelid);
+        this.updateDisplay(this.propertiespanelid);
+        return;
+    }
+
+
+    this.search = function() {
+        /* search */
+        var searchvalue = document.getElementById('kupu-searchbox-input').value;
+        //XXX make search variable configurable
+        var body = 'SearchableText=' + escape(searchvalue);
+
+        // the search uri might contain query parameters in HTTP GET
+        // style. We want to do a POST though, so find any possible
+        // parameters, trim them from the URI and append them to the
+        // POST body instead.
+        var chunks = this.searchuri.split('?');
+        var searchuri = chunks[0];
+        if (chunks[1]) {
+            body += "&" + chunks[1];
+        };
+        var wrapped_callback = new ContextFixer(this._searchCallback, this);
+        this._loadXML(searchuri, wrapped_callback.execute, body);
+    };
+
+    this._searchCallback = function(dom) {
+        var resultlib = dom.selectSingleNode("/library");
+
+        var items = resultlib.selectNodes("items/*");
+        if (!items.length) {
+            alert("No results found.");
+            return;
+        };
+
+        // we need to give the newly retrieved data a unique ID, we
+        // just use the time.
+        date = new Date();
+        time = date.getTime();
+        resultlib.setAttribute("id", time);
+
+        // deselect the previous collection and mark the result
+        // library as selected
+        this.deselectActiveCollection();
+        resultlib.setAttribute("selected", "1");
+
+        // now hook the result library into our DOM
+        if (this.editor.getBrowserName() == 'IE') {
+            resultlib = resultlib.cloneNode(true);
+        } else {
+            this.xmldata.importNode(resultlib, true);
+        }
+        var libraries = this.xmldata.selectSingleNode("/libraries");
+        libraries.appendChild(resultlib);
+
+        this.updateDisplay(this.drawerid);
+        var newseldiv = document.getElementById(time);
+        newseldiv.className = 'selected';
+    };
+
+    this.save = function() {
+        /* save the element, should be implemented on subclasses */
+        throw "Not yet implemented";
+    };
+
+    /*** Auxiliary methods ***/
+
+    this._transformXml = function() {
+        /* transform this.xmldata to HTML using this.xsl and return it */
+        var doc = Sarissa.getDomDocument();
+
+	//var xsltproc = new XSLTProcessor();
+	var result = this.xsltproc.transformToDocument(this.xmldata);
+
+	// this.xmldata.transformNodeToObject(this.xsl, doc);
+        return result;
+    };
+
+    this._loadXML = function(uri, callback, body) {
+        /* load the XML from a uri
+        
+            calls callback with one arg (the XML DOM) when done
+            the (optional) body arg should contain the body for the request
+*/
+        var xmlhttp = Sarissa.getXmlHttpRequest();
+        var method = 'GET';
+        if (body) {
+          method = 'POST';
+        } else {
+          // be sure that body is null and not an empty string or
+          // something
+          body = null;
+        };
+        xmlhttp.open(method, uri, true);
+        // use ContextFixer to wrap the Sarissa callback, both for isolating 
+        // the 'this' problem and to be able to pass in an extra argument 
+        // (callback)
+        var wrapped_callback = new ContextFixer(this._sarissaCallback, xmlhttp,
+                                                callback, uri);
+        xmlhttp.onreadystatechange = wrapped_callback.execute;
+        if (method == "POST") {
+            // by default, we would send a 'text/xml' request, which
+            // is a dirty lie; explicitly set the content type to what
+            // a web server expects from a POST.
+            xmlhttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
+        };
+        xmlhttp.send(body);
+    };
+
+    this._replaceNodeContents = function(doc, target, container) {
+        /* replace all childnodes in target with all childnodes in container */
+        var importedContainer = doc.importNode(container, true);
+        // XXX it seems that IE doesn't allow hacks like these
+        // no need to worry anyway, since the transformed HTML seems
+        // to have the right JS context variables anyway.
+
+        if (this.editor.getBrowserName() != 'IE') {
+            container.ownerDocument.contentWindow = doc.contentWindow;
+        };
+        while (target.hasChildNodes()) {
+            target.removeChild(target.firstChild);
+        };
+        // XXX don't know if this works since i'm not sure whether 
+        // appendChild actually removes a child from a previous
+        // location (although i think it does)
+        while (importedContainer.hasChildNodes()) {
+            target.appendChild(importedContainer.firstChild);
+        };
+    };
+
+    this._sarissaCallback = function(user_callback, uri) {
+        /* callback for Sarissa
+            when the callback is called because the data's ready it
+            will get the responseXML DOM and call user_callback
+            with the DOM as the first argument and the uri loaded
+            as the second
+            
+            note that this method should be called in the context of an 
+            xmlhttp object
+        */
+        var errmessage = 'Error loading XML: ';
+        if (uri) {
+            errmessage = 'Error loading ' + uri + ':';
+        };
+        if (this.readyState == 4) {
+            if (this.status && this.status != 200) {
+                alert(errmessage + this.status);
+                throw "Error loading XML";
+            };
+            var dom = this.responseXML;
+            user_callback(dom, uri);
+        };
+    };
+};
+
+LibraryDrawer.prototype = new Drawer;
+
+function ImageLibraryDrawer(tool, xsluri, libsuri, searchuri) {
+    /* a specific LibraryDrawer for images */
+
+    this.drawertitle = "Image Library";
+    this.drawertype = "image";
+    this.showupload = '';
+    this.init(tool, xsluri, libsuri, searchuri);    
+ 
+    
+    // upload, on submit/insert press
+    this.uploadImage = function() {
+        var form = document.kupu_upload_form;
+        if (!form || form.node_prop_image.value=='') return;
+
+        if (form.node_prop_caption.value == "") {
+            alert("Please enter a title for the image you are uploading");
+            return;        
+        };
+        
+        var targeturi =  this.xmldata.selectSingleNode('/libraries/*[@selected]/uri/text()').nodeValue
+        document.kupu_upload_form.action =  targeturi + "/kupuUploadImage";
+        document.kupu_upload_form.submit();
+    };
+    
+    // called for example when no permission to upload for some reason
+    this.cancelUpload = function(msg) {
+        var s = this.xmldata.selectSingleNode('/libraries/*[@selected]');     
+        s.removeAttribute("selected");
+        this.updateDisplay();
+        if (msg != '') {
+            alert(msg);
+        };
+    };
+    
+    // called by onLoad within document sent by server
+    this.finishUpload = function(url) {
+        var img = this.tool.createImage(url);
+        if (this.editor.config.captions) {
+            img.className = img.className + " captioned";
+        }
+        this.newimages = 1;
+        this.drawertool.closeDrawer();
+    };
+    
+
+    this.save = function() {
+        /* create an image in the iframe according to collected data
+           from the drawer */
+        var selxpath = '//resource[@selected]';
+        var selnode = this.xmldata.selectSingleNode(selxpath);
+        
+        // If no image resource is selected, check for upload
+        if (!selnode) {
+            var uploadbutton = this.xmldata.selectSingleNode("/libraries/*[@selected]//uploadbutton");
+            if (uploadbutton) {
+                this.uploadImage();
+            };
+            return;
+        };
+
+        var uri = selnode.selectSingleNode('uri/text()').nodeValue;
+        uri = uri.strip();  // needs kupuhelpers.js
+        var img = this.tool.createImage(uri);
+        var alt = document.getElementById('image_alt').value;
+        img.setAttribute('alt', alt);
+
+        // Set image class from the alignment radio buttons
+        var radios = document.getElementsByName('image-align');
+        for (var i = 0; i < radios.length; i++) {
+            if (radios[i].checked) {
+                img.className = radios[i].value;
+            }
+        }
+
+        var caption = document.getElementsByName('image-caption');
+        if (caption && caption.length>0 && caption[0].checked) {
+            img.className = img.className + " captioned";
+        }
+
+        this.drawertool.closeDrawer();
+    };
+};
+
+ImageLibraryDrawer.prototype = new LibraryDrawer;
+
+function LinkLibraryDrawer(tool, xsluri, libsuri, searchuri) {
+    /* a specific LibraryDrawer for links */
+
+    this.drawertitle = "Link Drawer";
+    this.drawertype = "link";
+    this.showupload = '';
+    this.init(tool, xsluri, libsuri, searchuri);
+
+    this.save = function() {
+        /* create a link in the iframe according to collected data
+           from the drawer */
+        var selxpath = '//resource[@selected]';
+        var selnode = this.xmldata.selectSingleNode(selxpath);
+        if (!selnode) {
+            return;
+        };
+
+        var uri = selnode.selectSingleNode('uri/text()').nodeValue;
+        uri = uri.strip();  // needs kupuhelpers.js
+        var title = '';
+        title = selnode.selectSingleNode('title/text()').nodeValue;
+        title = title.strip();
+
+        // XXX requiring the user to know what link type to enter is a
+        // little too much I think. (philiKON)
+        var type = null;
+        var name = document.getElementById('link_name').value;
+        var target = null;
+        if (document.getElementById('link_target') && document.getElementById('link_target').value != '')
+            target = document.getElementById('link_target').value;
+        
+        this.tool.createLink(uri, type, name, target, title);
+    };
+};
+
+LinkLibraryDrawer.prototype = new LibraryDrawer;

Added: incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers/allimages.xml
URL: http://svn.apache.org/viewcvs/incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers/allimages.xml?view=auto&rev=154214
==============================================================================
--- incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers/allimages.xml (added)
+++ incubator/graffito/trunk/applications/browser/src/webapp/kupu/kupudrawers/allimages.xml Thu Feb 17 15:57:09 2005
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?>
+<collection>
+    <uri>kupudrawers/allimages.xml</uri>
+    <icon>kupuimages/kupulibrary.png</icon>
+    <title>All images</title>
+    <src>kupudrawers/allimages.xml</src>
+    <items>
+        <collection id="logos">
+            <uri>kupudrawers/logos.xml</uri>
+            <title>Logo's</title>
+            <src>kupudrawers/logos.xml</src>
+            <icon>kupuimages/kupulibrary.png</icon>
+        </collection>
+        <collection id="kupubuttons">
+            <uri>kupudrawers/kupubuttons.xml</uri>
+            <title>Kupu buttons</title>
+            <src>kupudrawers/kupubuttons.xml</src>
+            <icon>kupuimages/kupulibrary.png</icon>
+        </collection>
+    </items>
+</collection>