You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ed...@apache.org on 2006/11/11 17:44:48 UTC

svn commit: r473755 [28/43] - in /jackrabbit/trunk/contrib/jcr-browser: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/jackrabbit/ src/main/java/org/apache/jackrabbit/browser/ src/main/resources/ ...

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DomWidget.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DomWidget.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DomWidget.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DomWidget.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,892 @@
+dojo.provide("dojo.widget.DomWidget");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.Widget");
+dojo.require("dojo.dom");
+dojo.require("dojo.html.style");
+dojo.require("dojo.xml.Parse");
+dojo.require("dojo.uri.*");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.extras");
+
+dojo.widget._cssFiles = {};
+dojo.widget._cssStrings = {};
+dojo.widget._templateCache = {};
+
+// Object: a mapping of strings that are used in template variable replacement
+dojo.widget.defaultStrings = {
+	dojoRoot: dojo.hostenv.getBaseScriptUri(),
+	baseScriptUri: dojo.hostenv.getBaseScriptUri()
+};
+
+dojo.widget.fillFromTemplateCache = function(	/*DomWidget*/				obj, 
+												/*String||dojo.uri.Uri*/	templatePath,
+												/*String, optional*/		templateString,
+												/*Boolean, optional*/		avoidCache){
+	// summary:
+	//		static method to build from a template w/ or w/o a real widget in
+	//		place
+	// obj: an instance of dojo.widget.DomWidget to initialize the template for
+	// templatePath: the URL to get the template from	
+	// templateString:
+	//		a string to use in lieu of fetching the template from a URL
+	// avoidCache:
+	//		should the template system not use whatever is in the cache and
+	//		always use the passed templatePath or templateString?
+
+	// dojo.debug("avoidCache:", avoidCache);
+	var tpath = templatePath || obj.templatePath;
+
+	var tmplts = dojo.widget._templateCache;
+	if(!obj["widgetType"]) { // don't have a real template here
+		do {
+			var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
+		} while(tmplts[dummyName]);
+		obj.widgetType = dummyName;
+	}
+	var wt = obj.widgetType;
+
+	var ts = tmplts[wt];
+	if(!ts){
+		tmplts[wt] = { "string": null, "node": null };
+		if(avoidCache){
+			ts = {};
+		}else{
+			ts = tmplts[wt];
+		}
+	}
+	if((!obj.templateString)&&(!avoidCache)){
+		obj.templateString = templateString || ts["string"];
+	}
+	if((!obj.templateNode)&&(!avoidCache)){
+		obj.templateNode = ts["node"];
+	}
+	if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){
+		// fetch a text fragment and assign it to templateString
+		// NOTE: we rely on blocking IO here!
+		var tstring = dojo.hostenv.getText(tpath);
+		if(tstring){
+			// strip <?xml ...?> declarations so that external SVG and XML
+			// documents can be added to a document without worry
+			tstring = tstring.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
+			var matches = tstring.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+			if(matches){
+				tstring = matches[1];
+			}
+		}else{
+			tstring = "";
+		}
+		obj.templateString = tstring;
+		if(!avoidCache){
+			tmplts[wt]["string"] = tstring;
+		}
+	}
+	if((!ts["string"])&&(!avoidCache)){
+		ts.string = obj.templateString;
+	}
+}
+dojo.widget._templateCache.dummyCount = 0;
+
+// Array: list of properties to search for node-to-property mappings
+dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
+// String: name of the property to use for mapping DOM events to widget functions
+dojo.widget.eventAttachProperty = "dojoAttachEvent";
+// String: property name of code to evaluate when the widget is constructed
+dojo.widget.onBuildProperty = "dojoOnBuild";
+// Array:  possible accessibility values to set on widget elements - role or state
+dojo.widget.waiNames  = ["waiRole", "waiState"];
+// Object: Contains functions to set accessibility roles and states 
+// 	onto widget elements
+dojo.widget.wai = {
+	waiRole: { 	
+				// String: information for mapping accessibility role
+				name: "waiRole", 
+				// String: URI of the namespace for the set of roles
+				"namespace": "http://www.w3.org/TR/xhtml2", 
+				// String: alias to assign the namespace
+				alias: "x2",
+				// String: prefix to assign to the role value
+				prefix: "wairole:"
+	},
+	waiState: { 
+				// String: informatin for mapping accessibility state
+				name: "waiState", 
+				// String: URI of the namespace for the set of states
+				"namespace": "http://www.w3.org/2005/07/aaa", 
+				// String: alias to assign the namespace
+				alias: "aaa",
+				// String: empty string - state value does not require prefix
+				prefix: ""
+	},
+	setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
+		// Summary: Use appropriate API to set the role or state attribute onto the element.
+		// Description: In IE use the generic setAttribute() api.  Append a namespace
+		//   alias to the attribute name and appropriate prefix to the value. 
+		//   Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
+		//   add the appropriate prefix to the attribute value.
+		if(dojo.render.html.ie){
+			node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
+		}else{
+			node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
+		}
+	},
+
+	getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+		// Summary:  Use the appropriate API to retrieve the role or state value
+		// Description: In IE use the generic getAttribute() api.  An alias value 
+		// 	was added to the attribute name to simulate a namespace when the attribute
+		//  was set.  Otherwise use the getAttributeNS() api to retrieve the state value
+		if(dojo.render.html.ie){
+			return node.getAttribute(this[ns].alias+":"+attr);
+		}else{
+			return node.getAttributeNS(this[ns]["namespace"], attr);
+		}
+	},
+	removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+		// Summary:  Use the appropriate API to remove the role or state value
+		// Description: In IE use the generic removeAttribute() api.  An alias value 
+		// 	was added to the attribute name to simulate a namespace when the attribute
+		//  was set.  Otherwise use the removeAttributeNS() api to remove the state value
+		var success = true; //only IE returns a value
+		if(dojo.render.html.ie){
+			 success = node.removeAttribute(this[ns].alias+":"+attr);
+		}else{
+			node.removeAttributeNS(this[ns]["namespace"], attr);
+		}
+		return success;
+	}
+};
+
+dojo.widget.attachTemplateNodes = function(	/*DomNode*/		rootNode, 
+											/*Widget*/		targetObj, 
+											/*Array*/		events ){
+	// summary:
+	//		map widget properties and functions to the handlers specified in
+	//		the dom node and it's descendants. This function iterates over all
+	//		nodes and looks for these properties:
+	//			* dojoAttachPoint
+	//			* dojoAttachEvent	
+	//			* waiRole
+	//			* waiState
+	//			* any "dojoOn*" proprties passed in the events array
+	// rootNode:
+	//		the node to search for properties. All children will be searched.
+	// events: a list of properties generated from getDojoEventsFromStr.
+
+	// FIXME: this method is still taking WAAAY too long. We need ways of optimizing:
+	//	a.) what we are looking for on each node
+	//	b.) the nodes that are subject to interrogation (use xpath instead?)
+	//	c.) how expensive event assignment is (less eval(), more connect())
+	// var start = new Date();
+	var elementNodeType = dojo.dom.ELEMENT_NODE;
+
+	function trim(str){
+		return str.replace(/^\s+|\s+$/g, "");
+	}
+
+	if(!rootNode){ 
+		rootNode = targetObj.domNode;
+	}
+
+	if(rootNode.nodeType != elementNodeType){
+		return;
+	}
+	// alert(events.length);
+
+	var nodes = rootNode.all || rootNode.getElementsByTagName("*");
+	var _this = targetObj;
+	for(var x=-1; x<nodes.length; x++){
+		var baseNode = (x == -1) ? rootNode : nodes[x];
+		// FIXME: is this going to have capitalization problems?  Could use getAttribute(name, 0); to get attributes case-insensitve
+		var attachPoint = [];
+		if(!targetObj.widgetsInTemplate || !baseNode.getAttribute('dojoType')){
+			for(var y=0; y<this.attachProperties.length; y++){
+				var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
+				if(tmpAttachPoint){
+					attachPoint = tmpAttachPoint.split(";");
+					for(var z=0; z<attachPoint.length; z++){
+						if(dojo.lang.isArray(targetObj[attachPoint[z]])){
+							targetObj[attachPoint[z]].push(baseNode);
+						}else{
+							targetObj[attachPoint[z]]=baseNode;
+						}
+					}
+					break;
+				}
+			}
+
+			var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
+			if(attachEvent){
+				// NOTE: we want to support attributes that have the form
+				// "domEvent: nativeEvent; ..."
+				var evts = attachEvent.split(";");
+				for(var y=0; y<evts.length; y++){
+					if((!evts[y])||(!evts[y].length)){ continue; }
+					var thisFunc = null;
+					var tevt = trim(evts[y]);
+					if(evts[y].indexOf(":") >= 0){
+						// oh, if only JS had tuple assignment
+						var funcNameArr = tevt.split(":");
+						tevt = trim(funcNameArr[0]);
+						thisFunc = trim(funcNameArr[1]);
+					}
+					if(!thisFunc){
+						thisFunc = tevt;
+					}
+	
+					var tf = function(){ 
+						var ntf = new String(thisFunc);
+						return function(evt){
+							if(_this[ntf]){
+								_this[ntf](dojo.event.browser.fixEvent(evt, this));
+							}
+						};
+					}();
+					dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
+					// dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc));
+				}
+			}
+	
+			for(var y=0; y<events.length; y++){
+				//alert(events[x]);
+				var evtVal = baseNode.getAttribute(events[y]);
+				if((evtVal)&&(evtVal.length)){
+					var thisFunc = null;
+					var domEvt = events[y].substr(4); // clober the "dojo" prefix
+					thisFunc = trim(evtVal);
+					var funcs = [thisFunc];
+					if(thisFunc.indexOf(";")>=0){
+						funcs = dojo.lang.map(thisFunc.split(";"), trim);
+					}
+					for(var z=0; z<funcs.length; z++){
+						if(!funcs[z].length){ continue; }
+						var tf = function(){ 
+							var ntf = new String(funcs[z]);
+							return function(evt){
+								if(_this[ntf]){
+									_this[ntf](dojo.event.browser.fixEvent(evt, this));
+								}
+							}
+						}();
+						dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
+						// dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z]));
+					}
+				}
+			}
+		}
+		// continue;
+
+		// FIXME: we need to put this into some kind of lookup structure
+		// instead of direct assignment
+		var tmpltPoint = baseNode.getAttribute(this.templateProperty);
+		if(tmpltPoint){
+			targetObj[tmpltPoint]=baseNode;
+		}
+
+		dojo.lang.forEach(dojo.widget.waiNames, function(name){
+			var wai = dojo.widget.wai[name];
+			var val = baseNode.getAttribute(wai.name);
+			if(val){
+				if(val.indexOf('-') == -1){ 
+					dojo.widget.wai.setAttr(baseNode, wai.name, "role", val);
+				}else{
+					// this is a state-value pair
+					var statePair = val.split('-');
+					dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]);
+				}
+			}
+		}, this);
+
+		var onBuild = baseNode.getAttribute(this.onBuildProperty);
+		if(onBuild){
+			eval("var node = baseNode; var widget = targetObj; "+onBuild);
+		}
+	}
+
+}
+
+dojo.widget.getDojoEventsFromStr = function(/*String*/str){
+	// summary:
+	//		generates a list of properties with names that match the form
+	//		dojoOn*
+	// str: the template string to search
+	
+	// var lstr = str.toLowerCase();
+	var re = /(dojoOn([a-z]+)(\s?))=/gi;
+	var evts = str ? str.match(re)||[] : [];
+	var ret = [];
+	var lem = {};
+	for(var x=0; x<evts.length; x++){
+		if(evts[x].length < 1){ continue; }
+		var cm = evts[x].replace(/\s/, "");
+		cm = (cm.slice(0, cm.length-1));
+		if(!lem[cm]){
+			lem[cm] = true;
+			ret.push(cm);
+		}
+	}
+	return ret; // Array
+}
+
+/*
+dojo.widget.buildAndAttachTemplate = function(obj, templatePath, templateCssPath, templateString, targetObj) {
+	this.buildFromTemplate(obj, templatePath, templateCssPath, templateString);
+	var node = dojo.dom.createNodesFromText(obj.templateString, true)[0];
+	this.attachTemplateNodes(node, targetObj||obj, dojo.widget.getDojoEventsFromStr(templateString));
+	return node;
+}
+*/
+
+
+// summary:
+//		dojo.widget.DomWidget is the superclass that provides behavior for all
+//		DOM-based renderers, including HtmlWidget and SvgWidget. DomWidget
+//		implements the templating system that most widget authors use to define
+//		the UI for their widgets.
+dojo.declare("dojo.widget.DomWidget", 
+	dojo.widget.Widget,
+	function(){
+		if((arguments.length>0)&&(typeof arguments[0] == "object")){
+			this.create(arguments[0]);
+		}
+	},
+	{							 
+		// DomNode: a node that represents the widget template. Pre-empts both templateString and templatePath.
+		templateNode: null,
+
+		// String:
+		//		a string that represents the widget template. Pre-empts the
+		//		templatePath. In builds that have their strings "interned", the
+		//		templatePath is converted to an inline templateString, thereby
+		//		preventing a synchronous network call.
+		templateString: null,
+
+		// String:
+		//		a string that represents the CSS for the widgettemplate.
+		//		Pre-empts the templateCssPath. In builds that have their
+		//		strings "interned", the templateCssPath is converted to an
+		//		inline templateCssString, thereby preventing a synchronous
+		//		network call.
+		templateCssString: null,
+
+		// Boolean:
+		//		should the widget not replace the node from which it was
+		//		constructed? Widgets that apply behaviors to pre-existing parts
+		//		of a page can be implemented easily by setting this to "true".
+		//		In these cases, the domNode property will point to the node
+		//		which the widget was created from.
+		preventClobber: false,
+
+		// DomNode:
+		//		this is our visible representation of the widget! Other DOM
+		//		Nodes may by assigned to other properties, usually through the
+		//		template system's dojoAttachPonit syntax, but the domNode
+		//		property is the canonical "top level" node in widget UI.
+		domNode: null, 
+
+		// DomNode:
+		//		holds child elements. "containerNode" is generally set via a
+		//		dojoAttachPoint assignment and it designates where widgets that
+		//		are defined as "children" of the parent will be placed
+		//		visually.
+		containerNode: null,
+
+		// Boolean:
+		//		should we parse the template to find widgets that might be
+		//		declared in markup inside it? false by default.
+		widgetsInTemplate: false,
+
+		addChild: function(	/*Widget*/				widget, 
+							/*DomNode, optional*/	overrideContainerNode, 
+							/*String, optional*/	pos, 
+							/*DomNode, optional*/	ref,
+							/*int, optional*/		insertIndex){
+			// summary:
+			//		Process the given child widget, inserting it's dom node as
+			//		a child of our dom node
+			// overrideContainerNode: a non-default container node for the widget
+			// pos:
+			//		can be one of "before", "after", "first", or "last". This
+			//		has the same meaning as in dojo.dom.insertAtPosition()
+			// ref: a node to place the widget relative to
+			// insertIndex: DOM index, same meaning as in dojo.dom.insertAtIndex()
+
+			// FIXME: should we support addition at an index in the children arr and
+			// order the display accordingly? Right now we always append.
+			if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
+				dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
+				return null;
+			}else{
+				if(insertIndex == undefined){
+					insertIndex = this.children.length;
+				}
+				this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
+				this.registerChild(widget, insertIndex);
+			}
+			return widget; // Widget: the widget that was inserted
+		},
+		
+		addWidgetAsDirectChild: function(	/*Widget*/				widget, 
+											/*DomNode*/				overrideContainerNode, 
+											/*String, optional*/	pos, 
+											/*DomNode, optional*/	ref, 
+											/*int, optional*/		insertIndex){
+			// summary:
+			//		Process the given child widget, inserting it's dom node as
+			//		a child of our dom node
+			// overrideContainerNode: a non-default container node for the widget
+			// pos:
+			//		can be one of "before", "after", "first", or "last". This
+			//		has the same meaning as in dojo.dom.insertAtPosition()
+			// ref: a node to place the widget relative to
+			// insertIndex: DOM index, same meaning as in dojo.dom.insertAtIndex()
+			if((!this.containerNode)&&(!overrideContainerNode)){
+				this.containerNode = this.domNode;
+			}
+			var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
+			if(!pos){ pos = "after"; }
+			if(!ref){ 
+				if(!cn){ cn = dojo.body(); }
+				ref = cn.lastChild; 
+			}
+			if(!insertIndex) { insertIndex = 0; }
+			widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
+
+			// insert the child widget domNode directly underneath my domNode, in the
+			// specified position (by default, append to end)
+			if(!ref){
+				cn.appendChild(widget.domNode);
+			}else{
+				// FIXME: was this meant to be the (ugly hack) way to support insert @ index?
+				//dojo.dom[pos](widget.domNode, ref, insertIndex);
+
+				// CAL: this appears to be the intended way to insert a node at a given position...
+				if (pos == 'insertAtIndex'){
+					// dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
+					dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
+				}else{
+					// dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
+					if((pos == "after")&&(ref === cn.lastChild)){
+						cn.appendChild(widget.domNode);
+					}else{
+						dojo.dom.insertAtPosition(widget.domNode, cn, pos);
+					}
+				}
+			}
+		},
+
+		registerChild: function(/*Widget*/widget, /*int*/insertionIndex){
+			// summary: record that given widget descends from me
+			// widget: the widget that is now a child
+			// inesrtionIndex: where in the children[] array to place it
+
+			// we need to insert the child at the right point in the parent's 
+			// 'children' array, based on the insertionIndex
+
+			widget.dojoInsertionIndex = insertionIndex;
+
+			var idx = -1;
+			for(var i=0; i<this.children.length; i++){
+
+				//This appears to fix an out of order issue in the case of mixed
+				//markup and programmatically added children.  Previously, if a child
+				//existed from markup, and another child was addChild()d without specifying
+				//any additional parameters, it would end up first in the list, when in fact
+				//it should be after.  I can't see cases where this would break things, but
+				//I could see no other obvious solution. -dustin
+
+				if (this.children[i].dojoInsertionIndex <= insertionIndex){
+					idx = i;
+				}
+			}
+
+			this.children.splice(idx+1, 0, widget);
+
+			widget.parent = this;
+			widget.addedTo(this, idx+1);
+			
+			// If this widget was created programatically, then it was erroneously added
+			// to dojo.widget.manager.topWidgets.  Fix that here.
+			delete dojo.widget.manager.topWidgets[widget.widgetId];
+		},
+
+		removeChild: function(/*Widget*/widget){
+			// summary: detach child domNode from parent domNode
+			dojo.dom.removeNode(widget.domNode);
+
+			// remove child widget from parent widget 
+			return dojo.widget.DomWidget.superclass.removeChild.call(this, widget); // Widget
+		},
+
+		getFragNodeRef: function(/*Object*/frag){
+			// summary:
+			//		returns the source node, if any, that the widget was
+			//		declared from
+			// frag:
+			//		an opaque data structure generated by the first-pass parser
+			if(!frag){return null;} // null
+			if(!frag[this.getNamespacedType()]){
+				dojo.raise("Error: no frag for widget type " + this.getNamespacedType() 
+					+ ", id " + this.widgetId
+					+ " (maybe a widget has set it's type incorrectly)");
+			}
+			return frag[this.getNamespacedType()]["nodeRef"]; // DomNode
+		},
+		
+		postInitialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parentComp){
+			// summary:
+			//		Replace the source domNode with the generated dom
+			//		structure, and register the widget with its parent.
+			//		This is an implementation of the stub function defined in
+			//		dojo.widget.Widget.
+			
+			//dojo.profile.start(this.widgetType + " postInitialize");
+			
+			var sourceNodeRef = this.getFragNodeRef(frag);
+			// Stick my generated dom into the output tree
+			//alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
+			if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
+				// Add my generated dom as a direct child of my parent widget
+				// This is important for generated widgets, and also cases where I am generating an
+				// <li> node that can't be inserted back into the original DOM tree
+				parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "",  args["dojoinsertionindex"], sourceNodeRef);
+			} else if (sourceNodeRef){
+				// Do in-place replacement of the my source node with my generated dom
+				if(this.domNode && (this.domNode !== sourceNodeRef)){
+					var oldNode = sourceNodeRef.parentNode.replaceChild(this.domNode, sourceNodeRef);
+				}
+			}
+
+			// Register myself with my parent, or with the widget manager if
+			// I have no parent
+			// TODO: the code below erroneously adds all programatically generated widgets
+			// to topWidgets (since we don't know who the parent is until after creation finishes)
+			if ( parentComp ) {
+				parentComp.registerChild(this, args.dojoinsertionindex);
+			} else {
+				dojo.widget.manager.topWidgets[this.widgetId]=this;
+			}
+
+			if(this.widgetsInTemplate){
+				var parser = new dojo.xml.Parse();
+
+				var subContainerNode;
+				//TODO: use xpath here?
+				var subnodes = this.domNode.getElementsByTagName("*");
+				for(var i=0;i<subnodes.length;i++){
+					if(subnodes[i].getAttribute('dojoAttachPoint') == 'subContainerWidget'){
+						subContainerNode = subnodes[i];
+//						break;
+					}
+					if(subnodes[i].getAttribute('dojoType')){
+						subnodes[i].setAttribute('_isSubWidget', true);
+					}
+				}
+				if (this.isContainer && !this.containerNode){
+					//no containerNode is available, which means a widget is used as a container. find it here and move
+					//all dom nodes defined in the main html page as children of this.domNode into the actual container
+					//widget's node (at this point, the subwidgets defined in the template file is not parsed yet)
+					if(subContainerNode){
+						var src = this.getFragNodeRef(frag);
+						if (src){
+							dojo.dom.moveChildren(src, subContainerNode);
+							//do not need to follow children nodes in the main html page, as they
+							//will be dealt with in the subContainerWidget
+							frag['dojoDontFollow'] = true;
+						}
+					}else{
+						dojo.debug("No subContainerWidget node can be found in template file for widget "+this);
+					}
+				}
+
+				var templatefrag = parser.parseElement(this.domNode, null, true);
+				// createSubComponents not createComponents because frag has already been created
+				dojo.widget.getParser().createSubComponents(templatefrag, this);
+	
+				//find all the sub widgets defined in the template file of this widget
+				var subwidgets = [];
+				var stack = [this];
+				var w;
+				while((w = stack.pop())){
+					for(var i = 0; i < w.children.length; i++){
+						var cwidget = w.children[i];
+						if(cwidget._processedSubWidgets || !cwidget.extraArgs['_issubwidget']){ continue; }
+						subwidgets.push(cwidget);
+						if(cwidget.isContainer){
+							stack.push(cwidget);
+						}
+					}
+				}
+	
+				//connect event to this widget/attach dom node
+				for(var i = 0; i < subwidgets.length; i++){
+					var widget = subwidgets[i];
+					if(widget._processedSubWidgets){
+						dojo.debug("This should not happen: widget._processedSubWidgets is already true!");
+						return;
+					}
+					widget._processedSubWidgets = true;
+					if(widget.extraArgs['dojoattachevent']){
+						var evts = widget.extraArgs['dojoattachevent'].split(";");
+						for(var j=0; j<evts.length; j++){
+							var thisFunc = null;
+							var tevt = dojo.string.trim(evts[j]);
+							if(tevt.indexOf(":") >= 0){
+								// oh, if only JS had tuple assignment
+								var funcNameArr = tevt.split(":");
+								tevt = dojo.string.trim(funcNameArr[0]);
+								thisFunc = dojo.string.trim(funcNameArr[1]);
+							}
+							if(!thisFunc){
+								thisFunc = tevt;
+							}
+							if(dojo.lang.isFunction(widget[tevt])){
+								dojo.event.kwConnect({
+									srcObj: widget, 
+									srcFunc: tevt, 
+									targetObj: this, 
+									targetFunc: thisFunc
+								});
+							}else{
+								alert(tevt+" is not a function in widget "+widget);
+							}
+						}
+					}
+	
+					if(widget.extraArgs['dojoattachpoint']){
+						//don't attach widget.domNode here, as we do not know which
+						//dom node we should connect to (in checkbox widget case, 
+						//it is inputNode). So we make the widget itself available
+						this[widget.extraArgs['dojoattachpoint']] = widget;
+					}
+				}
+			}
+
+			//dojo.profile.end(this.widgetType + " postInitialize");
+
+			// Expand my children widgets
+			/* dojoDontFollow is important for a very special case
+			 * basically if you have a widget that you instantiate from script
+			 * and that widget is a container, and it contains a reference to a parent
+			 * instance, the parser will start recursively parsing until the browser
+			 * complains.  So the solution is to set an initialization property of 
+			 * dojoDontFollow: true and then it won't recurse where it shouldn't
+			 */
+			if(this.isContainer && !frag["dojoDontFollow"]){
+				//alert("recurse from " + this.widgetId);
+				// build any sub-components with us as the parent
+				dojo.widget.getParser().createSubComponents(frag, this);
+			}
+		},
+
+		// method over-ride
+		buildRendering: function(/*Object*/args, /*Object*/frag){
+			// summary:
+			//		Construct the UI for this widget, generally from a
+			//		template. This can be over-ridden for custom UI creation to
+			//		to side-step the template system.  This is an
+			//		implementation of the stub function defined in
+			//		dojo.widget.Widget.
+
+			// DOM widgets construct themselves from a template
+			var ts = dojo.widget._templateCache[this.widgetType];
+			
+			// Handle style for this widget here, as even if templatePath
+			// is not set, style specified by templateCssString or templateCssPath
+			// should be applied. templateCssString has higher priority
+			// than templateCssPath
+			if(args["templatecsspath"]){
+				args["templateCssPath"] = args["templatecsspath"];
+			}
+			var cpath = args["templateCssPath"] || this.templateCssPath;
+			if(cpath && !dojo.widget._cssFiles[cpath.toString()]){
+				if((!this.templateCssString)&&(cpath)){
+					this.templateCssString = dojo.hostenv.getText(cpath);
+					this.templateCssPath = null;
+				}
+				dojo.widget._cssFiles[cpath.toString()] = true;
+			}
+		
+			if((this["templateCssString"])&&(!this.templateCssString["loaded"])){
+				dojo.html.insertCssText(this.templateCssString, null, cpath);
+				if(!this.templateCssString){ this.templateCssString = ""; }
+				this.templateCssString.loaded = true;
+			}
+			if(	
+				(!this.preventClobber)&&(
+					(this.templatePath)||
+					(this.templateNode)||
+					(
+						(this["templateString"])&&(this.templateString.length) 
+					)||
+					(
+						(typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
+					)
+				)
+			){
+				// if it looks like we can build the thing from a template, do it!
+				this.buildFromTemplate(args, frag);
+			}else{
+				// otherwise, assign the DOM node that was the source of the widget
+				// parsing to be the root node
+				this.domNode = this.getFragNodeRef(frag);
+			}
+			this.fillInTemplate(args, frag); 	// this is where individual widgets
+												// will handle population of data
+												// from properties, remote data
+												// sets, etc.
+	},
+
+		buildFromTemplate: function(/*Object*/args, /*Object*/frag){
+			// summary:
+			//		Called by buildRendering, creates the actual UI in a DomWidget.
+
+			// var start = new Date();
+			// copy template properties if they're already set in the templates object
+			// dojo.debug("buildFromTemplate:", this);
+			var avoidCache = false;
+			if(args["templatepath"]){
+				avoidCache = true;
+				args["templatePath"] = args["templatepath"];
+			}
+			dojo.widget.fillFromTemplateCache(	this, 
+												args["templatePath"], 
+												null,
+												avoidCache);
+			var ts = dojo.widget._templateCache[this.widgetType];
+			if((ts)&&(!avoidCache)){
+				if(!this.templateString.length){
+					this.templateString = ts["string"];
+				}
+				if(!this.templateNode){
+					this.templateNode = ts["node"];
+				}
+			}
+			var matches = false;
+			var node = null;
+			// var tstr = new String(this.templateString); 
+			var tstr = this.templateString; 
+			// attempt to clone a template node, if there is one
+			if((!this.templateNode)&&(this.templateString)){
+				matches = this.templateString.match(/\$\{([^\}]+)\}/g);
+				if(matches) {
+					// if we do property replacement, don't create a templateNode
+					// to clone from.
+					var hash = this.strings || {};
+					// FIXME: should this hash of default replacements be cached in
+					// templateString?
+					for(var key in dojo.widget.defaultStrings) {
+						if(dojo.lang.isUndefined(hash[key])) {
+							hash[key] = dojo.widget.defaultStrings[key];
+						}
+					}
+					// FIXME: this is a lot of string munging. Can we make it faster?
+					for(var i = 0; i < matches.length; i++) {
+						var key = matches[i];
+						key = key.substring(2, key.length-1);
+						var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key];
+						var value;
+						if((kval)||(dojo.lang.isString(kval))){
+							value = new String((dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval);
+							// Safer substitution, see heading "Attribute values" in  
+							// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
+							while (value.indexOf("\"") > -1) {
+								value=value.replace("\"","&quot;");
+							}
+							tstr = tstr.replace(matches[i], value);
+						}
+					}
+				}else{
+					// otherwise, we are required to instantiate a copy of the template
+					// string if one is provided.
+					
+					// FIXME: need to be able to distinguish here what should be done
+					// or provide a generic interface across all DOM implementations
+					// FIMXE: this breaks if the template has whitespace as its first 
+					// characters
+					// node = this.createNodesFromText(this.templateString, true);
+					// this.templateNode = node[0].cloneNode(true); // we're optimistic here
+					this.templateNode = this.createNodesFromText(this.templateString, true)[0];
+					if(!avoidCache){
+						ts.node = this.templateNode;
+					}
+				}
+			}
+			if((!this.templateNode)&&(!matches)){ 
+				dojo.debug("DomWidget.buildFromTemplate: could not create template");
+				return false;
+			}else if(!matches){
+				node = this.templateNode.cloneNode(true);
+				if(!node){ return false; }
+			}else{
+				node = this.createNodesFromText(tstr, true)[0];
+			}
+
+			// recurse through the node, looking for, and attaching to, our
+			// attachment points which should be defined on the template node.
+
+			this.domNode = node;
+			// dojo.profile.start("attachTemplateNodes");
+			this.attachTemplateNodes();
+			// dojo.profile.end("attachTemplateNodes");
+		
+			// relocate source contents to templated container node
+			// this.containerNode must be able to receive children, or exceptions will be thrown
+			if (this.isContainer && this.containerNode){
+				var src = this.getFragNodeRef(frag);
+				if (src){
+					dojo.dom.moveChildren(src, this.containerNode);
+				}
+			}
+		},
+
+		attachTemplateNodes: function(/*DomNode*/baseNode, /*Widget*/targetObj){
+			// summary: 
+			//		hooks up event handlers and property/node linkages. Calls
+			//		dojo.widget.attachTemplateNodes to do all the hard work.
+			// baseNode: defaults to "this.domNode"
+			// targetObj: defaults to "this"
+			if(!baseNode){ baseNode = this.domNode; }
+			if(!targetObj){ targetObj = this; }
+			return dojo.widget.attachTemplateNodes(baseNode, targetObj, 
+						dojo.widget.getDojoEventsFromStr(this.templateString));
+		},
+
+		fillInTemplate: function(){
+			// summary:
+			//		stub function! sub-classes may use as a default UI
+			//		initializer function. The UI rendering will be available by
+			//		the time this is called from buildRendering. If
+			//		buildRendering is over-ridden, this function may not be
+			//		fired!
+			// dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate");
+		},
+		
+		// method over-ride
+		destroyRendering: function(){
+			// summary: UI destructor
+			try{
+				delete this.domNode;
+			}catch(e){ /* squelch! */ }
+		},
+
+		// FIXME: method over-ride
+		cleanUp: function(){},
+		
+		getContainerHeight: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.getContainerHeight");
+		},
+
+		getContainerWidth: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.getContainerWidth");
+		},
+
+		createNodesFromText: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText");
+		}
+	}
+);

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DomWidget.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownContainer.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownContainer.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownContainer.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownContainer.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,80 @@
+dojo.provide("dojo.widget.DropdownContainer");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.PopupContainer");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.html.util");
+
+// summary:
+//		dojo.widget.DropdownContainer provides an input box and a button for a dropdown.
+//		In subclass, the dropdown can be specified.
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownContainer",
+	dojo.widget.HtmlWidget,
+	{
+		// String: width of the input box
+		inputWidth: "7em",
+		// String: id of this widget
+		id: "",
+		// String: id of the input box
+		inputId: "",
+		// String: name of the input box
+		inputName: "",
+		// dojo.uri.Uri: icon for the dropdown button
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"),
+		// dojo.uri.Uri: alt text for the dropdown button icon
+		iconAlt: "",
+
+		inputNode: null,
+		buttonNode: null,
+		containerNode: null,
+
+		// String: toggle property of the dropdown
+		containerToggle: "plain",
+		// Int: toggle duration property of the dropdown
+		containerToggleDuration: 150,
+		containerAnimInProgress: false,
+
+		templateString: '<span style="white-space:nowrap"><input type="hidden" name="" value="" dojoAttachPoint="valueNode" /><input name="" type="text" value="" style="vertical-align:middle;" dojoAttachPoint="inputNode" autocomplete="off" /> <img src="${this.iconURL}" alt="${this.iconAlt}" dojoAttachEvent="onclick: onIconClick" dojoAttachPoint="buttonNode" style="vertical-align:middle; cursor:pointer; cursor:hand" /></span>',
+		templateCssPath: "",
+
+		fillInTemplate: function(args, frag){
+			var source = this.getFragNodeRef(frag);
+
+			this.popup = dojo.widget.createWidget("PopupContainer", {toggle: this.containerToggle, toggleDuration: this.containerToggleDuration});
+
+			this.containerNode = this.popup.domNode;
+
+			this.domNode.appendChild(this.popup.domNode);
+			if(this.id) { this.domNode.id = this.id; }
+			if(this.inputId){ this.inputNode.id = this.inputId; }
+			if(this.inputName){ this.inputNode.name = this.inputName; }
+			this.inputNode.style.width = this.inputWidth;
+
+			dojo.event.connect(this.inputNode, "onchange", this, "onInputChange");
+		},
+
+		onIconClick: function(evt){
+			if(!this.isEnabled) return;
+			if(!this.popup.isShowingNow){
+				this.popup.open(this.inputNode, this, this.buttonNode);
+			}else{
+				this.popup.close();
+			}
+		},
+
+		hideContainer: function(){
+			// summary: hide the dropdown
+			if(this.popup.isShowingNow){
+				this.popup.close();
+			}
+		},
+
+		onInputChange: function(){
+			// summary: signal for changes in the input box
+		}
+	}
+);

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownContainer.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownDatePicker.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownDatePicker.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownDatePicker.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownDatePicker.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,218 @@
+dojo.provide("dojo.widget.DropdownDatePicker");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.DropdownContainer");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.date.format");
+dojo.require("dojo.date.serialize");
+dojo.require("dojo.string.common");
+dojo.require("dojo.i18n.common");
+dojo.requireLocalization("dojo.widget", "DropdownDatePicker");
+
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownDatePicker",
+	dojo.widget.DropdownContainer,
+	{
+		/*
+		summary: 
+			A form input for entering dates with a pop-up dojo.widget.DatePicker to aid in selection
+
+	 	description: 
+			This is DatePicker in a DropdownContainer, it supports all features of DatePicker.
+	
+			The value displayed in the widget is localized according to the default algorithm provided
+			in dojo.date.format and dojo.date.parse.  It is possible to customize the user-visible formatting
+			with either the formatLength or displayFormat attributes.  The value sent to the server is
+			typically a locale-independent value in a hidden field as defined by the name attribute.
+			RFC3339 representation is used by default, but other options are available with saveFormat.
+
+	 	usage: 
+	 	              var ddp = dojo.widget.createWidget("DropdownDatePicker", {},   
+	 	              dojo.byId("DropdownDatePickerNode")); 
+	 	 
+	 	              <input dojoType="DropdownDatePicker">
+		*/
+
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/dateIcon.gif"),
+		zIndex: "10",
+
+		//String
+		// 	Type of visible formatting used, appropriate to locale (choice of long, short, medium or full)
+		//  See dojo.date.format for details.
+		formatLength: "short",
+		//String
+		// 	Pattern used to display formatted date.  Setting this overrides the locale-specific settings
+		//  which are used by default.  See dojo.date.format for a reference which defines the formatting patterns.
+		displayFormat: "",
+		dateFormat: "", // deprecated in 0.5
+		//String
+		//	Formatting scheme used when submitting the form element.  This formatting is used in a hidden
+		//  field (name) intended for server use, and is therefore typically locale-independent.
+		//  By default, uses rfc3339 style date formatting (rfc)
+		//	Use a pattern string like displayFormat or one of the following:
+		//	rfc|iso|posix|unix
+		saveFormat: "",
+		//String|Date
+		//	form value property if =='today' will default to todays date
+		value: "", 
+		//String
+		// 	name of the form element, used to create a hidden field by this name for form element submission.
+		name: "",
+
+		// Implement various attributes from DatePicker
+
+		//Integer
+		//	total weeks to display default 
+		displayWeeks: 6, 
+		//Boolean
+		//	if true, weekly size of calendar changes to accomodate the month if false, 42 day format is used
+		adjustWeeks: false,
+		//String|Date
+		//	first available date in the calendar set
+		startDate: "1492-10-12",
+		//String|Date
+		//	last available date in the calendar set
+		endDate: "2941-10-12",
+		//Integer
+		//	adjusts the first day of the week 0==Sunday..6==Saturday
+		weekStartsOn: "",
+		//Boolean
+		//	disable all incremental controls, must pick a date in the current display
+		staticDisplay: false,
+		
+		postMixInProperties: function(localProperties, frag){
+			// summary: see dojo.widget.DomWidget
+
+			dojo.widget.DropdownDatePicker.superclass.postMixInProperties.apply(this, arguments);
+			var messages = dojo.i18n.getLocalization("dojo.widget", "DropdownDatePicker", this.lang);
+			this.iconAlt = messages.selectDate;
+			
+			if(typeof(this.value)=='string'&&this.value.toLowerCase()=='today'){
+				this.value = new Date();
+			}
+			if(this.value && isNaN(this.value)){
+				var orig = this.value;
+				this.value = dojo.date.fromRfc3339(this.value);
+				if(!this.value){this.value = new Date(orig); dojo.deprecated("dojo.widget.DropdownDatePicker", "date attributes must be passed in Rfc3339 format", "0.5");}
+			}
+			if(this.value && !isNaN(this.value)){
+				this.value = new Date(this.value);
+			}
+		},
+
+		fillInTemplate: function(args, frag){
+			// summary: see dojo.widget.DomWidget
+			dojo.widget.DropdownDatePicker.superclass.fillInTemplate.call(this, args, frag);
+			//attributes to be passed on to DatePicker
+			var dpArgs = {widgetContainerId: this.widgetId, lang: this.lang, value: this.value,
+				startDate: this.startDate, endDate: this.endDate, displayWeeks: this.displayWeeks,
+				weekStartsOn: this.weekStartsOn, adjustWeeks: this.adjustWeeks, staticDisplay: this.staticDisplay};
+
+			//build the args for DatePicker based on the public attributes of DropdownDatePicker
+			this.datePicker = dojo.widget.createWidget("DatePicker", dpArgs, this.containerNode, "child");
+			dojo.event.connect(this.datePicker, "onValueChanged", this, "onSetDate");
+			
+			if(this.value){
+				this.onSetDate();
+			}
+			this.containerNode.style.zIndex = this.zIndex;
+			this.containerNode.explodeClassName = "calendarBodyContainer";
+			this.valueNode.name=this.name;
+		},
+
+		getValue: function(){
+			// summary: return current date in RFC 3339 format
+			return this.valueNode.value; /*String*/
+		},
+
+		getDate: function(){
+			// summary: return current date as a Date object
+			return this.datePicker.value; /*Date*/
+		},
+
+		setValue: function(/*Date|String*/rfcDate){
+			//summary: set the current date from RFC 3339 formatted string or a date object, synonymous with setDate
+			this.setDate(rfcDate);
+		},
+
+		setDate: function(/*Date|String*/dateObj){
+		//summary: set the current date and update the UI
+			this.datePicker.setDate(dateObj);
+			this._synchValueNode();
+		},
+	
+		onSetDate: function(){
+			if(this.dateFormat){
+				dojo.deprecated("dojo.widget.DropdownDatePicker",
+				"Must use displayFormat attribute instead of dateFormat.  See dojo.date.format for specification.", "0.5");
+				this.inputNode.value = dojo.date.strftime(this.datePicker.value, this.dateFormat, this.lang);
+			}else{
+				this.inputNode.value = dojo.date.format(this.datePicker.value,
+					{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'dateOnly', locale:this.lang});
+			}
+			if(this.value < this.datePicker.startDate||this.value>this.datePicker.endDate){
+				this.inputNode.value = "";
+			}
+			this._synchValueNode();
+			this.onValueChanged(this.getDate());
+			this.hideContainer();
+		},
+
+		onValueChanged: function(/*Date*/dateObj){
+		//summary: triggered when this.value is changed
+		},
+		
+		onInputChange: function(){
+			if(this.dateFormat){
+				dojo.deprecated("dojo.widget.DropdownDatePicker",
+				"Cannot parse user input.  Must use displayFormat attribute instead of dateFormat.  See dojo.date.format for specification.", "0.5");
+			}else{
+				var input = dojo.string.trim(this.inputNode.value);
+				if(input){
+					var inputDate = dojo.date.parse(input,
+							{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'dateOnly', locale:this.lang});			
+					if(inputDate){
+						this.setDate(inputDate);
+					}
+				} else {
+					this.valueNode.value = input;
+				}
+			}
+			// If the date entered didn't parse, reset to the old date.  KISS, for now.
+			//TODO: usability?  should we provide more feedback somehow? an error notice?
+			// seems redundant to do this if the parse failed, but at least until we have validation,
+			// this will fix up the display of entries like 01/32/2006
+			if(input){ this.onSetDate(); }
+		},
+
+		_synchValueNode: function(){
+			var date = this.datePicker.value;
+			var value;
+			switch(this.saveFormat.toLowerCase()){
+				case "rfc": case "iso": case "":
+					value = dojo.date.toRfc3339(date, 'dateOnly');
+					break;
+				case "posix": case "unix":
+					value = Number(date);
+					break;
+				default:
+					value = dojo.date.format(date, {datePattern:this.saveFormat, selector:'dateOnly', locale:this.lang});
+			}
+			this.valueNode.value = value;
+		},
+		
+		enable: function() {
+			this.inputNode.disabled = false;
+			this.datePicker.enable();
+			dojo.widget.DropdownDatePicker.superclass.enable.apply(this, arguments);
+		},
+		
+		disable: function() {
+			this.inputNode.disabled = true;
+			this.datePicker.disable();
+			dojo.widget.DropdownDatePicker.superclass.disable.apply(this, arguments);
+		}
+	}
+);

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownDatePicker.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownTimePicker.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownTimePicker.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownTimePicker.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownTimePicker.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,106 @@
+dojo.provide("dojo.widget.DropdownTimePicker");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.DropdownContainer");
+dojo.require("dojo.widget.TimePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.date.format");
+dojo.require("dojo.date.serialize");
+dojo.require("dojo.i18n.common");
+dojo.requireLocalization("dojo.widget", "DropdownTimePicker");
+
+// summary
+//	input box with a drop-down gui control, for setting the time (hours, minutes, seconds, am/pm) of an event
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownTimePicker",
+	dojo.widget.DropdownContainer,
+	{
+		// URL
+		//	path of icon for button to display time picker widget
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/timeIcon.gif"),
+		
+		// Number
+		//	z-index of time picker widget
+		zIndex: "10",
+
+		// pattern used in display of formatted time.  Uses locale-specific format by default.  See dojo.date.format.
+		displayFormat: "",
+
+		// String
+		//	Deprecated. format string for how time is displayed in the input box using strftime, see dojo.date.strftime	
+		timeFormat: "",
+
+//FIXME: need saveFormat attribute support
+
+		// type of format appropriate to locale.  see dojo.date.format
+		formatLength: "short",
+
+		// String
+		//	time value in RFC3339 format (http://www.ietf.org/rfc/rfc3339.txt)
+		//	ex: 12:00
+		value: "",
+
+		postMixInProperties: function() {
+			dojo.widget.DropdownTimePicker.superclass.postMixInProperties.apply(this, arguments);
+			var messages = dojo.i18n.getLocalization("dojo.widget", "DropdownTimePicker", this.lang);
+			this.iconAlt = messages.selectTime;
+		},
+
+		fillInTemplate: function(){
+			dojo.widget.DropdownTimePicker.superclass.fillInTemplate.apply(this, arguments);
+
+			var timeProps = { widgetContainerId: this.widgetId, lang: this.lang };
+			this.timePicker = dojo.widget.createWidget("TimePicker", timeProps, this.containerNode, "child");
+			dojo.event.connect(this.timePicker, "onSetTime", this, "onSetTime");
+			dojo.event.connect(this.inputNode,  "onchange",  this, "onInputChange");
+			this.containerNode.style.zIndex = this.zIndex;
+			this.containerNode.explodeClassName = "timeBorder";
+			if(this.value){
+				this.timePicker.selectedTime.anyTime = false;
+				this.timePicker.setDateTime("2005-01-01T" + this.value);
+				this.timePicker.initData();
+				this.timePicker.initUI();
+				this.onSetTime();
+			}
+		},
+		
+		onSetTime: function(){
+			// summary: callback when user sets the time via the TimePicker widget
+			if(this.timePicker.selectedTime.anyTime){
+				this.inputNode.value = "";
+			}else if(this.timeFormat){
+				dojo.deprecated("dojo.widget.DropdownTimePicker",
+				"Must use displayFormat attribute instead of timeFormat.  See dojo.date.format for specification.", "0.5");
+				this.inputNode.value = dojo.date.strftime(this.timePicker.time, this.timeFormat, this.lang);
+			}else{
+				this.inputNode.value = dojo.date.format(this.timePicker.time,
+					{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'timeOnly', locale:this.lang});
+			}
+
+			this.hideContainer();
+		},
+		
+		onInputChange: function(){
+			// summary: callback when the user has typed in a time value manually
+			this.timePicker.time = "2005-01-01T" + this.inputNode.value; //FIXME: i18n
+			this.timePicker.setDateTime(this.timePicker.time);
+			this.timePicker.initData();
+			this.timePicker.initUI();
+		},
+		
+		enable: function() {
+			// summary: enable this widget to accept user input
+			this.inputNode.disabled = false;
+			this.timePicker.enable();
+			dojo.widget.DropdownTimePicker.superclass.enable.apply(this, arguments);
+		},
+		
+		disable: function() {
+			// summary: lock this widget so that the user can't change the value
+			this.inputNode.disabled = true;
+			this.timePicker.disable();
+			dojo.widget.DropdownTimePicker.superclass.disable.apply(this, arguments);
+		}
+	}
+);

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/DropdownTimePicker.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,524 @@
+/* TODO:
+ * - font selector
+ * - test, bug fix, more features :)
+*/
+dojo.provide("dojo.widget.Editor");
+dojo.deprecated("dojo.widget.Editor", "is replaced by dojo.widget.Editor2", "0.5");
+
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.Toolbar");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.ColorPalette");
+dojo.require("dojo.string.extras");
+
+dojo.widget.tags.addParseTreeHandler("dojo:Editor");
+
+dojo.widget.Editor = function() {
+	dojo.widget.HtmlWidget.call(this);
+	this.contentFilters = [];
+	this._toolbars = [];
+}
+dojo.inherits(dojo.widget.Editor, dojo.widget.HtmlWidget);
+
+dojo.widget.Editor.itemGroups = {
+	textGroup: ["bold", "italic", "underline", "strikethrough"],
+	blockGroup: ["formatBlock", "fontName", "fontSize"],
+	justifyGroup: ["justifyleft", "justifycenter", "justifyright"],
+	commandGroup: ["save", "cancel"],
+	colorGroup: ["forecolor", "hilitecolor"],
+	listGroup: ["insertorderedlist", "insertunorderedlist"],
+	indentGroup: ["outdent", "indent"],
+	linkGroup: ["createlink", "insertimage", "inserthorizontalrule"]
+};
+
+dojo.widget.Editor.formatBlockValues = {
+	"Normal": "p",
+	"Main heading": "h2",
+	"Sub heading": "h3",
+	"Sub sub heading": "h4",
+	"Preformatted": "pre"
+};
+
+dojo.widget.Editor.fontNameValues = {
+	"Arial": "Arial, Helvetica, sans-serif",
+	"Verdana": "Verdana, sans-serif",
+	"Times New Roman": "Times New Roman, serif",
+	"Courier": "Courier New, monospace"
+};
+
+dojo.widget.Editor.fontSizeValues = {
+	"1 (8 pt)" : "1",
+	"2 (10 pt)": "2",
+	"3 (12 pt)": "3",
+	"4 (14 pt)": "4",
+	"5 (18 pt)": "5",
+	"6 (24 pt)": "6",
+	"7 (36 pt)": "7"
+};
+
+dojo.widget.Editor.defaultItems = [
+	"commandGroup", "|", "blockGroup", "|", "textGroup", "|", "colorGroup", "|", "justifyGroup", "|", "listGroup", "indentGroup", "|", "linkGroup"
+];
+
+// ones we support by default without asking the RichText component
+// NOTE: you shouldn't put buttons like bold, italic, etc in here
+dojo.widget.Editor.supportedCommands = ["save", "cancel", "|", "-", "/", " "];
+
+dojo.lang.extend(dojo.widget.Editor, {
+	widgetType: "Editor",
+
+	saveUrl: "",
+	saveMethod: "post",
+	saveArgName: "editorContent",
+	closeOnSave: false,
+	items: dojo.widget.Editor.defaultItems,
+	formatBlockItems: dojo.lang.shallowCopy(dojo.widget.Editor.formatBlockValues),
+	fontNameItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontNameValues),
+	fontSizeItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontSizeValues),
+
+	// used to get the properties of an item if it is given as a string
+	getItemProperties: function(name) {
+		var props = {};
+		switch(name.toLowerCase()) {
+			case "bold":
+			case "italic":
+			case "underline":
+			case "strikethrough":
+				props.toggleItem = true;
+				break;
+
+			case "justifygroup":
+				props.defaultButton = "justifyleft";
+				props.preventDeselect = true;
+				props.buttonGroup = true;
+				break;
+
+			case "listgroup":
+				props.buttonGroup = true;
+				break;
+
+			case "save":
+			case "cancel":
+				props.label = dojo.string.capitalize(name);
+				break;
+
+			case "forecolor":
+			case "hilitecolor":
+				props.name = name;
+				props.toggleItem = true; // FIXME: they aren't exactly toggle items
+				props.icon = this.getCommandImage(name);
+				break;
+
+			case "formatblock":
+				props.name = "formatBlock";
+				props.values = this.formatBlockItems;
+				break;
+
+			case "fontname":
+				props.name = "fontName";
+				props.values = this.fontNameItems;
+
+			case "fontsize":
+				props.name = "fontSize";
+				props.values = this.fontSizeItems;
+		}
+		return props;
+	},
+
+	validateItems: true, // set to false to add items, regardless of support
+	focusOnLoad: true,
+	minHeight: "1em",
+
+	_richText: null, // RichText widget
+	_richTextType: "RichText",
+
+	_toolbarContainer: null, // ToolbarContainer widget
+	_toolbarContainerType: "ToolbarContainer",
+
+	_toolbars: [],
+	_toolbarType: "Toolbar",
+
+	_toolbarItemType: "ToolbarItem",
+
+	buildRendering: function(args, frag) {
+		// get the node from args/frag
+		var node = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+		var trt = dojo.widget.createWidget(this._richTextType, {
+			focusOnLoad: this.focusOnLoad,
+			minHeight: this.minHeight
+		}, node)
+		var _this = this;
+		// this appears to fix a weird timing bug on Safari
+		setTimeout(function(){
+			_this.setRichText(trt);
+
+			_this.initToolbar();
+
+			_this.fillInTemplate(args, frag);
+		}, 0);
+	},
+
+	setRichText: function(richText) {
+		if(this._richText && this._richText == richText) {
+			dojo.debug("Already set the richText to this richText!");
+			return;
+		}
+
+		if(this._richText && !this._richText.isClosed) {
+			dojo.debug("You are switching richTexts yet you haven't closed the current one. Losing reference!");
+		}
+		this._richText = richText;
+		dojo.event.connect(this._richText, "close", this, "onClose");
+		dojo.event.connect(this._richText, "onLoad", this, "onLoad");
+		dojo.event.connect(this._richText, "onDisplayChanged", this, "updateToolbar");
+		if(this._toolbarContainer) {
+			this._toolbarContainer.enable();
+			this.updateToolbar(true);
+		}
+	},
+
+	initToolbar: function() {
+		// var tic = new Date();
+		if(this._toolbarContainer) { return; } // only create it once
+		this._toolbarContainer = dojo.widget.createWidget(this._toolbarContainerType);
+		var tb = this.addToolbar();
+		var last = true;
+		for(var i = 0; i < this.items.length; i++) {
+			if(this.items[i] == "\n") { // new row
+				tb = this.addToolbar();
+			} else {
+				if((this.items[i] == "|")&&(!last)){
+					last = true;
+				}else{
+					last = this.addItem(this.items[i], tb);
+				}
+			}
+		}
+		this.insertToolbar(this._toolbarContainer.domNode, this._richText.domNode);
+		// alert(new Date - tic);
+	},
+
+	// allow people to override this so they can make their own placement logic
+	insertToolbar: function(tbNode, richTextNode) {
+		dojo.html.insertBefore(tbNode, richTextNode);
+		//dojo.html.insertBefore(this._toolbarContainer.domNode, this._richText.domNode);
+	},
+
+	addToolbar: function(toolbar) {
+		this.initToolbar();
+		if(!(toolbar instanceof dojo.widget.Toolbar)) {
+			toolbar = dojo.widget.createWidget(this._toolbarType);
+		}
+		this._toolbarContainer.addChild(toolbar);
+		this._toolbars.push(toolbar);
+		return toolbar;
+	},
+
+	addItem: function(item, tb, dontValidate) {
+		if(!tb) { tb = this._toolbars[0]; }
+		var cmd = ((item)&&(!dojo.lang.isUndefined(item["getValue"]))) ?  cmd = item["getValue"](): item;
+
+		var groups = dojo.widget.Editor.itemGroups;
+		if(item instanceof dojo.widget.ToolbarItem) {
+			tb.addChild(item);
+		} else if(groups[cmd]) {
+			var group = groups[cmd];
+			var worked = true;
+			if(cmd == "justifyGroup" || cmd == "listGroup") {
+				var btnGroup = [cmd];
+				for(var i = 0 ; i < group.length; i++) {
+					if(dontValidate || this.isSupportedCommand(group[i])) {
+						btnGroup.push(this.getCommandImage(group[i]));
+					}else{
+						worked = false;
+					}
+				}
+				if(btnGroup.length){
+					/*
+					// the addChild interface is assinine. Work around it.
+					var tprops = this.getItemProperties(cmd);
+					var tmpGroup = dojo.widget.createWidget("ToolbarButtonGroup", tprops);
+					dojo.debug(btnGroup);
+					dojo.event.connect(tmpGroup, "onClick", this, "_action");
+					dojo.event.connect(tmpGroup, "onChangeSelect", this, "_action");
+					*/
+					var btn = tb.addChild(btnGroup, null, this.getItemProperties(cmd));
+					dojo.event.connect(btn, "onClick", this, "_action");
+					dojo.event.connect(btn, "onChangeSelect", this, "_action");
+				}
+				return worked;
+			} else {
+				for(var i = 0; i < group.length; i++) {
+					if(!this.addItem(group[i], tb)){
+						worked = false;
+					}
+				}
+				return worked;
+			}
+		} else {
+			if((!dontValidate)&&(!this.isSupportedCommand(cmd))){
+				return false;
+			}
+			if(dontValidate || this.isSupportedCommand(cmd)) {
+				cmd = cmd.toLowerCase();
+				if(cmd == "formatblock") {
+					var select = dojo.widget.createWidget("ToolbarSelect", {
+						name: "formatBlock",
+						values: this.formatBlockItems
+					});
+					tb.addChild(select);
+					var _this = this;
+					dojo.event.connect(select, "onSetValue", function(item, value) {
+						_this.onAction("formatBlock", value);
+					});
+				} else if(cmd == "fontname") {
+					var select = dojo.widget.createWidget("ToolbarSelect", {
+						name: "fontName",
+						values: this.fontNameItems
+					});
+					tb.addChild(select);
+					dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) {
+						this.onAction("fontName", value);
+					}));
+				} else if(cmd == "fontsize") {
+					var select = dojo.widget.createWidget("ToolbarSelect", {
+						name: "fontSize",
+						values: this.fontSizeItems
+					});
+					tb.addChild(select);
+					dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) {
+						this.onAction("fontSize", value);
+					}));
+				} else if(dojo.lang.inArray(cmd, ["forecolor", "hilitecolor"])) {
+					var btn = tb.addChild(dojo.widget.createWidget("ToolbarColorDialog", this.getItemProperties(cmd)));
+					dojo.event.connect(btn, "onSetValue", this, "_setValue");
+				} else {
+					var btn = tb.addChild(this.getCommandImage(cmd), null, this.getItemProperties(cmd));
+					if(cmd == "save"){
+						dojo.event.connect(btn, "onClick", this, "_save");
+					}else if(cmd == "cancel"){
+						dojo.event.connect(btn, "onClick", this, "_close");
+					} else {
+						dojo.event.connect(btn, "onClick", this, "_action");
+						dojo.event.connect(btn, "onChangeSelect", this, "_action");
+					}
+				}
+			}
+		}
+		return true;
+	},
+
+	enableToolbar: function() {
+		if(this._toolbarContainer) {
+			this._toolbarContainer.domNode.style.display = "";
+			this._toolbarContainer.enable();
+		}
+	},
+
+	disableToolbar: function(hide){
+		if(hide){
+			if(this._toolbarContainer){
+				this._toolbarContainer.domNode.style.display = "none";
+			}
+		}else{
+			if(this._toolbarContainer){
+				this._toolbarContainer.disable();
+			}
+		}
+	},
+
+	_updateToolbarLastRan: null,
+	_updateToolbarTimer: null,
+	_updateToolbarFrequency: 500,
+
+	updateToolbar: function(force) {
+		if(!this._toolbarContainer) { return; }
+
+		// keeps the toolbar from updating too frequently
+		// TODO: generalize this functionality?
+		var diff = new Date() - this._updateToolbarLastRan;
+		if(!force && this._updateToolbarLastRan && (diff < this._updateToolbarFrequency)) {
+			clearTimeout(this._updateToolbarTimer);
+			var _this = this;
+			this._updateToolbarTimer = setTimeout(function() {
+				_this.updateToolbar();
+			}, this._updateToolbarFrequency/2);
+			return;
+		} else {
+			this._updateToolbarLastRan = new Date();
+		}
+		// end frequency checker
+
+		var items = this._toolbarContainer.getItems();
+		for(var i = 0; i < items.length; i++) {
+			var item = items[i];
+			if(item instanceof dojo.widget.ToolbarSeparator) { continue; }
+			var cmd = item._name;
+			if (cmd == "save" || cmd == "cancel") { continue; }
+			else if(cmd == "justifyGroup") {
+				try {
+					if(!this._richText.queryCommandEnabled("justifyleft")) {
+						item.disable(false, true);
+					} else {
+						item.enable(false, true);
+						var jitems = item.getItems();
+						for(var j = 0; j < jitems.length; j++) {
+							var name = jitems[j]._name;
+							var value = this._richText.queryCommandValue(name);
+							if(typeof value == "boolean" && value) {
+								value = name;
+								break;
+							} else if(typeof value == "string") {
+								value = "justify"+value;
+							} else {
+								value = null;
+							}
+						}
+						if(!value) { value = "justifyleft"; } // TODO: query actual style
+						item.setValue(value, false, true);
+					}
+				} catch(err) {}
+			} else if(cmd == "listGroup") {
+				var litems = item.getItems();
+				for(var j = 0; j < litems.length; j++) {
+					this.updateItem(litems[j]);
+				}
+			} else {
+				this.updateItem(item);
+			}
+		}
+	},
+
+	updateItem: function(item) {
+		try {
+			var cmd = item._name;
+			var enabled = this._richText.queryCommandEnabled(cmd);
+			item.setEnabled(enabled, false, true);
+
+			var active = this._richText.queryCommandState(cmd);
+			if(active && cmd == "underline") {
+				// don't activate underlining if we are on a link
+				active = !this._richText.queryCommandEnabled("unlink");
+			}
+			item.setSelected(active, false, true);
+			return true;
+		} catch(err) {
+			return false;
+		}
+	},
+
+	supportedCommands: dojo.widget.Editor.supportedCommands.concat(),
+
+	isSupportedCommand: function(cmd) {
+		// FIXME: how do we check for ActiveX?
+		var yes = dojo.lang.inArray(cmd, this.supportedCommands);
+		if(!yes) {
+			try {
+				var richText = this._richText || dojo.widget.HtmlRichText.prototype;
+				yes = richText.queryCommandAvailable(cmd);
+			} catch(E) {}
+		}
+		return yes;
+	},
+
+	getCommandImage: function(cmd) {
+		if(cmd == "|") {
+			return cmd;
+		} else {
+			return dojo.uri.dojoUri("src/widget/templates/buttons/" + cmd + ".gif");
+		}
+	},
+
+	_action: function(e) {
+		this._fire("onAction", e.getValue());
+	},
+
+	_setValue: function(a, b) {
+		this._fire("onAction", a.getValue(), b);
+	},
+
+	_save: function(e){
+		// FIXME: how should this behave when there's a larger form in play?
+		if(!this._richText.isClosed){
+			if(this.saveUrl.length){
+				var content = {};
+				content[this.saveArgName] = this.getHtml();
+				dojo.io.bind({
+					method: this.saveMethod,
+					url: this.saveUrl,
+					content: content
+				});
+			}else{
+				dojo.debug("please set a saveUrl for the editor");
+			}
+			if(this.closeOnSave){
+				this._richText.close(e.getName().toLowerCase() == "save");
+			}
+		}
+	},
+
+	_close: function(e) {
+		if(!this._richText.isClosed) {
+			this._richText.close(e.getName().toLowerCase() == "save");
+		}
+	},
+
+	onAction: function(cmd, value) {
+		switch(cmd) {
+			case "createlink":
+				if(!(value = prompt("Please enter the URL of the link:", "http://"))) {
+					return;
+				}
+				break;
+			case "insertimage":
+				if(!(value = prompt("Please enter the URL of the image:", "http://"))) {
+					return;
+				}
+				break;
+		}
+		this._richText.execCommand(cmd, value);
+	},
+
+	fillInTemplate: function(args, frag) {
+		// dojo.event.connect(this, "onResized", this._richText, "onResized");
+	},
+
+	_fire: function(eventName) {
+		if(dojo.lang.isFunction(this[eventName])) {
+			var args = [];
+			if(arguments.length == 1) {
+				args.push(this);
+			} else {
+				for(var i = 1; i < arguments.length; i++) {
+					args.push(arguments[i]);
+				}
+			}
+			this[eventName].apply(this, args);
+		}
+	},
+
+	getHtml: function(){
+		this._richText.contentFilters = this._richText.contentFilters.concat(this.contentFilters);
+		return this._richText.getEditorContent();
+	},
+
+	getEditorContent: function(){
+		return this.getHtml();
+	},
+
+	onClose: function(save, hide){
+		this.disableToolbar(hide);
+		if(save) {
+			this._fire("onSave");
+		} else {
+			this._fire("onCancel");
+		}
+	},
+
+	// events baby!
+	onLoad: function(){},
+	onSave: function(){},
+	onCancel: function(){}
+});
+

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor2.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor2.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor2.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor2.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,656 @@
+/* TODO:
+ * - font selector
+ * - test, bug fix, more features :)
+*/
+dojo.provide("dojo.widget.Editor2");
+
+dojo.require("dojo.io.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.Editor2Toolbar");
+
+// Object: Manager of current focused Editor2 Instance and available editor2 commands
+dojo.widget.Editor2Manager = {
+	_currentInstance: null,
+	_loadedCommands: {},
+
+	// Object: state a command may be in
+	commandState: {Disabled: 0, Latched: 1, Enabled: 2},
+
+	getCurrentInstance: function(){
+		// summary: Return the current focused Editor2 instance
+		return this._currentInstance;
+	},
+	setCurrentInstance: function(/*Widget*/inst){
+		// summary: Set current focused Editor2 instance
+		this._currentInstance = inst;
+	},
+	registerCommand: function(/*String*/name, /*Object*/cmd){
+		// summary: Register an Editor2 command
+		// name: name of the command (case insensitive)
+		// cmd: an object which implements interface dojo.widget.Editor2Command
+		name = name.toLowerCase();
+		if(this._loadedCommands[name]){
+			delete this._loadedCommands[name];
+		}
+		this._loadedCommands[name] = cmd;
+	},
+	getCommand: function(/*String*/name){
+		// summary: Return Editor2 command with the given name
+		// name: name of the command (case insensitive)
+		name = name.toLowerCase();
+		var oCommand = this._loadedCommands[name];
+		if(oCommand){
+			return oCommand;
+		}
+
+		switch(name){
+			case 'htmltoggle':
+				//Editor2 natively provide the htmltoggle functionalitity
+				//and it is treated as a builtin command
+				oCommand = new dojo.widget.Editor2BrowserCommand(name);
+				break;
+			case 'formatblock':
+				oCommand = new dojo.widget.Editor2FormatBlockCommand(name);
+				break;
+			case 'anchor':
+				oCommand = new dojo.widget.Editor2Command(name);
+				break;
+
+			//dialog command
+			case 'createlink':
+				oCommand = new dojo.widget.Editor2DialogCommand(name,
+						{contentFile: "dojo.widget.Editor2Plugin.CreateLinkDialog",
+							contentClass: "Editor2CreateLinkDialog",
+							title: "Insert/Edit Link", width: "300px", height: "200px"});
+				break;
+			case 'insertimage':
+				oCommand = new dojo.widget.Editor2DialogCommand(name,
+						{contentFile: "dojo.widget.Editor2Plugin.InsertImageDialog",
+							contentClass: "Editor2InsertImageDialog",
+							title: "Insert/Edit Image", width: "400px", height: "270px"});
+				break;
+			// By default we assume that it is a builtin simple command.
+			default:
+				var curtInst = this.getCurrentInstance();
+				if((curtInst && curtInst.queryCommandAvailable(name)) ||
+					(!curtInst && dojo.widget.Editor2.prototype.queryCommandAvailable(name))){
+					oCommand = new dojo.widget.Editor2BrowserCommand(name);
+				}else{
+					dojo.debug("dojo.widget.Editor2Manager.getCommand: Unknown command "+name);
+					return;
+				}
+		}
+		this._loadedCommands[name] = oCommand;
+		return oCommand;
+	},
+	destroy: function(){
+		// summary: Cleaning up. This is called automatically on page unload.
+		this._currentInstance = null;
+		for(var cmd in this._loadedCommands){
+			this._loadedCommands[cmd].destory();
+		}
+	}
+};
+
+dojo.addOnUnload(dojo.widget.Editor2Manager, "destroy");
+
+// summary:
+//		dojo.widget.Editor2Command is the base class for all command in Editor2
+dojo.lang.declare("dojo.widget.Editor2Command",null,{
+		initializer: function(name){
+			// summary: Constructor of this class
+			this._name = name;
+		},
+		//this function should be re-implemented in subclass
+		execute: function(para){
+			// summary: Execute the command. should be implemented in subclass
+			dojo.unimplemented("dojo.widget.Editor2Command.execute");
+		},
+		//default implemetation always returns Enabled
+		getState: function(){
+			// summary:
+			//		Return the state of the command. The default behavior is
+			//		to always return Enabled
+			return dojo.widget.Editor2Manager.commandState.Enabled;
+		},
+		destory: function(){
+			// summary: Destructor
+		}
+	}
+);
+
+// summary:
+//		dojo.widget.Editor2BrowserCommand is the base class for all the browser built
+//		in commands
+dojo.lang.declare("dojo.widget.Editor2BrowserCommand", dojo.widget.Editor2Command, {
+		execute: function(para){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				curInst.execCommand(this._name, para);
+			}
+		},
+		getState: function(){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				try{
+					if(curInst.queryCommandEnabled(this._name)){
+						if(curInst.queryCommandState(this._name)){
+							return dojo.widget.Editor2Manager.commandState.Latched;
+						}else{
+							return dojo.widget.Editor2Manager.commandState.Enabled;
+						}
+					}else{
+						return dojo.widget.Editor2Manager.commandState.Disabled;
+					}
+				}catch (e) {
+					//dojo.debug("exception when getting state for command "+this._name+": "+e);
+					return dojo.widget.Editor2Manager.commandState.Enabled;
+				}
+			}
+			return dojo.widget.Editor2Manager.commandState.Disabled;
+		},
+		getValue: function(){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				try{
+					return curInst.queryCommandValue(this._name);
+				}catch(e){}
+			}
+		}
+	}
+);
+
+dojo.lang.declare("dojo.widget.Editor2FormatBlockCommand", dojo.widget.Editor2BrowserCommand, {
+		/* In none-ActiveX mode under IE, <p> and no <p> text can not be distinguished
+		getCurrentValue: function(){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(!curInst){ return ''; }
+
+			var h = dojo.render.html;
+
+			// safari f's us for selection primitives
+			if(h.safari){ return ''; }
+
+			var selectedNode = (h.ie) ? curInst.document.selection.createRange().parentElement() : curInst.window.getSelection().anchorNode;
+			// make sure we actuall have an element
+			while((selectedNode)&&(selectedNode.nodeType != 1)){
+				selectedNode = selectedNode.parentNode;
+			}
+			if(!selectedNode){ return ''; }
+
+			var formats = ["p", "pre", "h1", "h2", "h3", "h4", "h5", "h6", "address"];
+			// gotta run some specialized updates for the various
+			// formatting options
+			var type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())];
+			while((selectedNode!=curInst.editNode)&&(!type)){
+				selectedNode = selectedNode.parentNode;
+				if(!selectedNode){ break; }
+				type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())];
+			}
+			if(!type){
+				type = "";
+			}
+			return type;
+		}*/
+	}
+);
+
+dojo.require("dojo.widget.FloatingPane");
+// summary:
+//		dojo.widget.Editor2Dialog provides a Dialog which can be modal or normal for the Editor2.
+dojo.widget.defineWidget(
+	"dojo.widget.Editor2Dialog",
+	[dojo.widget.HtmlWidget, dojo.widget.FloatingPaneBase, dojo.widget.ModalDialogBase],
+	{
+		templatePath: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorDialog.html"),
+		// Boolean: Whether this is a modal dialog. True by default.
+		modal: true,
+//		refreshOnShow: true, //for debug for now
+
+		// String: Wwidth of the dialog. None by default
+		width: false,
+		// String: Height of the dialog. None by default
+		height: false,
+
+		// String: startup state of the dialog
+		windowState: "minimized",
+
+		displayCloseAction: true,
+
+		contentFile: "",
+		contentClass: "",
+
+		fillInTemplate: function(args, frag){
+			this.fillInFloatingPaneTemplate(args, frag);
+			dojo.widget.Editor2Dialog.superclass.fillInTemplate.call(this, args, frag);
+		},
+		postCreate: function(){
+			if(this.contentFile){
+				dojo.require(this.contentFile);
+			}
+			if(this.modal){
+				dojo.widget.ModalDialogBase.prototype.postCreate.call(this);
+			}else{
+				with(this.domNode.style) {
+					zIndex = 999;
+					display = "none";
+				}
+			}
+			dojo.widget.FloatingPaneBase.prototype.postCreate.apply(this, arguments);
+			dojo.widget.Editor2Dialog.superclass.postCreate.call(this);
+			if(this.width && this.height){
+				with(this.domNode.style){
+					width = this.width;
+					height = this.height;
+				}
+			}
+		},
+		createContent: function(){
+			if(!this.contentWidget && this.contentClass){
+				this.contentWidget = dojo.widget.createWidget(this.contentClass);
+				this.addChild(this.contentWidget);
+			}
+		},
+		show: function(){
+			if(!this.contentWidget){
+				//buggy IE: if the dialog is hidden, the button widgets
+				//in the dialog can not be shown, so show it temporary (as the
+				//dialog may decide not to show it in loadContent() later)
+				dojo.widget.Editor2Dialog.superclass.show.apply(this, arguments);
+				this.createContent();
+				dojo.widget.Editor2Dialog.superclass.hide.call(this);
+			}
+
+			if(!this.contentWidget || !this.contentWidget.loadContent()){
+				return;
+			}
+			this.showFloatingPane();
+			dojo.widget.Editor2Dialog.superclass.show.apply(this, arguments);
+			if(this.modal){
+				this.showModalDialog();
+			}
+			this.placeModalDialog();
+			if(this.modal){
+				//place the background div under this modal pane
+				this.shared.bg.style.zIndex = this.domNode.style.zIndex-1;
+			}
+		},
+		onShow: function(){
+			dojo.widget.Editor2Dialog.superclass.onShow.call(this);
+			this.onFloatingPaneShow();
+		},
+		closeWindow: function(){
+			this.hide();
+			dojo.widget.Editor2Dialog.superclass.closeWindow.apply(this, arguments);
+		},
+		hide: function(){
+			if(this.modal){
+				this.hideModalDialog();
+			}
+			dojo.widget.Editor2Dialog.superclass.hide.call(this);
+		}
+	}
+);
+
+// summary:
+//		dojo.widget.Editor2DialogContent is the actual content of a Editor2Dialog.
+//		This class should be subclassed to provide the content.
+dojo.widget.defineWidget(
+	"dojo.widget.Editor2DialogContent",
+	dojo.widget.HtmlWidget,
+{
+	widgetsInTemplate: true,
+
+	loadContent:function(){
+		// summary: Load the content. Called by Editor2Dialog when first shown
+		return true;
+	},
+	cancel: function(){
+		// summary: Default handler when cancel button is clicked.
+		this.parent.hide();
+	}
+});
+
+// summary:
+//		dojo.widget.Editor2DialogCommand provides an easy way to popup a dialog when
+//		the command is executed.
+dojo.lang.declare("dojo.widget.Editor2DialogCommand", dojo.widget.Editor2BrowserCommand,
+	function(name, dialogParas){
+		this.dialogParas = dialogParas;
+	},
+{
+	execute: function(){
+		if(!this.dialog){
+			if(!this.dialogParas.contentFile || !this.dialogParas.contentClass){
+				alert("contentFile and contentClass should be set for dojo.widget.Editor2DialogCommand.dialogParas!");
+				return;
+			}
+			this.dialog = dojo.widget.createWidget("Editor2Dialog", this.dialogParas);
+
+			dojo.body().appendChild(this.dialog.domNode);
+
+			dojo.event.connect(this, "destroy", this.dialog, "destroy");
+		}
+		this.dialog.show();
+	}
+});
+
+// summary:
+//		dojo.widget.Editor2 is the WYSIWYG editor in dojo with toolbar. It supports a plugin
+//		framework which can be used to extend the functionalities of the editor, such as
+//		adding a context menu, table operation etc.
+// description:
+//		Plugins are available using dojo's require syntax. Please find available built-in plugins
+//		under src/widget/Editor2Plugin.
+dojo.widget.defineWidget(
+	"dojo.widget.Editor2",
+	dojo.widget.RichText,
+	{
+//		// String: url to which save action should send content to
+//		saveUrl: "",
+//		// String: HTTP method for save (post or get)
+//		saveMethod: "post",
+//		saveArgName: "editorContent",
+//		closeOnSave: false,
+
+		// Boolean: Whether to share toolbar with other instances of Editor2
+		shareToolbar: false,
+		// Boolean: Whether the toolbar should scroll to keep it in the view
+		toolbarAlwaysVisible: false,
+
+//		htmlEditing: false,
+
+		toolbarWidget: null,
+		scrollInterval: null,
+
+		// Object: dojo.uri.Uri object to specify the template file for the toolbar
+		toolbarTemplatePath: dojo.uri.dojoUri("src/widget/templates/EditorToolbarOneline.html"),
+		// Object: dojo.uri.Uri object to specify the css file for the toolbar
+		toolbarTemplateCssPath: null,
+//		toolbarTemplatePath: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbarFCKStyle.html"),
+//		toolbarTemplateCssPath: dojo.uri.dojoUri("src/widget/templates/Editor2/FCKDefault/EditorToolbarFCKStyle.css"),
+
+		_inSourceMode: false,
+		_htmlEditNode: null,
+
+		editorOnLoad: function(){
+			// summary:
+			//		Create toolbar and other initialization routines. This is called after
+			//		the finish of the loading of document in the editing element
+//			dojo.profile.start("dojo.widget.Editor2::editorOnLoad");
+
+			dojo.event.topic.publish("dojo.widget.Editor2::preLoadingToolbar", this);
+			if(this.toolbarAlwaysVisible){
+				dojo.require("dojo.widget.Editor2Plugin.AlwaysShowToolbar");
+			}
+
+			var toolbars = dojo.widget.byType("Editor2Toolbar");
+			if((!toolbars.length)||(!this.shareToolbar)){
+				if(this.toolbarWidget){
+					this.toolbarWidget.show();
+					//re-add the toolbar to the new domNode (caused by open() on another element)
+					dojo.html.insertBefore(this.toolbarWidget.domNode, this.domNode.firstChild);
+				}else{
+					var tbOpts = {};
+					tbOpts.templatePath = this.toolbarTemplatePath;
+					if(this.toolbarTemplateCssPath){
+						tbOpts.templateCssPath = this.toolbarTemplateCssPath;
+					}
+					this.toolbarWidget = dojo.widget.createWidget("Editor2Toolbar", tbOpts, this.domNode.firstChild, "before");
+
+					dojo.event.connect(this, "close", this.toolbarWidget, "hide");
+
+					this.toolbarLoaded();
+				}
+			}else{
+				// FIXME: 	selecting in one shared toolbar doesn't clobber
+				// 			selection in the others. This is problematic.
+				this.toolbarWidget = toolbars[0];
+			}
+
+			dojo.event.topic.registerPublisher("Editor2.clobberFocus", this, "clobberFocus");
+			dojo.event.topic.subscribe("Editor2.clobberFocus", this, "setBlur");
+
+			dojo.event.topic.publish("dojo.widget.Editor2::onLoad", this);
+//			dojo.profile.end("dojo.widget.Editor2::editorOnLoad");
+		},
+
+		//event for plugins to use
+		toolbarLoaded: function(){
+			// summary:
+			//		Fired when the toolbar for this editor is created.
+			//		This event is for plugins to use
+		},
+
+		//TODO: provide a query mechanism about loaded plugins?
+		registerLoadedPlugin: function(/*Object*/obj){
+			// summary: Register a plugin which is loaded for this instance
+			if(!this.loadedPlugins){
+				this.loadedPlugins = [];
+			}
+			this.loadedPlugins.push(obj);
+		},
+		unregisterLoadedPlugin: function(/*Object*/obj){
+			// summery: Delete a loaded plugin for this instance
+			for(var i in this.loadedPlugins){
+				if(this.loadedPlugins[i] === obj){
+					delete this.loadedPlugins[i];
+					return;
+				}
+			}
+			dojo.debug("dojo.widget.Editor2.unregisterLoadedPlugin: unknow plugin object: "+obj);
+		},
+
+		//overload the original ones to provide extra commands
+		execCommand: function(/*String*/command, argument){
+			switch(command.toLowerCase()){
+				case 'htmltoggle':
+					this.toggleHtmlEditing();
+					break;
+				default:
+					dojo.widget.Editor2.superclass.execCommand.apply(this, arguments);
+			}
+		},
+		queryCommandEnabled: function(/*String*/command, argument){
+			switch(command.toLowerCase()){
+				case 'htmltoggle':
+					return true;
+				default:
+					if(this._inSourceMode){ return false;}
+					return dojo.widget.Editor2.superclass.queryCommandEnabled.apply(this, arguments);
+			}
+		},
+		queryCommandState: function(/*String*/command, argument){
+			switch(command.toLowerCase()){
+				case 'htmltoggle':
+					return this._inSourceMode;
+				default:
+					return dojo.widget.Editor2.superclass.queryCommandState.apply(this, arguments);
+			}
+		},
+
+		onClick: function(/*Event*/e){
+			dojo.widget.Editor2.superclass.onClick.call(this, e);
+			//if Popup is used, call dojo.widget.PopupManager.onClick
+			//manually when click in the editing area to close all
+			//open popups (dropdowns)
+			if(dojo.widget.PopupManager){
+				if(!e){ //IE
+					e = this.window.event;
+				}
+				dojo.widget.PopupManager.onClick(e);
+			}
+		},
+
+		clobberFocus: function(){
+			// summary: stub to signal other instances to clobber focus
+		},
+		toggleHtmlEditing: function(){
+			// summary: toggle between WYSIWYG mode and HTML source mode
+			if(this===dojo.widget.Editor2Manager.getCurrentInstance()){
+				if(!this._inSourceMode){
+					this._inSourceMode = true;
+
+					if(!this._htmlEditNode){
+						this._htmlEditNode = dojo.doc().createElement("textarea");
+						dojo.html.insertAfter(this._htmlEditNode, this.editorObject);
+					}
+					this._htmlEditNode.style.display = "";
+					this._htmlEditNode.style.width = "100%";
+					this._htmlEditNode.style.height = dojo.html.getBorderBox(this.editNode).height+"px";
+					this._htmlEditNode.value = this.editNode.innerHTML;
+
+					//activeX object (IE) doesn't like to be hidden, so move it outside of screen instead
+					with(this.editorObject.style){
+						position = "absolute";
+						left = "-2000px";
+						top = "-2000px";
+					}
+				}else{
+					this._inSourceMode = false;
+
+					//In IE activeX mode, if _htmlEditNode is focused,
+					//when toggling, an error would occur, so unfocus it
+					this._htmlEditNode.blur();
+
+					with(this.editorObject.style){
+						position = "";
+						left = "";
+						top = "";
+					}
+
+					dojo.lang.setTimeout(this, "replaceEditorContent", 1, this._htmlEditNode.value);
+					this._htmlEditNode.style.display = "none";
+					this.focus();
+				}
+				this.updateToolbar(true);
+			}
+		},
+
+		setFocus: function(){
+			// summary: focus is set on this instance
+//			dojo.debug("setFocus: start "+this.widgetId);
+			if(dojo.widget.Editor2Manager.getCurrentInstance() === this){ return; }
+
+			this.clobberFocus();
+//			dojo.debug("setFocus:", this);
+			dojo.widget.Editor2Manager.setCurrentInstance(this);
+		},
+
+		setBlur: function(){
+			// summary: focus on this instance is lost
+//			 dojo.debug("setBlur:", this);
+			//dojo.event.disconnect(this.toolbarWidget, "exec", this, "execCommand");
+		},
+
+		saveSelection: function(){
+			// summary: save the current selection for restoring it
+			this._bookmark = null;
+			this._bookmark = dojo.withGlobal(this.window, dojo.html.selection.getBookmark);
+		},
+		restoreSelection: function(){
+			// summary: restore the last saved selection
+			if(this._bookmark){
+				this.focus(); //require for none-activeX IE
+				dojo.withGlobal(this.window, "moveToBookmark", dojo.html.selection, [this._bookmark]);
+				this._bookmark = null;
+			}else{
+				dojo.debug("restoreSelection: no saved selection is found!");
+			}
+		},
+
+		_updateToolbarLastRan: null,
+		_updateToolbarTimer: null,
+		_updateToolbarFrequency: 500,
+
+		updateToolbar: function(/*Boolean*/force){
+			// summary: update the associated toolbar of this Editor2
+			if((!this.isLoaded)||(!this.toolbarWidget)){ return; }
+
+			// keeps the toolbar from updating too frequently
+			// TODO: generalize this functionality?
+			var diff = new Date() - this._updateToolbarLastRan;
+			if( (!force)&&(this._updateToolbarLastRan)&&
+				((diff < this._updateToolbarFrequency)) ){
+
+				clearTimeout(this._updateToolbarTimer);
+				var _this = this;
+				this._updateToolbarTimer = setTimeout(function() {
+					_this.updateToolbar();
+				}, this._updateToolbarFrequency/2);
+				return;
+
+			}else{
+				this._updateToolbarLastRan = new Date();
+			}
+			// end frequency checker
+
+			//IE has the habit of generating events even when this editor is blurred, prevent this
+			if(dojo.widget.Editor2Manager.getCurrentInstance() !== this){ return; }
+
+			this.toolbarWidget.update();
+		},
+
+		destroy: function(/*Boolean*/finalize){
+			this._htmlEditNode = null;
+			dojo.event.disconnect(this, "close", this.toolbarWidget, "hide");
+			if(!finalize){
+				this.toolbarWidget.destroy();
+			}
+			dojo.widget.Editor2.superclass.destroy.call(this);
+		},
+
+		onDisplayChanged: function(/*Object*/e){
+			dojo.widget.Editor2.superclass.onDisplayChanged.call(this,e);
+			this.updateToolbar();
+		},
+
+		onLoad: function(){
+			try{
+				dojo.widget.Editor2.superclass.onLoad.call(this);
+			}catch(e){ // FIXME: debug why this is throwing errors in IE!
+				dojo.debug(e);
+			}
+			this.editorOnLoad();
+		},
+
+		onFocus: function(){
+			dojo.widget.Editor2.superclass.onFocus.call(this);
+			this.setFocus();
+		},
+
+		//overload to support source editing mode
+		getEditorContent: function(){
+			if(this._inSourceMode){
+				this.replaceEditorContent(this._htmlEditNode.value);
+			}
+			return dojo.widget.Editor2.superclass.getEditorContent.call(this);
+		}
+		/*,
+		// FIXME: probably not needed any more with new design, but need to verify
+		_save: function(e){
+			// FIXME: how should this behave when there's a larger form in play?
+			if(!this.isClosed){
+				dojo.debug("save attempt");
+				if(this.saveUrl.length){
+					var content = {};
+					content[this.saveArgName] = this.getEditorContent();
+					dojo.io.bind({
+						method: this.saveMethod,
+						url: this.saveUrl,
+						content: content
+					});
+				}else{
+					dojo.debug("please set a saveUrl for the editor");
+				}
+				if(this.closeOnSave){
+					this.close(e.getName().toLowerCase() == "save");
+				}
+			}
+		}*/
+	},
+	"html"
+);
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Editor2.js
------------------------------------------------------------------------------
    svn:eol-style = native