jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From edgarp...@apache.org
Subject svn commit: r473755 [26/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/ ...
Date Sat, 11 Nov 2006 16:44:48 GMT
Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AccordionContainer.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AccordionContainer.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AccordionContainer.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AccordionContainer.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,269 @@
+dojo.provide("dojo.widget.AccordionContainer");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.lfx.html");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.widget.html.layout");
+dojo.require("dojo.widget.PageContainer");
+
+
+/**
+ *summary
+ *	Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
+ *	and switching between panes is visualized by sliding the other panes up/down.
+ *
+ * description
+ *	Front view (3 panes, pane #2 open)
+ *	------------------------
+ *	|:::Pane#1 title:::    |
+ * 	|:::Pane#2 title:::    |
+ *	|                      |
+ *	|    pane#2 contents   |
+ *	|                      |
+ *	|:::Pane#3 title:::    |
+ *	------------------------
+ *
+ *	Side view (showing implementation):
+ *
+ *         viewport    pane#3     pane#2     pane#1
+ *            =                                
+ *            |                                =
+ *            |                      =         |
+ *	front     |                      |         |
+ *            |                      |         =
+ *            |                      =
+ *            |          =
+ *            =          |
+ *                       |
+ *                       =
+ *
+ *	Panes are stacked by z-index like a stack of cards, so they can be slid correctly.
+ *	The panes on the bottom extend past the bottom of the viewport (but are hidden).
+ *
+ * usage
+ *	<div dojoType="AccordionContainer">
+ *		<div dojoType="ContentPane" label="pane 1">...</div>
+ *		...
+ *	</div>
+ *
+ * TODO:
+ *	* this widget should extend PageContainer
+ *	* call child.onShow(), child.onHide() so you can attach to those methods if you want
+ */
+ dojo.widget.defineWidget(
+	"dojo.widget.AccordionContainer",
+	dojo.widget.HtmlWidget,
+	{
+		isContainer: true,
+		
+		// String
+		//	CSS class name for dom node w/the title
+		labelNodeClass: "label",
+		
+		// String
+		//	CSS class name for dom node holding the content
+		containerNodeClass: "accBody",
+
+		// Integer
+		//	Amount of time (in ms) it takes to slide panes
+		duration: 250,
+
+		fillInTemplate: function(){
+			with(this.domNode.style){
+				// position must be either relative or absolute
+				if(position!="absolute"){
+					position="relative";
+				}
+				overflow="hidden";
+			}
+		},
+
+		addChild: function(/*Widget*/ widget){
+			var child = this._addChild(widget);
+			this._setSizes();
+			return child;	// Widget
+		},
+		
+		_addChild: function(/*Widget*/ widget){
+			// summary
+			//	Internal call to add child, used during postCreate() and by the real addChild() call
+			if(widget.open){
+				dojo.deprecated("open parameter deprecated, use 'selected=true' instead will be removed in ", "0.5");
+				dojo.debug(widget.widgetId + ": open == " + widget.open);
+				widget.selected=true;
+			}
+			if (widget.widgetType != "AccordionPane") {
+				var wrapper=dojo.widget.createWidget("AccordionPane",{label: widget.label, selected: widget.selected, labelNodeClass: this.labelNodeClass, containerNodeClass: this.containerNodeClass, allowCollapse: this.allowCollapse });
+				wrapper.addChild(widget);
+				this.addWidgetAsDirectChild(wrapper);
+				this.registerChild(wrapper, this.children.length);
+				return wrapper;	// Widget
+			} else {
+				dojo.html.addClass(widget.containerNode, this.containerNodeClass);
+				dojo.html.addClass(widget.labelNode, this.labelNodeClass);
+				this.addWidgetAsDirectChild(widget);
+				this.registerChild(widget, this.children.length);	
+				return widget;	// Widget
+			}
+		},
+	
+		postCreate: function() {
+			var tmpChildren = this.children;
+			this.children=[];
+			dojo.html.removeChildren(this.domNode);
+			dojo.lang.forEach(tmpChildren, dojo.lang.hitch(this,"_addChild"));
+			this._setSizes();
+		},
+	
+		removeChild: function(/*Widget*/ widget) {
+			dojo.widget.AccordionContainer.superclass.removeChild.call(this, widget);
+			this._setSizes();
+		},
+		
+		onResized: function(){
+			this._setSizes();
+		},
+
+		_setSizes: function() {
+			// summary
+			//	Set panes' size/position based on my size, and the current open node.
+
+			// get cumulative height of all the title bars, and figure out which pane is open
+			var totalCollapsedHeight = 0;
+			var openIdx = 0;
+			dojo.lang.forEach(this.children, function(child, idx){
+				totalCollapsedHeight += child.getLabelHeight();
+				if(child.selected){ openIdx=idx; }
+			});
+
+			// size and position each pane
+			var mySize=dojo.html.getContentBox(this.domNode);
+			var y = 0;
+			dojo.lang.forEach(this.children, function(child, idx){
+				var childCollapsedHeight = child.getLabelHeight();
+				child.resizeTo(mySize.width, mySize.height-totalCollapsedHeight+childCollapsedHeight);
+				child.domNode.style.zIndex=idx+1;
+				child.domNode.style.position="absolute";
+				child.domNode.style.top = y+"px";
+				y += (idx==openIdx) ? dojo.html.getBorderBox(child.domNode).height : childCollapsedHeight;
+			});
+		},
+
+		selectChild: function(/*Widget*/ page){
+			// summary
+			//	close the current page and select a new one
+			dojo.lang.forEach(this.children, function(child){child.setSelected(child==page);});
+
+			// slide each pane that needs to be moved
+			var y = 0;
+			var anims = [];
+			dojo.lang.forEach(this.children, function(child, idx){
+				if(child.domNode.style.top != (y+"px")){
+					anims.push(dojo.lfx.html.slideTo(child.domNode, {top: y, left: 0}, this.duration));
+				}
+				y += child.selected ? dojo.html.getBorderBox(child.domNode).height : child.getLabelHeight();
+			});
+			dojo.lfx.combine(anims).play();
+		}
+	}
+);
+
+/**
+ * summary
+ *	AccordionPane is a box with a title that contains another widget (often a ContentPane).
+ *	It's a widget used internally by AccordionContainer.
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.AccordionPane",
+	dojo.widget.HtmlWidget,
+{
+	// parameters
+
+	// String
+	//	label to print on top of AccordionPane
+	label: "",
+
+	// String
+	//	CSS class name for the AccordionPane's dom node
+	"class": "dojoAccordionPane",
+
+	// String
+	//	CSS class name for the AccordionPane's label node
+	labelNodeClass: "label",
+
+	// String
+	//	CSS class name for the AccordionPane's container node
+	containerNodeClass: "accBody",
+	
+	// Boolean
+	//	if true, this is the open pane
+	selected: false,
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/AccordionPane.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/AccordionPane.css"),
+
+	isContainer: true,
+
+    fillInTemplate: function() {
+    	dojo.html.addClass(this.domNode, this["class"]);
+		dojo.widget.AccordionPane.superclass.fillInTemplate.call(this);
+		dojo.html.disableSelection(this.labelNode);
+		this.setSelected(this.selected);
+	},
+
+	setLabel: function(/*String*/ label) {
+		// summary: set the  title of the node
+		this.labelNode.innerHTML=label;
+	},
+	
+	resizeTo: function(width, height){
+		dojo.html.setMarginBox(this.domNode, {width: width, height: height});
+		var children = [
+			{domNode: this.labelNode, layoutAlign: "top"},
+			{domNode: this.containerNode, layoutAlign: "client"}
+		];
+		dojo.widget.html.layout(this.domNode, children);
+		var childSize = dojo.html.getContentBox(this.containerNode);
+		this.children[0].resizeTo(childSize.width, childSize.height);
+	},
+
+	getLabelHeight: function() {
+		// summary: returns the height of the title dom node
+		return dojo.html.getMarginBox(this.labelNode).height;	// Integer
+	},
+
+	onLabelClick: function() {
+		// summary: callback when someone clicks my label
+		this.parent.selectChild(this);
+	},
+	
+	setSelected: function(/*Boolean*/ isSelected){
+		this.selected=isSelected;
+		(isSelected ? dojo.html.addClass : dojo.html.removeClass)(this.domNode, this["class"]+"-selected");
+
+		// make sure child is showing (lazy load), and also that onShow()/onHide() is called
+		var child = this.children[0];
+		if(child){
+			if(isSelected){
+				if(!child.isShowing()){
+					child.show();
+				}else{
+					child.onShow();
+				}
+			}else{
+				child.onHide();
+			}
+		}
+	}
+});
+
+// These arguments can be specified for the children of an AccordionContainer
+// Since any widget can be specified as a child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	// String
+	//	is this the selected child?
+	//	DEPRECATED: will be removed in 0.5.  Used "selected" attribute instead.
+	open: false
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AnimatedPng.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AnimatedPng.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AnimatedPng.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/AnimatedPng.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,88 @@
+dojo.provide("dojo.widget.AnimatedPng");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+// summary
+//	PNGs have great tranparency, but lack animation.
+//	This widget lets you point an img tag at an animated gif for graceful degrading,
+//	while letting you specify a png containing a grid of cells to animate between.
+//
+// usage
+//	<img dojoType="AnimatedPng"
+//		src="images/animatedpng_static.gif"		(for degradation; in case javascript is disabled)
+//		aniSrc="images/animatedpng_frames.gif"
+//		width="20"
+//		height="20"
+//		interval="50"
+//	/>
+//
+//	var params = {src: "images/animatedpng_static.gif", aniSrc: "images/animatedpng_frames.gif", width: 20, height: 20, interval: 50};
+//	var widget = dojo.widget.createWidget("AnimatedPng", params, document.getElementById("pngContainer"));
+//
+dojo.widget.defineWidget(
+	"dojo.widget.AnimatedPng",
+	dojo.widget.HtmlWidget,
+	{
+		isContainer: false,
+
+		// Integer
+		//	width (of each frame) in pixels
+		width: 0,
+		
+		// Integer
+		//	height (of each frame) in pixels
+		height: 0,
+		
+		// String
+		//	pathname to png file containing frames to be animated (ie, displayed sequentially)
+		aniSrc: '',
+		
+		// Integer
+		//	time to display each frame
+		interval: 100,
+
+		_blankSrc: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+
+		templateString: '<img class="dojoAnimatedPng" />',
+
+		postCreate: function(){
+			this.cellWidth = this.width;
+			this.cellHeight = this.height;
+
+			var img = new Image();
+			var self = this;
+
+			img.onload = function(){ self._initAni(img.width, img.height); };
+			img.src = this.aniSrc;
+		},
+
+		_initAni: function(w, h){
+			this.domNode.src = this._blankSrc;
+			this.domNode.width = this.cellWidth;
+			this.domNode.height = this.cellHeight;
+			this.domNode.style.backgroundImage = 'url('+this.aniSrc+')';
+			this.domNode.style.backgroundRepeat = 'no-repeat';
+
+			this.aniCols = Math.floor(w/this.cellWidth);
+			this.aniRows = Math.floor(h/this.cellHeight);
+			this.aniCells = this.aniCols * this.aniRows;
+			this.aniFrame = 0;
+
+			window.setInterval(dojo.lang.hitch(this, '_tick'), this.interval);
+		},
+
+		_tick: function(){
+			this.aniFrame++;
+			if (this.aniFrame == this.aniCells) this.aniFrame = 0;
+
+			var col = this.aniFrame % this.aniCols;
+			var row = Math.floor(this.aniFrame / this.aniCols);
+
+			var bx = -1 * col * this.cellWidth;
+			var by = -1 * row * this.cellHeight;
+
+			this.domNode.style.backgroundPosition = bx+'px '+by+'px';
+		}
+	}
+);

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Button.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Button.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Button.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Button.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,422 @@
+dojo.provide("dojo.widget.Button");
+
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.widget.*");
+
+/*
+ * summary
+ *	Basically the same thing as a normal HTML button, but with special styling.
+ * usage
+ *	<button dojoType="button" onClick="...">Hello world</button>
+ *
+ *  var button1 = dojo.widget.createWidget("Button", {caption: "hello world", onClick: foo});
+ *	document.body.appendChild(button1.domNode);
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.Button",
+	dojo.widget.HtmlWidget,
+	{
+		isContainer: true,
+
+		// String
+		//	text to display in button
+		caption: "",
+		
+		// Boolean
+		//	if true, cannot click button
+		disabled: false,
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.css"),
+		
+		// Url
+		//	prefix of filename holding images (left, center, right) for button in normal state
+		inactiveImg: "src/widget/templates/images/soriaButton-",
+		
+		// Url
+		//	prefix of filename holding images (left, center, right) for button when it's being hovered over
+		activeImg: "src/widget/templates/images/soriaActive-",
+
+		// Url
+		//	prefix of filename holding images (left, center, right) for button between mouse-down and mouse-up
+		pressedImg: "src/widget/templates/images/soriaPressed-",
+
+		// Url
+		//	prefix of filename holding images (left, center, right) for button when it's disabled (aka, grayed-out)
+		disabledImg: "src/widget/templates/images/soriaDisabled-",
+		
+		// Number
+		//	shape of the button's end pieces;
+		//	the height of the end pieces is a function of the button's height (which in turn is a function of the button's content),
+		//	and then the width of the end pieces is relative to their height.
+		width2height: 1.0/3.0,
+
+		fillInTemplate: function(){
+			if(this.caption){
+				this.containerNode.appendChild(document.createTextNode(this.caption));
+			}
+			dojo.html.disableSelection(this.containerNode);
+		},
+
+		postCreate: function(){
+			this._sizeMyself();
+		},
+	
+		_sizeMyself: function(){
+			// we cannot size correctly if any of our ancestors are hidden (display:none),
+			// so temporarily attach to document.body
+			if(this.domNode.parentNode){
+				var placeHolder = document.createElement("span");
+				dojo.html.insertBefore(placeHolder, this.domNode);
+			}
+			dojo.body().appendChild(this.domNode);
+			
+			this._sizeMyselfHelper();
+			
+			// Put this.domNode back where it was originally
+			if(placeHolder){
+				dojo.html.insertBefore(this.domNode, placeHolder);
+				dojo.html.removeNode(placeHolder);
+			}
+		},
+
+		_sizeMyselfHelper: function(){
+			var mb = dojo.html.getMarginBox(this.containerNode);
+			this.height = mb.height;
+			this.containerWidth = mb.width;
+			var endWidth= this.height * this.width2height;
+	
+			this.containerNode.style.left=endWidth+"px";
+	
+			this.leftImage.height = this.rightImage.height = this.centerImage.height = this.height;
+			this.leftImage.width = this.rightImage.width = endWidth+1;
+			this.centerImage.width = this.containerWidth;
+			this.centerImage.style.left=endWidth+"px";
+			this._setImage(this.disabled ? this.disabledImg : this.inactiveImg);
+
+			if ( this.disabled ) {
+				dojo.html.prependClass(this.domNode, "dojoButtonDisabled");
+				this.domNode.removeAttribute("tabIndex");
+				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", true);
+			} else {
+				dojo.html.removeClass(this.domNode, "dojoButtonDisabled");
+				this.domNode.setAttribute("tabIndex", "0");
+				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", false);
+			}
+				
+			this.domNode.style.height=this.height + "px";
+			this.domNode.style.width= (this.containerWidth+2*endWidth) + "px";
+		},
+	
+		onMouseOver: function(/*Event*/ e){
+			// summary: callback when user mouses-over the button
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.buttonNode, "dojoButtonHover");
+			this._setImage(this.activeImg);
+		},
+	
+		onMouseDown: function(/*Event*/ e){
+			// summary: callback when user starts to click the button
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.buttonNode, "dojoButtonDepressed");
+			dojo.html.removeClass(this.buttonNode, "dojoButtonHover");
+			this._setImage(this.pressedImg);
+		},
+
+		onMouseUp: function(/*Event*/ e){
+			// summary: callback when the user finishes clicking
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.buttonNode, "dojoButtonHover");
+			dojo.html.removeClass(this.buttonNode, "dojoButtonDepressed");
+			this._setImage(this.activeImg);
+		},
+	
+		onMouseOut: function(/*Event*/ e){
+			// summary: callback when the user moves the mouse off the button
+			if( this.disabled ){ return; }
+			if( e.toElement && dojo.html.isDescendantOf(e.toElement, this.buttonNode) ){
+				return; // Ignore IE mouseOut events that dont actually leave button - Prevents hover image flicker in IE
+			}
+			dojo.html.removeClass(this.buttonNode, "dojoButtonHover");
+			this._setImage(this.inactiveImg);
+		},
+
+		onKey: function(/*Event*/ e){
+			// summary: callback when the user presses a key (on key-down)
+			if (!e.key) { return; }
+			var menu = dojo.widget.getWidgetById(this.menuId);
+			if (e.key == e.KEY_ENTER || e.key == " "){
+				this.onMouseDown(e);
+				this.buttonClick(e);
+				dojo.lang.setTimeout(this, "onMouseUp", 75, e);
+				dojo.event.browser.stopEvent(e);
+			}
+			if(menu && menu.isShowingNow && e.key == e.KEY_DOWN_ARROW){
+				// disconnect onBlur when focus moves into menu
+				dojo.event.disconnect(this.domNode, "onblur", this, "onBlur");
+				// allow event to propagate to menu
+			}
+		},
+
+		onFocus: function(/*Event*/ e){
+			// summary: callback on focus to the button
+			var menu = dojo.widget.getWidgetById(this.menuId);
+			if (menu){
+				dojo.event.connectOnce(this.domNode, "onblur", this, "onBlur");
+			}
+		},
+
+		onBlur: function(/*Event*/ e){
+			// summary: callback when button loses focus
+			var menu = dojo.widget.getWidgetById(this.menuId);
+			if ( !menu ) { return; }
+	
+			if ( menu.close && menu.isShowingNow ){
+				menu.close();
+			}
+		},
+
+		buttonClick: function(/*Event*/ e){
+			// summary: internal function for handling button clicks
+			if(!this.disabled){ 
+				// focus may fail when tabIndex is not supported on div's
+				// by the browser, or when the node is disabled
+				try { this.domNode.focus(); } catch(e2) {};
+				this.onClick(e); 
+			}
+		},
+
+		onClick: function(/*Event*/ e) {
+			// summary: callback for when button is clicked; user can override this function
+		},
+
+		_setImage: function(/*String*/ prefix){
+			this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif");
+			this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif");
+			this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif");
+		},
+		
+		_toggleMenu: function(/*String*/ menuId){
+			var menu = dojo.widget.getWidgetById(menuId); 
+			if ( !menu ) { return; }
+			if ( menu.open && !menu.isShowingNow) {
+				var pos = dojo.html.getAbsolutePosition(this.domNode, false);
+				menu.open(pos.x, pos.y+this.height, this);
+			} else if ( menu.close && menu.isShowingNow ){
+				menu.close();
+			} else {
+				menu.toggle();
+			}
+		},
+		
+		setCaption: function(/*String*/ content){
+			// summary: reset the caption (text) of the button; takes an HTML string
+			this.caption=content;
+			this.containerNode.innerHTML=content;
+			this._sizeMyself();
+		},
+		
+		setDisabled: function(/*Boolean*/ disabled){
+			// summary: set disabled state of button
+			this.disabled=disabled;
+			this._sizeMyself();
+		}
+	});
+
+/*
+ * summary
+ *	push the button and a menu shows up
+ * usage
+ *	<button dojoType="DropDownButton" menuId="mymenu">Hello world</button>
+ *
+ *  var button1 = dojo.widget.createWidget("DropDownButton", {caption: "hello world", menuId: foo});
+ *	document.body.appendChild(button1.domNode);
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.DropDownButton",
+	dojo.widget.Button,
+	{
+		// String
+		//	widget id of the menu that this button should activate
+		menuId: "",
+
+		// Url
+		//	path of arrow image to display to the right of the button text
+		downArrow: "src/widget/templates/images/whiteDownArrow.gif",
+
+		// Url
+		//	path of arrow image to display to the right of the button text, when the button is disabled
+		disabledDownArrow: "src/widget/templates/images/whiteDownArrow.gif",
+	
+		fillInTemplate: function(){
+			dojo.widget.DropDownButton.superclass.fillInTemplate.apply(this, arguments);
+	
+			this.arrow = document.createElement("img");
+			dojo.html.setClass(this.arrow, "downArrow");
+
+			dojo.widget.wai.setAttr(this.domNode, "waiState", "haspopup", this.menuId);
+		},
+
+		_sizeMyselfHelper: function(){
+			// draw the arrow (todo: why is the arror in containerNode rather than outside it?)
+			this.arrow.src = dojo.uri.dojoUri(this.disabled ? this.disabledDownArrow : this.downArrow);
+			this.containerNode.appendChild(this.arrow);
+
+			dojo.widget.DropDownButton.superclass._sizeMyselfHelper.call(this);
+		},
+
+		onClick: function(/*Event*/ e){
+			// summary: callback when button is clicked; user shouldn't override this function or else the menu won't toggle
+			this._toggleMenu(this.menuId);
+		}
+	});
+
+/*
+ * summary
+ *	left side is normal button, right side displays menu
+ * usage
+ *	<button dojoType="ComboButton" onClick="..." menuId="mymenu">Hello world</button>
+ *
+ *  var button1 = dojo.widget.createWidget("DropDownButton", {caption: "hello world", onClick: foo, menuId: "myMenu"});
+ *	document.body.appendChild(button1.domNode);
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.ComboButton",
+	dojo.widget.Button,
+	{
+		// String
+		//	widget id of the menu that this button should activate
+		menuId: "",
+	
+		templatePath: dojo.uri.dojoUri("src/widget/templates/ComboButtonTemplate.html"),
+	
+		// Integer
+		//	# of pixels between left & right part of button
+		splitWidth: 2,
+		
+		// Integer
+		//	width of segment holding down arrow
+		arrowWidth: 5,
+	
+		_sizeMyselfHelper: function(/*Event*/ e){
+			var mb = dojo.html.getMarginBox(this.containerNode);
+			this.height = mb.height;
+			this.containerWidth = mb.width;
+
+			var endWidth= this.height/3;
+
+			if(this.disabled){
+				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", true);
+				this.domNode.removeAttribute("tabIndex");
+			}
+			else {
+				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", false);
+				this.domNode.setAttribute("tabIndex", "0");
+			}
+	
+			// left part
+			this.leftImage.height = this.rightImage.height = this.centerImage.height = 
+				this.arrowBackgroundImage.height = this.height;
+			this.leftImage.width = endWidth+1;
+			this.centerImage.width = this.containerWidth;
+			this.buttonNode.style.height = this.height + "px";
+			this.buttonNode.style.width = endWidth + this.containerWidth + "px";
+			this._setImage(this.disabled ? this.disabledImg : this.inactiveImg);
+
+			// right part
+			this.arrowBackgroundImage.width=this.arrowWidth;
+			this.rightImage.width = endWidth+1;
+			this.rightPart.style.height = this.height + "px";
+			this.rightPart.style.width = this.arrowWidth + endWidth + "px";
+			this._setImageR(this.disabled ? this.disabledImg : this.inactiveImg);
+	
+			// outer container
+			this.domNode.style.height=this.height + "px";
+			var totalWidth = this.containerWidth+this.splitWidth+this.arrowWidth+2*endWidth;
+			this.domNode.style.width= totalWidth + "px";
+		},
+	
+		_setImage: function(prefix){
+			this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif");
+			this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif");
+		},
+	
+		/*** functions on right part of button ***/
+		rightOver: function(/*Event*/ e){
+			// summary:
+			//	callback when mouse-over right part of button;
+			//	onMouseOver() is the callback for the left side of the button.
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.rightPart, "dojoButtonHover");
+			this._setImageR(this.activeImg);
+		},
+	
+		rightDown: function(/*Event*/ e){
+			// summary:
+			//	callback when mouse-down right part of button;
+			//	onMouseDown() is the callback for the left side of the button.
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.rightPart, "dojoButtonDepressed");
+			dojo.html.removeClass(this.rightPart, "dojoButtonHover");
+			this._setImageR(this.pressedImg);
+		},
+
+		rightUp: function(/*Event*/ e){
+			// summary:
+			//	callback when mouse-up right part of button;
+			//	onMouseUp() is the callback for the left side of the button.
+			if( this.disabled ){ return; }
+			dojo.html.prependClass(this.rightPart, "dojoButtonHover");
+			dojo.html.removeClass(this.rightPart, "dojoButtonDepressed");
+			this._setImageR(this.activeImg);
+		},
+	
+		rightOut: function(/*Event*/ e){
+			// summary:
+			//	callback when moving the mouse off of the right part of button;
+			//	onMouseOut() is the callback for the left side of the button.
+			if( this.disabled ){ return; }
+			dojo.html.removeClass(this.rightPart, "dojoButtonHover");
+			this._setImageR(this.inactiveImg);
+		},
+
+		rightClick: function(/*Event*/ e){
+			// summary:
+			//	callback when clicking the right part of button;
+			//	onClick() is the callback for the left side of the button.
+			if( this.disabled ){ return; }
+			// focus may fail when tabIndex is not supported on div's
+			// by the browser, or when the node is disabled
+			try { this.domNode.focus(); } catch(e2) {};
+			this._toggleMenu(this.menuId);
+		},
+	
+		_setImageR: function(prefix){
+			this.arrowBackgroundImage.src=dojo.uri.dojoUri(prefix + "c.gif");
+			this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif");
+		},
+
+		/*** keyboard functions ***/
+		
+		onKey: function(/*Event*/ e){
+			if (!e.key) { return; }
+			var menu = dojo.widget.getWidgetById(this.menuId);
+			if(e.key== e.KEY_ENTER || e.key == " "){
+				this.onMouseDown(e);
+				this.buttonClick(e);
+				dojo.lang.setTimeout(this, "onMouseUp", 75, e);
+				dojo.event.browser.stopEvent(e);
+			} else if (e.key == e.KEY_DOWN_ARROW && e.altKey){
+				this.rightDown(e);
+				this.rightClick(e);
+				dojo.lang.setTimeout(this, "rightUp", 75, e);
+				dojo.event.browser.stopEvent(e);
+			} else if(menu && menu.isShowingNow && e.key == e.KEY_DOWN_ARROW){
+				// disconnect onBlur when focus moves into menu
+				dojo.event.disconnect(this.domNode, "onblur", this, "onBlur");
+				// allow event to propagate to menu
+			}
+		}
+	});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Chart.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Chart.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Chart.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Chart.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,247 @@
+dojo.provide("dojo.widget.Chart");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.gfx.color");
+dojo.require("dojo.gfx.color.hsl");
+
+// Base class for svg and vml implementations of Chart
+dojo.declare(
+	"dojo.widget.Chart",
+	null,
+	function(){
+		this.series = [];
+	},
+{
+	isContainer: false,
+
+	assignColors: function(){
+		//	summary
+		//	Assigns/generates a color for a data series.
+		var hue=30;
+		var sat=120;
+		var lum=120;
+		var steps = Math.round(330/this.series.length);
+
+		for(var i=0; i<this.series.length; i++){
+			var c=dojo.gfx.color.hsl2rgb(hue,sat,lum);
+			if(!this.series[i].color){
+				this.series[i].color = dojo.gfx.color.rgb2hex(c[0],c[1],c[2]);
+			}
+			hue += steps;
+		}
+	},
+	parseData: function(table){
+		var thead=table.getElementsByTagName("thead")[0];
+		var tbody=table.getElementsByTagName("tbody")[0];
+		if(!(thead&&tbody)) dojo.raise("dojo.widget.Chart: supplied table must define a head and a body.");
+
+		//	set up the series.
+		var columns=thead.getElementsByTagName("tr")[0].getElementsByTagName("th");	//	should be <tr><..>
+		
+		//	assume column 0 == X
+		for (var i=1; i<columns.length; i++){
+			var key="column"+i;
+			var label=columns[i].innerHTML;
+			var plotType=columns[i].getAttribute("plotType")||"line";
+			var color=columns[i].getAttribute("color");
+			var ds=new dojo.widget.Chart.DataSeries(key,label,plotType,color);
+			this.series.push(ds);
+		}
+
+		//	ok, get the values.
+		var rows=tbody.rows;
+		var xMin=Number.MAX_VALUE,xMax=Number.MIN_VALUE;
+		var yMin=Number.MAX_VALUE,yMax=Number.MIN_VALUE;
+		var ignore = [
+			"accesskey","align","bgcolor","class",
+			"colspan","height","id","nowrap",
+			"rowspan","style","tabindex","title",
+			"valign","width"
+		];
+
+		for(var i=0; i<rows.length; i++){
+			var row=rows[i];
+			var cells=row.cells;
+			var x=Number.MIN_VALUE;
+			for (var j=0; j<cells.length; j++){
+				if (j==0){
+					x=parseFloat(cells[j].innerHTML);
+					xMin=Math.min(xMin, x);
+					xMax=Math.max(xMax, x);
+				} else {
+					var ds=this.series[j-1];
+					var y=parseFloat(cells[j].innerHTML);
+					yMin=Math.min(yMin,y);
+					yMax=Math.max(yMax,y);
+					var o={x:x, value:y};
+					var attrs=cells[j].attributes;
+					for(var k=0; k<attrs.length; k++){
+						var attr=attrs.item(k);
+						var bIgnore=false;
+						for (var l=0; l<ignore.length; l++){
+							if (attr.nodeName.toLowerCase()==ignore[l]){
+								bIgnore=true;
+								break;
+							}
+						}
+						if(!bIgnore) o[attr.nodeName]=attr.nodeValue;
+					}
+					ds.add(o);
+				}
+			}
+		}
+		return { x:{ min:xMin, max:xMax}, y:{ min:yMin, max:yMax} };
+	}
+});
+
+/*
+ *	Every chart has a set of data series; this is the series.  Note that each
+ *	member of value is an object and in the minimum has 2 properties: .x and
+ *	.value.
+ */
+dojo.declare(
+	"dojo.widget.Chart.DataSeries",
+	null,
+	function(key, label, plotType, color){
+		this.id = "DataSeries"+dojo.widget.Chart.DataSeries.count++;
+		this.key = key;
+		this.label = label||this.id;
+		this.plotType = plotType||"line";	//	let line be the default.
+		this.color = color;
+		this.values = [];
+	},
+{
+	add: function(v){
+		if(v.x==null||v.value==null){
+			dojo.raise("dojo.widget.Chart.DataSeries.add: v must have both an 'x' and 'value' property.");
+		}
+		this.values.push(v);
+	},
+
+	clear: function(){
+		this.values=[];
+	},
+
+	createRange: function(len){
+		var idx = this.values.length-1;
+		var length = (len||this.values.length);
+		return { "index": idx, "length": length, "start":Math.max(idx-length,0) };
+	},
+
+	//	trend values
+	getMean: function(len){
+		var range = this.createRange(len);
+		if(range.index<0){ return 0; }
+		var t = 0;
+		var c = 0;
+		for(var i=range.index; i>=range.start; i--){
+			var n = parseFloat(this.values[i].value);
+			if(!isNaN(n)){ t += n; c++; }
+		}
+		t /= Math.max(c,1);
+		return t;
+	},
+
+	getMovingAverage: function(len){
+		var range = this.createRange(len);
+		if(range.index<0){ return 0; }
+		var t = 0;
+		var c = 0;
+		for(var i=range.index; i>=range.start; i--){
+			var n = parseFloat(this.values[i].value);
+			if(!isNaN(n)){ t += n; c++; }
+		}
+		t /= Math.max(c,1);
+		return t;
+	},
+
+	getVariance: function(len){
+		var range = this.createRange(len);
+		if(range.index < 0){ return 0; }
+		var t = 0; // FIXME: for tom: wtf are t, c, and s?
+		var s = 0;
+		var c = 0;
+		for(var i=range.index; i>=range.start; i--){
+			var n = parseFloat(this.values[i].value);
+			if(!isNaN(n)){
+				t += n;
+				s += Math.pow(n,2);
+				c++;
+			}
+		}
+		return (s/c)-Math.pow(t/c,2);
+	},
+
+	getStandardDeviation: function(len){
+		return Math.sqrt(this.getVariance(len));
+	},
+
+	getMax: function(len){
+		var range = this.createRange(len);
+		if(range.index < 0){ return 0; }
+		var t = 0;
+		for (var i=range.index; i>=range.start; i--){
+			var n=parseFloat(this.values[i].value);
+			if (!isNaN(n)){
+				t=Math.max(n,t);
+			}
+		}
+		return t;
+	},
+
+	getMin: function(len){
+		var range=this.createRange(len);
+		if(range.index < 0){ return 0; }
+		var t = 0;
+		for(var i=range.index; i>=range.start; i--){
+			var n = parseFloat(this.values[i].value);
+			if(!isNaN(n)){
+				t=Math.min(n,t);
+			}
+		}
+		return t;
+	},
+
+	getMedian: function(len){
+		var range = this.createRange(len);
+
+		if(range.index<0){ return 0; }
+
+		var a = [];
+		for (var i=range.index; i>=range.start; i--){
+			var n=parseFloat(this.values[i].value);
+			if (!isNaN(n)){
+				var b=false;
+				for(var j=0; j<a.length&&!b; j++){
+					if (n==a[j]) b=true; 
+				}
+				if(!b){ a.push(n); }
+			}
+		}
+		a.sort();
+		if(a.length>0){ return a[Math.ceil(a.length/2)]; }
+		return 0;
+	},
+
+	getMode: function(len){
+		var range=this.createRange(len);
+		if(range.index<0){ return 0; }
+		var o = {};
+		var ret = 0
+		var m = 0;
+		for(var i=range.index; i>=range.start; i--){
+			var n=parseFloat(this.values[i].value);
+			if(!isNaN(n)){
+				if (!o[this.values[i].value]) o[this.values[i].value] = 1;
+				else o[this.values[i].value]++;
+			}
+		}
+		for(var p in o){
+			if(m<o[p]){ m=o[p]; ret=p; }
+		}
+		return parseFloat(ret);
+	}
+});
+
+dojo["requireIf"](dojo.render.svg.capable, "dojo.widget.svg.Chart");
+dojo["requireIf"](!dojo.render.svg.capable && dojo.render.vml.capable, "dojo.widget.vml.Chart");

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Checkbox.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Checkbox.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Checkbox.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Checkbox.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,172 @@
+dojo.provide("dojo.widget.Checkbox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+
+// summary
+//	Same as an HTML checkbox, but with fancy styling
+dojo.widget.defineWidget(
+	"dojo.widget.Checkbox",
+	dojo.widget.HtmlWidget,
+	{
+		templatePath: dojo.uri.dojoUri('src/widget/templates/Checkbox.html'),
+		templateCssPath: dojo.uri.dojoUri('src/widget/templates/Checkbox.css'),
+
+		// Boolean
+		//	if true, clicking will not change the state of the checkbox.
+		//	in markup, this is specified as "disabled='disabled'", or just "disabled", 
+		disabled: false,
+		
+		// String
+		//	name used when submitting form; same as "name" attribute or plain HTML elements
+		name: "",
+
+		// String
+		//	id attached to the checkbox, used when submitting form
+		id: "",
+
+		// Boolean
+		//	if true, checkbox is initially marked turned on;
+		//	in markup, specified as "checked='checked'" or just "checked"
+		checked: false,
+		
+		// Integer
+		//	order fields are traversed when user hits the tab key
+		tabIndex: "",
+
+		// Value
+		//	equivalent to value field on normal checkbox (if checked, the value is passed as
+		//	the value when form is submitted)
+		value: "on",
+
+		postMixInProperties: function(){
+			dojo.widget.Checkbox.superclass.postMixInProperties.apply(this, arguments);
+			
+			// set tabIndex="0" because if tabIndex=="" user won't be able to tab to the field
+			if(!this.disabled && this.tabIndex==""){ this.tabIndex="0"; }
+		},
+
+		fillInTemplate: function(){
+			this._setInfo();
+		},
+
+		postCreate: function(){
+			// find any associated label and create a labelled-by relationship
+			// assumes <label for="inputId">label text </label> rather than
+			// <label><input type="xyzzy">label text</label>
+			var notcon = true;
+			this.id = this.id !="" ? this.id : this.widgetId;
+			if(this.id != ""){
+				var labels = document.getElementsByTagName("label");
+				if (labels != null && labels.length > 0){
+					for(var i=0; i<labels.length; i++){
+						if (labels[i].htmlFor == this.id){
+							labels[i].id = (labels[i].htmlFor + "label");
+							this._connectEvents(labels[i]);
+							dojo.widget.wai.setAttr(this.domNode, "waiState", "labelledby", labels[i].id);
+							break;
+						}
+					}
+				}
+			}
+			this._connectEvents(this.domNode);
+			// this is needed here for IE
+			this.inputNode.checked=this.checked;
+		},
+
+		_connectEvents: function(/*DomNode*/ node){
+			dojo.event.connect(node, "onmouseover", this, "mouseOver");
+			dojo.event.connect(node, "onmouseout", this, "mouseOut");
+			dojo.event.connect(node, "onkey", this, "onKey");
+			dojo.event.connect(node, "onclick", this, "_onClick");
+			dojo.html.disableSelection(node);
+		},
+
+		_onClick: function(/*Event*/ e){
+			if(this.disabled == false){
+				this.checked = !this.checked;
+				this._setInfo();
+			}
+			e.preventDefault();
+			e.stopPropagation();
+			this.onClick();
+		},
+
+		onClick: function(){
+			// summary: user overridable callback function for checkbox being clicked
+		},
+
+		onKey: function(/*Event*/ e){
+			// summary: callback when user hits a key
+			var k = dojo.event.browser.keys;
+			if(e.key == " "){
+	 			this._onClick(e);
+	 		}
+		},
+
+		mouseOver: function(/*Event*/ e){
+			// summary: callback when user moves mouse over checkbox
+			this._hover(e, true);
+		},
+
+		mouseOut: function(/*Event*/ e){
+			// summary: callback when user moves mouse off of checkbox
+			this._hover(e, false);
+		},
+
+		_hover: function(/*Event*/ e, /*Boolean*/ isOver){
+			if (this.disabled == false){
+				var state = this.checked ? "On" : "Off";
+				var style = "dojoHtmlCheckbox" + state + "Hover";
+				if (isOver){
+					dojo.html.addClass(this.imageNode, style);
+				}else{
+					dojo.html.removeClass(this.imageNode,style);
+				}
+			}
+		},
+
+		_setInfo: function(){
+			// summary:
+			//	set state of hidden checkbox node to correspond to displayed value.
+			//	also set CSS class string according to checked/unchecked and disabled/enabled state
+			var state = "dojoHtmlCheckbox" + (this.disabled ? "Disabled" : "") + (this.checked ? "On" : "Off");
+			dojo.html.setClass(this.imageNode, "dojoHtmlCheckbox " + state);
+			this.inputNode.checked = this.checked;
+			if(this.disabled){
+				this.inputNode.setAttribute("disabled",true);
+			}else{
+				this.inputNode.removeAttribute("disabled");
+			}
+			dojo.widget.wai.setAttr(this.domNode, "waiState", "checked", this.checked);
+		}
+	}
+);
+
+// summary
+//	variation on Checkbox widget to be display on monitors in high-contrast mode (that don't display CSS background images)
+dojo.widget.defineWidget(
+	"dojo.widget.a11y.Checkbox",
+	dojo.widget.Checkbox,
+	{
+		templatePath: dojo.uri.dojoUri('src/widget/templates/CheckboxA11y.html'),
+
+		fillInTemplate: function(){
+		},
+
+		postCreate: function(args, frag){
+			this.inputNode.checked=this.checked;
+			//only set disabled if true since FF interprets any value for disabled as true
+			if (this.disabled){
+				this.inputNode.setAttribute("disabled",true);
+			} 
+		},
+
+		_onClick: function(){
+			this.onClick();
+		}
+	}
+);
\ No newline at end of file

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Clock.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Clock.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Clock.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Clock.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,201 @@
+dojo.provide("dojo.widget.Clock");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.gfx.*");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.timing.Timer");
+
+dojo.widget.defineWidget(
+	"dojo.widget.Clock",
+	dojo.widget.HtmlWidget,
+	function(){
+		var self=this;
+		this.timeZoneOffset=0;	//	this is fun.
+		this.label="";		//	optional label.
+		
+		this.date=new Date();
+		
+		this.handColor="#788598";
+		this.handStroke="#6f7b8c";
+	//	this.secondHandColor="#c90405";
+		this.secondHandColor=[201, 4, 5, 0.8];
+		this.topLabelColor="#efefef";
+		this.labelColor="#fff";
+
+		//	timer
+		this.timer = new dojo.lang.timing.Timer(1000);
+
+		//	shapes
+		this.center={ x:75, y:75 };
+		this.hands={
+			hour:null,
+			minute:null,
+			second:null
+		};
+		this.shadows={
+			hour:{ shadow:null, shift:{ dx:2, dy:2} },
+			minute:{ shadow:null, shift:{ dx:2, dy:3} },
+			second:{ shadow:null, shift:{ dx:4, dy:4} }
+		};
+		this.image = dojo.uri.dojoUri("src/widget/templates/images/clock.png");
+		this.surface=null;
+		this.labelNode=null;
+		this.topLabelNode=null;
+
+		this.draw=function(){
+			self.date=new Date();
+			var h=(self.date.getHours()+self.timeZoneOffset) % 12;
+			var m=self.date.getMinutes();
+			var s=self.date.getSeconds();
+
+			self.placeHour(h, m, s);
+			self.placeMinute(m, s);
+			self.placeSecond(s);
+
+			self.topLabelNode.innerHTML=((self.date.getHours()+self.timeZoneOffset)>11)?"PM":"AM";
+		};
+
+		this.timer.onTick=self.draw;
+	},
+	{
+		set:function(/* Date */dt){
+			this.date=dt;
+			if(!this.timer.isRunning){
+				this.draw();
+			}
+		},
+		start:function(){ this.timer.start(); },
+		stop:function(){ this.timer.stop(); },
+
+		_initPoly:function(parent, points){
+			var path = parent.createPath();
+			var first = true;
+			dojo.lang.forEach(points, function(c){
+				if(first){
+					path.moveTo(c.x, c.y);
+					first=false;
+				} else {
+					path.lineTo(c.x, c.y);
+				}
+			});
+			return path;
+		},
+		_placeHand:function(shape, angle, shift){
+			var move = { dx:this.center.x + (shift?shift.dx:0), dy:this.center.y+(shift?shift.dy:0) };
+			return shape.setTransform([move, dojo.gfx.matrix.rotateg(-angle)]);
+		},
+		placeHour:function(h, m, s){
+			var angle=30 *(h + m/60 + s/3600);
+			this._placeHand(this.hands.hour, angle);
+			this._placeHand(this.shadows.hour.shadow, angle, this.shadows.hour.shift);
+		},
+		placeMinute:function(m, s){
+			var angle=6 * (m + s/60);
+			this._placeHand(this.hands.minute, angle);
+			this._placeHand(this.shadows.minute.shadow, angle, this.shadows.minute.shift);
+		},
+		placeSecond:function(s){
+			var angle=6 * s;
+			this._placeHand(this.hands.second, angle);
+			this._placeHand(this.shadows.second.shadow, angle, this.shadows.second.shift);
+		},
+		
+		init:function(){
+			//	start by setting up the domNode
+			if(this.domNode.style.position != "absolute"){
+				this.domNode.style.position = "relative";
+			}
+
+			//	clean out any children
+			while(this.domNode.childNodes.length>0){
+				this.domNode.removeChild(this.domNode.childNodes[0]);
+			}
+			
+			//	set ourselves up.
+			this.domNode.style.width="150px";
+			this.domNode.style.height="150px";
+
+			this.surface=dojo.gfx.createSurface(this.domNode, 150, 150);
+			this.surface.createRect({width: 150, height: 150});
+			this.surface.createImage({width: 150, height: 150, src: this.image+""});
+			
+			var hP=[ {x: -3, y: -4}, {x: 3, y: -4}, {x: 1, y: -27}, { x:-1, y:-27}, {x: -3, y: -4} ];
+			var mP=[ {x: -3, y: -4}, {x: 3, y: -4}, {x: 1, y: -38}, {x:-1, y:-38}, {x: -3, y: -4} ];
+			var sP=[ {x: -2, y: -2}, {x: 2, y: -2}, {x: 1, y: -45}, {x: -1, y: -45}, {x: -2, y: -2} ];
+			
+			this.shadows.hour.shadow = this._initPoly(this.surface, hP)
+				.setFill([0, 0, 0, 0.1]);
+			this.hands.hour = this._initPoly(this.surface, hP)
+				.setStroke({color: this.handStroke, width:1 })
+				.setFill({ 
+					type:"linear", 
+					x1:0, y1:0, x2:0, y2:-27, 
+					colors:[{offset:0, color:"#fff"}, {offset:0.33, color:this.handColor}]
+				});
+			this.shadows.minute.shadow = this._initPoly(this.surface, mP)
+				.setFill([0, 0, 0, 0.1]);
+			this.hands.minute = this._initPoly(this.surface, mP)
+				.setStroke({color: this.handStroke, width:1 })
+				.setFill({ 
+					type:"linear", 
+					x1:0, y1:0, x2:0, y2:-38, 
+					colors:[{offset:0, color:"#fff"}, {offset:0.33, color:this.handColor}]
+				});
+
+			this.surface.createCircle({r: 6})
+				.setStroke({color: this.handStroke, width:2 })
+				.setFill("#fff")
+				.setTransform({dx: 75, dy: 75});
+
+			this.shadows.second.shadow = this._initPoly(this.surface, sP)
+				.setFill([0, 0, 0, 0.1]);
+			this.hands.second = this._initPoly(this.surface, sP)
+				.setFill(this.secondHandColor);
+
+			//	clock centers, doesn't move.
+			this.surface.createCircle({r: 4})
+				.setFill(this.secondHandColor)
+				.setTransform({dx: 75, dy: 75});
+
+			//	labels
+			this.topLabelNode=document.createElement("div");
+			with(this.topLabelNode.style){
+				position="absolute";
+				top="3px";
+				left="0px";
+				color=this.topLabelColor;
+				textAlign="center";
+				width="150px";
+				fontFamily="sans-serif";
+				fontSize="11px";
+				textTransform="uppercase";
+				fontWeight="bold";
+			}
+			this.topLabelNode.innerHTML=((this.date.getHours()+this.timeZoneOffset)>11)?"PM":"AM";
+			this.domNode.appendChild(this.topLabelNode);
+
+			this.labelNode=document.createElement("div");
+			with(this.labelNode.style){
+				position="absolute";
+				top="134px";
+				left="0px";
+				color=this.labelColor;
+				textAlign="center";
+				width="150px";
+				fontFamily="sans-serif";
+				fontSize="10px";
+				textTransform="uppercase";
+				fontWeight="bold";
+			}
+			this.labelNode.innerHTML=this.label||"&nbsp;";
+			this.domNode.appendChild(this.labelNode);
+			
+			this.draw();
+		},
+		postCreate:function(){
+			this.init();
+			this.start();
+		}
+	}
+);

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ColorPalette.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ColorPalette.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ColorPalette.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ColorPalette.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,113 @@
+dojo.provide("dojo.widget.ColorPalette");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.selection");
+
+dojo.widget.defineWidget(
+	"dojo.widget.ColorPalette",
+	dojo.widget.HtmlWidget,
+{
+	palette: "7x10",
+
+	bgIframe: null,
+
+	palettes: {
+		"7x10": [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"],
+			["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"],
+			["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"],
+			["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"],
+			["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"],
+			["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"],
+			["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]],
+
+		"3x4": [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/],
+			["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/],
+			["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]]
+			//["00ffff"/*aqua*/, "808000"/*olive*/, "800000"/*maroon*/, "008080"/*teal*/]];
+	},
+
+	buildRendering: function () {
+		this.domNode = document.createElement("table");
+//		dojo.body().appendChild(this.domNode);
+		dojo.html.disableSelection(this.domNode);
+		dojo.event.connect(this.domNode, "onmousedown", function (e) {
+			e.preventDefault();
+		});
+		with (this.domNode) { // set the table's properties
+			cellPadding = "0"; cellSpacing = "1"; border = "1";
+			style.backgroundColor = "white"; //style.position = "absolute";
+		}
+		var colors = this.palettes[this.palette];
+		for (var i = 0; i < colors.length; i++) {
+			var tr = this.domNode.insertRow(-1);
+			for (var j = 0; j < colors[i].length; j++) {
+				if (colors[i][j].length == 3) {
+					colors[i][j] = colors[i][j].replace(/(.)(.)(.)/, "$1$1$2$2$3$3");
+				}
+
+				var td = tr.insertCell(-1);
+				with (td.style) {
+					backgroundColor = "#" + colors[i][j];
+					border = "1px solid gray";
+					width = height = "15px";
+					fontSize = "1px";
+				}
+
+				td.color = "#" + colors[i][j];
+
+				td.onmouseover = function (e) { this.style.borderColor = "white"; }
+				td.onmouseout = function (e) { this.style.borderColor = "gray"; }
+				dojo.event.connect(td, "onmousedown", this, "click");
+
+				td.innerHTML = "&nbsp;";
+			}
+		}
+
+		if(dojo.render.html.ie){
+			this.bgIframe = document.createElement("<iframe frameborder='0' src='javascript:void(0);'>");
+			with(this.bgIframe.style){
+				position = "absolute";
+				left = top = "0px";
+				display = "none";
+			}
+			dojo.body().appendChild(this.bgIframe);
+			dojo.html.setOpacity(this.bgIframe, 0);
+		}
+	},
+
+	click: function (e) {
+		this.onColorSelect(e.currentTarget.color);
+		e.currentTarget.style.borderColor = "gray";
+	},
+
+	onColorSelect: function (color) { },
+
+	hide: function (){
+		this.domNode.parentNode.removeChild(this.domNode);
+		if(this.bgIframe){
+			this.bgIframe.style.display = "none";
+		}
+	},
+
+	showAt: function (x, y) {
+		with(this.domNode.style){
+			top = y + "px";
+			left = x + "px";
+			zIndex = 999;
+		}
+		dojo.body().appendChild(this.domNode);
+		if(this.bgIframe){
+			with(this.bgIframe.style){
+				display = "block";
+				top = y + "px";
+				left = x + "px";
+				zIndex = 998;
+				var s = dojo.html.getMarginBox(this.domNode);
+				width = s.width + "px";
+				height = s.height + "px";
+			}
+
+		}
+	}
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ComboBox.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ComboBox.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ComboBox.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ComboBox.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,826 @@
+dojo.provide("dojo.widget.ComboBox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.html.stabile");
+dojo.require("dojo.widget.PopupContainer");
+
+dojo.widget.incrementalComboBoxDataProvider = function(/*String*/ url, /*Number*/ limit, /*Number*/ timeout){
+	this.searchUrl = url;
+	this.inFlight = false;
+	this.activeRequest = null;
+	this.allowCache = false;
+
+	this.cache = {};
+
+	this.init = function(/*Widget*/ cbox){
+		this.searchUrl = cbox.dataUrl;
+	};
+
+	this.addToCache = function(/*String*/ keyword, /*Array*/ data){
+		if(this.allowCache){
+			this.cache[keyword] = data;
+		}
+	};
+
+	this.startSearch = function(/*String*/ searchStr, /*String*/ type, /*Boolean*/ ignoreLimit){
+		if(this.inFlight){
+			// FIXME: implement backoff!
+		}
+		var tss = encodeURIComponent(searchStr);
+		var realUrl = dojo.string.substituteParams(this.searchUrl, {"searchString": tss});
+		var _this = this;
+		var request = dojo.io.bind({
+			url: realUrl,
+			method: "get",
+			mimetype: "text/json",
+			load: function(type, data, evt){
+				_this.inFlight = false;
+				if(!dojo.lang.isArray(data)){
+					var arrData = [];
+					for(var key in data){
+						arrData.push([data[key], key]);
+					}
+					data = arrData;
+				}
+				_this.addToCache(searchStr, data);
+				_this.provideSearchResults(data);
+			}
+		});
+		this.inFlight = true;
+	};
+};
+
+dojo.widget.ComboBoxDataProvider = function(/*Array*/ dataPairs, /*Number*/ limit, /*Number*/ timeout){
+	// NOTE: this data provider is designed as a naive reference
+	// implementation, and as such it is written more for readability than
+	// speed. A deployable data provider would implement lookups, search
+	// caching (and invalidation), and a significantly less naive data
+	// structure for storage of items.
+
+	this.data = [];
+	this.searchTimeout = timeout || 500;
+	this.searchLimit = limit || 30;
+	this.searchType = "STARTSTRING"; // may also be "STARTWORD" or "SUBSTRING"
+	this.caseSensitive = false;
+	// for caching optimizations
+	this._lastSearch = "";
+	this._lastSearchResults = null;
+
+	this.init = function(/*Widget*/ cbox, /*DomNode*/ node){
+		if(!dojo.string.isBlank(cbox.dataUrl)){
+			this.getData(cbox.dataUrl);
+		}else{
+			// check to see if we can populate the list from <option> elements
+			if((node)&&(node.nodeName.toLowerCase() == "select")){
+				// NOTE: we're not handling <optgroup> here yet
+				var opts = node.getElementsByTagName("option");
+				var ol = opts.length;
+				var data = [];
+				for(var x=0; x<ol; x++){
+					var text = opts[x].textContent || opts[x].innerText || opts[x].innerHTML;
+					var keyValArr = [String(text), String(opts[x].value)];
+					data.push(keyValArr);
+					if(opts[x].selected){ 
+						cbox.setAllValues(keyValArr[0], keyValArr[1]);
+					}
+				}
+				this.setData(data);
+			}
+		}
+	};
+
+	this.getData = function(/*String*/ url){
+		dojo.io.bind({
+			url: url,
+			load: dojo.lang.hitch(this, function(type, data, evt){ 
+				if(!dojo.lang.isArray(data)){
+					var arrData = [];
+					for(var key in data){
+						arrData.push([data[key], key]);
+					}
+					data = arrData;
+				}
+				this.setData(data);
+			}),
+			mimetype: "text/json"
+		});
+	};
+
+	this.startSearch = function(/*String*/ searchStr, /*String*/ type, /*Boolean*/ ignoreLimit){
+		// FIXME: need to add timeout handling here!!
+		this._preformSearch(searchStr, type, ignoreLimit);
+	};
+
+	this._preformSearch = function(/*String*/ searchStr, /*String*/ type, /*Boolean*/ ignoreLimit){
+		//
+		//	NOTE: this search is LINEAR, which means that it exhibits perhaps
+		//	the worst possible speed characteristics of any search type. It's
+		//	written this way to outline the responsibilities and interfaces for
+		//	a search.
+		//
+		var st = type||this.searchType;
+		// FIXME: this is just an example search, which means that we implement
+		// only a linear search without any of the attendant (useful!) optimizations
+		var ret = [];
+		if(!this.caseSensitive){
+			searchStr = searchStr.toLowerCase();
+		}
+		for(var x=0; x<this.data.length; x++){
+			if((!ignoreLimit)&&(ret.length >= this.searchLimit)){
+				break;
+			}
+			// FIXME: we should avoid copies if possible!
+			var dataLabel = new String((!this.caseSensitive) ? this.data[x][0].toLowerCase() : this.data[x][0]);
+			if(dataLabel.length < searchStr.length){
+				// this won't ever be a good search, will it? What if we start
+				// to support regex search?
+				continue;
+			}
+
+			if(st == "STARTSTRING"){
+				if(searchStr == dataLabel.substr(0, searchStr.length)){
+					ret.push(this.data[x]);
+				}
+			}else if(st == "SUBSTRING"){
+				// this one is a gimmie
+				if(dataLabel.indexOf(searchStr) >= 0){
+					ret.push(this.data[x]);
+				}
+			}else if(st == "STARTWORD"){
+				// do a substring search and then attempt to determine if the
+				// preceeding char was the beginning of the string or a
+				// whitespace char.
+				var idx = dataLabel.indexOf(searchStr);
+				if(idx == 0){
+					// implicit match
+					ret.push(this.data[x]);
+				}
+				if(idx <= 0){
+					// if we didn't match or implicily matched, march onward
+					continue;
+				}
+				// otherwise, we have to go figure out if the match was at the
+				// start of a word...
+				// this code is taken almost directy from nWidgets
+				var matches = false;
+				while(idx!=-1){
+					// make sure the match either starts whole string, or
+					// follows a space, or follows some punctuation
+					if(" ,/(".indexOf(dataLabel.charAt(idx-1)) != -1){
+						// FIXME: what about tab chars?
+						matches = true; break;
+					}
+					idx = dataLabel.indexOf(searchStr, idx+1);
+				}
+				if(!matches){
+					continue;
+				}else{
+					ret.push(this.data[x]);
+				}
+			}
+		}
+		this.provideSearchResults(ret);
+	};
+
+	this.provideSearchResults = function(/*Array*/ resultsDataPairs){
+	};
+
+	this.addData = function(/*Array*/ pairs){
+		// FIXME: incredibly naive and slow!
+		this.data = this.data.concat(pairs);
+	};
+
+	this.setData = function(/*Array*/ pdata){
+		// populate this.data and initialize lookup structures
+		this.data = pdata;
+	};
+	
+	if(dataPairs){
+		this.setData(dataPairs);
+	}
+};
+
+dojo.widget.defineWidget(
+	"dojo.widget.ComboBox",
+	dojo.widget.HtmlWidget,
+	{
+		// Applies to any renderer
+		isContainer: false,
+	
+		forceValidOption: false,
+		searchType: "stringstart",
+		dataProvider: null,
+	
+		startSearch: function(/*String*/ searchString){},
+		selectNextResult: function(){},
+		selectPrevResult: function(){},
+		setSelectedResult: function(){},
+
+		// HTML specific stuff
+		autoComplete: true,
+		name: "", // clone in the name from the DOM node
+		textInputNode: null,
+		comboBoxValue: null,
+		comboBoxSelectionValue: null,
+		optionsListWrapper: null,
+		optionsListNode: null,
+		downArrowNode: null,
+		searchTimer: null,
+		searchDelay: 100,
+		dataUrl: "",
+		fadeTime: 200,
+		disabled: false,
+		// maxListLength limits list to X visible rows, scroll on rest 
+		maxListLength: 8, 
+		// mode can also be "remote" for JSON-returning live search or "html" for
+		// dumber live search
+		mode: "local", 
+		selectedResult: null,
+		_highlighted_option: null,
+		_prev_key_backspace: false,
+		_prev_key_esc: false,
+		_gotFocus: false,
+		_mouseover_list: false,
+		dataProviderClass: "dojo.widget.ComboBoxDataProvider",
+		buttonSrc: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"),
+
+		//the old implementation has builtin fade toggle, so we mimic it here
+		dropdownToggle: "fade",
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/ComboBox.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/ComboBox.css"),
+
+
+		setValue: function(/*String*/ value){
+			this.comboBoxValue.value = value;
+			if (this.textInputNode.value != value){ // prevent mucking up of selection
+				this.textInputNode.value = value;
+				// only change state and value if a new value is set
+				dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+				this.onValueChanged(value);
+			}
+		},
+
+		// for user to override
+		onValueChanged: function(){ },
+
+		getValue: function(){
+			return this.comboBoxValue.value;
+		},
+	
+		getState: function(){
+			return {value: this.getValue()};
+		},
+
+		setState: function(/*Object*/ state){
+			this.setValue(state.value);
+		},
+
+		enable:function(){
+			this.disabled=false;
+			this.isEnabled = true; 
+			this.textInputNode.removeAttribute("disabled");
+		},
+ 
+		disable: function(){
+			this.disabled = true; 
+			this.isEnabled = false;
+			this.textInputNode.setAttribute("disabled",true); 
+		}, 
+
+		getCaretPos: function(/*DomNode*/ element){
+			// khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
+			if(dojo.lang.isNumber(element.selectionStart)){
+				// FIXME: this is totally borked on Moz < 1.3. Any recourse?
+				return element.selectionStart;
+			}else if(dojo.render.html.ie){
+				// in the case of a mouse click in a popup being handled,
+				// then the document.selection is not the textarea, but the popup
+				// var r = document.selection.createRange();
+				// hack to get IE 6 to play nice. What a POS browser.
+				var tr = document.selection.createRange().duplicate();
+				var ntr = element.createTextRange();
+				tr.move("character",0);
+				ntr.move("character",0);
+				try {
+					// If control doesnt have focus, you get an exception.
+					// Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
+					// There appears to be no workaround for this - googled for quite a while.
+					ntr.setEndPoint("EndToEnd", tr);
+					return String(ntr.text).replace(/\r/g,"").length;
+				} catch (e){
+					return 0; // If focus has shifted, 0 is fine for caret pos.
+				}
+				
+			}
+		},
+
+		setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
+			location = parseInt(location);
+			this.setSelectedRange(element, location, location);
+		},
+
+		setSelectedRange: function(/*DomNode*/ element, /*Number*/ start, /*Number*/ end){
+			if(!end){ end = element.value.length; }  // NOTE: Strange - should be able to put caret at start of text?
+			// Mozilla
+			// parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
+			if(element.setSelectionRange){
+				element.focus();
+				element.setSelectionRange(start, end);
+			}else if(element.createTextRange){ // IE
+				var range = element.createTextRange();
+				with(range){
+					collapse(true);
+					moveEnd('character', end);
+					moveStart('character', start);
+					select();
+				}
+			}else{ //otherwise try the event-creation hack (our own invention)
+				// do we need these?
+				element.value = element.value;
+				element.blur();
+				element.focus();
+				// figure out how far back to go
+				var dist = parseInt(element.value.length)-end;
+				var tchar = String.fromCharCode(37);
+				var tcc = tchar.charCodeAt(0);
+				for(var x = 0; x < dist; x++){
+					var te = document.createEvent("KeyEvents");
+					te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
+					element.dispatchEvent(te);
+				}
+			}
+		},
+
+		// does the keyboard related stuff
+		_handleKeyEvents: function(/*Event*/ evt){
+			if(evt.ctrlKey || evt.altKey || !evt.key){ return; }
+
+			// reset these
+			this._prev_key_backspace = false;
+			this._prev_key_esc = false;
+
+			var k = dojo.event.browser.keys;
+			var doSearch = true;
+
+			switch(evt.key){
+	 			case k.KEY_DOWN_ARROW:
+					if(!this.popupWidget.isShowingNow){
+						this.startSearchFromInput();
+					}
+					this.highlightNextOption();
+					dojo.event.browser.stopEvent(evt);
+					return;
+				case k.KEY_UP_ARROW:
+					this.highlightPrevOption();
+					dojo.event.browser.stopEvent(evt);
+					return;
+				case k.KEY_TAB:
+					// using linux alike tab for autocomplete
+					if(!this.autoComplete && this.popupWidget.isShowingNow && this._highlighted_option){
+						dojo.event.browser.stopEvent(evt);
+						this.selectOption({ 'target': this._highlighted_option, 'noHide': false});
+	
+						// put caret last
+						this.setSelectedRange(this.textInputNode, this.textInputNode.value.length, null);
+					}else{
+						this.selectOption();
+						return;
+					}
+					break;
+				case k.KEY_ENTER:
+					// prevent submitting form if we press enter with list open
+					if(this.popupWidget.isShowingNow){
+						dojo.event.browser.stopEvent(evt);
+					}
+					// fallthrough
+				case " ":
+					if(this.popupWidget.isShowingNow && this._highlighted_option){
+						dojo.event.browser.stopEvent(evt);
+						this.selectOption();
+						this.hideResultList();
+						return;
+					}
+					break;
+				case k.KEY_ESCAPE:
+					this.hideResultList();
+					this._prev_key_esc = true;
+					return;
+				case k.KEY_BACKSPACE:
+					this._prev_key_backspace = true;
+					if(!this.textInputNode.value.length){
+						this.setAllValues("", "");
+						this.hideResultList();
+						doSearch = false;
+					}
+					break;
+				case k.KEY_RIGHT_ARROW: // fall through
+				case k.KEY_LEFT_ARROW: // fall through
+					doSearch = false;
+					break;
+				default:// non char keys (F1-F12 etc..)  shouldn't open list
+					if(evt.charCode==0){
+						doSearch = false;
+					}
+			}
+
+			if(this.searchTimer){
+				clearTimeout(this.searchTimer);
+			}
+			if(doSearch){
+				// if we have gotten this far we dont want to keep our highlight
+				this.blurOptionNode();
+	
+				// need to wait a tad before start search so that the event bubbles through DOM and we have value visible
+				this.searchTimer = setTimeout(dojo.lang.hitch(this, this.startSearchFromInput), this.searchDelay);
+			}
+		},
+
+		// When inputting characters using an input method, such as Asian  
+		// languages, it will generate this event instead of onKeyDown event 
+		compositionEnd: function(/*Event*/ evt){
+			evt.key = evt.keyCode;
+			this._handleKeyEvents(evt);
+		},
+
+		onKeyUp: function(/*Event*/ evt){
+			this.setValue(this.textInputNode.value);
+		},
+
+		setSelectedValue: function(/*String*/ value){
+			// FIXME, not sure what to do here!
+			this.comboBoxSelectionValue.value = value;
+		},
+
+		setAllValues: function(/*String*/ value1, /*String*/ value2){
+			this.setSelectedValue(value2);
+			this.setValue(value1);
+		},
+
+		// does the actual highlight
+		focusOptionNode: function(/*DomNode*/ node){
+			if(this._highlighted_option != node){
+				this.blurOptionNode();
+				this._highlighted_option = node;
+				dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+			}
+		},
+
+		// removes highlight on highlighted
+		blurOptionNode: function(){
+			if(this._highlighted_option){
+				dojo.html.removeClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+				this._highlighted_option = null;
+			}
+		},
+
+		highlightNextOption: function(){
+			if((!this._highlighted_option) || !this._highlighted_option.parentNode){
+				this.focusOptionNode(this.optionsListNode.firstChild);
+			}else if(this._highlighted_option.nextSibling){
+				this.focusOptionNode(this._highlighted_option.nextSibling);
+			}
+			dojo.html.scrollIntoView(this._highlighted_option);
+		},
+
+		highlightPrevOption: function(){
+			if(this._highlighted_option && this._highlighted_option.previousSibling){
+				this.focusOptionNode(this._highlighted_option.previousSibling);
+			}else{
+				this._highlighted_option = null;
+				this.hideResultList();
+				return;
+			}
+			dojo.html.scrollIntoView(this._highlighted_option);
+		},
+
+		itemMouseOver: function(/*Event*/ evt){
+			if (evt.target === this.optionsListNode){ return; }
+			this.focusOptionNode(evt.target);
+			dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+		},
+
+		itemMouseOut: function(/*Event*/ evt){
+			if (evt.target === this.optionsListNode){ return; }
+			this.blurOptionNode();
+		},
+
+		// reset button size; this function is called when the input area has changed size
+		onResize: function(){
+			var inputSize = dojo.html.getContentBox(this.textInputNode);
+			if( inputSize.height == 0 ){
+				// need more time to calculate size
+				dojo.lang.setTimeout(this, "onResize", 50);
+				return;
+			}
+			var buttonSize = { width: inputSize.height, height: inputSize.height};
+			dojo.html.setContentBox(this.downArrowNode, buttonSize);
+		},
+
+		fillInTemplate: function(/*Object*/ args, /*Object*/ frag){
+			// For inlining a table we need browser specific CSS
+			dojo.html.applyBrowserClass(this.domNode);
+
+			var source = this.getFragNodeRef(frag); 
+			if (! this.name && source.name){ this.name = source.name; } 
+			this.comboBoxValue.name = this.name; 
+			this.comboBoxSelectionValue.name = this.name+"_selected";
+
+			/* different nodes get different parts of the style */
+			dojo.html.copyStyle(this.domNode, source);
+			dojo.html.copyStyle(this.textInputNode, source);
+			dojo.html.copyStyle(this.downArrowNode, source);
+			with (this.downArrowNode.style){ // calculate these later
+				width = "0px";
+				height = "0px";
+			}
+
+			var dpClass;
+			if(this.mode == "remote"){
+				dpClass = dojo.widget.incrementalComboBoxDataProvider;
+			}else if(typeof this.dataProviderClass == "string"){
+				dpClass = dojo.evalObjPath(this.dataProviderClass)
+			}else{
+				dpClass = this.dataProviderClass;
+			}
+			this.dataProvider = new dpClass();
+			this.dataProvider.init(this, this.getFragNodeRef(frag));
+
+			this.popupWidget = new dojo.widget.createWidget("PopupContainer", 
+				{toggle: this.dropdownToggle, toggleDuration: this.toggleDuration});
+			dojo.event.connect(this, 'destroy', this.popupWidget, 'destroy');
+			this.optionsListNode = this.popupWidget.domNode;
+			this.domNode.appendChild(this.optionsListNode);
+			dojo.html.addClass(this.optionsListNode, 'dojoComboBoxOptions');
+			dojo.event.connect(this.optionsListNode, 'onclick', this, 'selectOption');
+			dojo.event.connect(this.optionsListNode, 'onmouseover', this, '_onMouseOver');
+			dojo.event.connect(this.optionsListNode, 'onmouseout', this, '_onMouseOut');
+			
+			dojo.event.connect(this.optionsListNode, "onmouseover", this, "itemMouseOver");
+			dojo.event.connect(this.optionsListNode, "onmouseout", this, "itemMouseOut");
+		},
+
+		focus: function(){
+			// summary
+			//	set focus to input node from code
+			this.tryFocus();
+		},
+
+		openResultList: function(/*Array*/ results){
+			if (!this.isEnabled){
+				return;
+			}
+			this.clearResultList();
+			if(!results.length){
+				this.hideResultList();
+			}
+
+			if(	(this.autoComplete)&&
+				(results.length)&&
+				(!this._prev_key_backspace)&&
+				(this.textInputNode.value.length > 0)){
+				var cpos = this.getCaretPos(this.textInputNode);
+				// only try to extend if we added the last character at the end of the input
+				if((cpos+1) > this.textInputNode.value.length){
+					// only add to input node as we would overwrite Capitalisation of chars
+					this.textInputNode.value += results[0][0].substr(cpos);
+					// build a new range that has the distance from the earlier
+					// caret position to the end of the first string selected
+					this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length);
+				}
+			}
+
+			var even = true;
+			while(results.length){
+				var tr = results.shift();
+				if(tr){
+					var td = document.createElement("div");
+					td.appendChild(document.createTextNode(tr[0]));
+					td.setAttribute("resultName", tr[0]);
+					td.setAttribute("resultValue", tr[1]);
+					td.className = "dojoComboBoxItem "+((even) ? "dojoComboBoxItemEven" : "dojoComboBoxItemOdd");
+					even = (!even);
+					this.optionsListNode.appendChild(td);
+				}
+			}
+
+			// show our list (only if we have content, else nothing)
+			this.showResultList();
+		},
+
+		onFocusInput: function(){
+			this._hasFocus = true;
+		},
+
+		onBlurInput: function(){
+			this._hasFocus = false;
+			this._handleBlurTimer(true, 500);
+		},
+
+		// collect all blur timers issues here
+		_handleBlurTimer: function(/*Boolean*/clear, /*Number*/ millisec){
+			if(this.blurTimer && (clear || millisec)){
+				clearTimeout(this.blurTimer);
+			}
+			if(millisec){ // we ignore that zero is false and never sets as that never happens in this widget
+				this.blurTimer = dojo.lang.setTimeout(this, "checkBlurred", millisec);
+			}
+		},
+	
+		// these 2 are needed in IE and Safari as inputTextNode loses focus when scrolling optionslist
+		_onMouseOver: function(/*Event*/ evt){
+			if(!this._mouseover_list){
+				this._handleBlurTimer(true, 0);
+				this._mouseover_list = true;
+			}
+		},
+
+		_onMouseOut:function(/*Event*/ evt){
+			var relTarget = evt.relatedTarget;
+			if(!relTarget || relTarget.parentNode!=this.optionsListNode){
+				this._mouseover_list = false;
+				this._handleBlurTimer(true, 100);
+				this.tryFocus();
+			}
+		},
+
+		_isInputEqualToResult: function(/*String*/ result){
+			var input = this.textInputNode.value;
+			if(!this.dataProvider.caseSensitive){
+				input = input.toLowerCase();
+				result = result.toLowerCase();
+			}
+			return (input == result);
+		},
+
+		_isValidOption: function(){
+			var tgt = dojo.html.firstElement(this.optionsListNode);
+			var isValidOption = false;
+			while(!isValidOption && tgt){
+				if(this._isInputEqualToResult(tgt.getAttribute("resultName"))){
+					isValidOption = true;
+				}else{
+					tgt = dojo.html.nextElement(tgt);
+				}
+			}
+			return isValidOption;
+		},
+
+		checkBlurred: function(){
+			if(!this._hasFocus && !this._mouseover_list){
+				this.hideResultList();
+				// clear the list if the user empties field and moves away.
+				if(!this.textInputNode.value.length){
+					this.setAllValues("", "");
+					return;
+				}
+
+				var isValidOption = this._isValidOption();
+				// enforce selection from option list
+				if(this.forceValidOption && !isValidOption){
+					this.setAllValues("", "");
+					return;
+				}
+				if(!isValidOption){// clear
+					this.setSelectedValue("");
+				}
+			}
+		},
+
+		sizeBackgroundIframe: function(){
+			var mb = dojo.html.getMarginBox(this.optionsListNode);
+			if( mb.width==0 || mb.height==0 ){
+				// need more time to calculate size
+				dojo.lang.setTimeout(this, "sizeBackgroundIframe", 100);
+				return;
+			}
+		},
+
+		selectOption: function(/*Event*/ evt){
+			var tgt = null;
+			if(!evt){
+				evt = { target: this._highlighted_option };
+			}
+
+			if(!dojo.html.isDescendantOf(evt.target, this.optionsListNode)){
+				// handle autocompletion where the the user has hit ENTER or TAB
+	
+				// if the input is empty do nothing
+				if(!this.textInputNode.value.length){
+					return;
+				}
+				tgt = dojo.html.firstElement(this.optionsListNode);
+
+				// user has input value not in option list
+				if(!tgt || !this._isInputEqualToResult(tgt.getAttribute("resultName"))){
+					return;
+				}
+				// otherwise the user has accepted the autocompleted value
+			}else{
+				tgt = evt.target; 
+			}
+
+			while((tgt.nodeType!=1)||(!tgt.getAttribute("resultName"))){
+				tgt = tgt.parentNode;
+				if(tgt === dojo.body()){
+					return false;
+				}
+			}
+
+			this.selectedResult = [tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")];
+			this.setAllValues(tgt.getAttribute("resultName"), tgt.getAttribute("resultValue"));
+			if(!evt.noHide){
+				this.hideResultList();
+				this.setSelectedRange(this.textInputNode, 0, null);
+			}
+			this.tryFocus();
+		},
+
+		clearResultList: function(){
+			if(this.optionsListNode.innerHTML){
+				this.optionsListNode.innerHTML = "";  // browser natively knows how to collect this memory
+			}
+		},
+
+		hideResultList: function(){
+			this.popupWidget.close();
+		},
+
+		showResultList: function(){
+			// Our dear friend IE doesnt take max-height so we need to calculate that on our own every time
+			var childs = this.optionsListNode.childNodes;
+			if(childs.length){
+				var visibleCount = this.maxListLength;
+				if(childs.length < visibleCount){
+					visibleCount = childs.length;
+				}
+
+				with(this.optionsListNode.style)
+				{
+					display = "";
+					if(visibleCount == childs.length){
+						//no scrollbar is required, so unset height to let browser calcuate it,
+						//as in css, overflow is already set to auto
+						height = "";
+					}else{
+						//show it first to get the correct dojo.style.getOuterHeight(childs[0])
+						//FIXME: shall we cache the height of the item?
+						height = visibleCount * dojo.html.getMarginBox(childs[0]).height +"px";
+					}
+					width = (dojo.html.getMarginBox(this.domNode).width-2)+"px";
+					
+				}
+				this.popupWidget.open(this.domNode, this, this.downArrowNode);
+			}else{
+				this.hideResultList();
+			}
+		},
+
+		handleArrowClick: function(){
+			this._handleBlurTimer(true, 0);
+			this.tryFocus();
+			if(this.popupWidget.isShowingNow){
+				this.hideResultList();
+			}else{
+				// forces full population of results, if they click
+				// on the arrow it means they want to see more options
+				this.startSearch("");
+			}
+		},
+
+		tryFocus: function(){
+			try {
+				this.textInputNode.focus();
+			} catch (e){
+				// element isn't focusable if disabled, or not visible etc - not easy to test for.
+	 		};
+		},
+
+		startSearchFromInput: function(){
+			this.startSearch(this.textInputNode.value);
+		},
+
+		postCreate: function(){
+			this.onResize();
+			dojo.event.connect(this, "startSearch", this.dataProvider, "startSearch");
+			dojo.event.connect(this.dataProvider, "provideSearchResults", this, "openResultList");
+			dojo.event.connect(this.textInputNode, "onblur", this, "onBlurInput");
+			dojo.event.connect(this.textInputNode, "onfocus", this, "onFocusInput");
+			if (this.disabled){ 
+				this.disable();
+			}
+			var s = dojo.widget.html.stabile.getState(this.widgetId);
+			if (s){
+				this.setState(s);
+			}
+		}
+	}
+);

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



Mime
View raw message