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 [34/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/ ...

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Show.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Show.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Show.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Show.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,244 @@
+dojo.provide("dojo.widget.Show");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.event.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.math.curves");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.func");
+
+dojo.widget.defineWidget(
+	"dojo.widget.Show",
+	dojo.widget.HtmlWidget,
+	function(){
+		this._slides=[];
+	},
+	{
+	isContainer: true,
+	_slide: -1,
+
+	body: null,
+	nav: null,
+	hider: null,
+	select: null,
+	option: null,
+	inNav: false,
+	debugPane: null,
+	noClick: false,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/Show.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/Show.css"),
+	fillInTemplate: function(args, frag){
+		if(args.debugPane){
+			var dp = this.debugPane = dojo.widget.byId(args.debugPane);
+			dp.hide();
+			dojo.event.connect(dp, "closeWindow", dojo.lang.hitch(this, function(){ this.debugPane = false; }));
+		}
+		var source = this.getFragNodeRef(frag);
+		this.sourceNode = dojo.body().appendChild(source.cloneNode(true));
+		for(var i = 0, child; child = this.sourceNode.childNodes[i]; i++){
+			if(child.tagName && child.getAttribute("dojotype").toLowerCase() == "showslide"){
+				child.className = "dojoShowPrintSlide";
+				child.innerHTML = "<h1>" + child.title + "</h1>" + child.innerHTML;
+			}
+		}
+		this.sourceNode.className = "dojoShowPrint";
+		this.sourceNode.style.display = "none";
+		
+		dojo.event.connect(document, "onclick", this, "gotoSlideByEvent");
+		if(dojo.render.html.ie) {
+			dojo.event.connect(document,"onkeydown",this, "gotoSlideByEvent");
+		} else {
+			// while keydown works, keypress allows rapid successive key presses
+			// to be handled correctly
+			dojo.event.connect(document,"onkeypress",this, "gotoSlideByEvent");
+		}
+		dojo.event.connect(window, "onresize", this, "resizeWindow");
+		dojo.event.connect(this.nav, "onmousemove", this, "popUpNav");
+	},
+	postCreate: function(){
+		this._slides = [];
+		for(var i = 0, child; child = this.children[i]; i++){
+			if(child.widgetType == "ShowSlide"){
+				this._slides.push(child);
+				this.option.text = child.title+" ("+(i+1)+")";
+				this.option.parentNode.insertBefore(this.option.cloneNode(true), this.option);
+			}
+		}
+		this.option.parentNode.removeChild(this.option);
+		this.domNode.style.display = "block";
+		this.resizeWindow();
+
+		this.gotoSlide(0, true);
+
+		// check to see if we're initialized from a particular slide
+		dojo.addOnLoad(dojo.lang.hitch(this, 
+			function(){
+				var th = window.location.hash;
+				if(th.length){
+					var parts = (""+window.location).split(this.widgetId+"_SlideNo_");
+					if(parts.length > 1){
+						setTimeout(dojo.lang.hitch(this, function(){
+							this.gotoSlide(parseInt(parts[1]), true);
+						}), 300);
+					}
+				}
+			})
+		);
+	},
+	gotoSlide: function(/*int*/ slide, /*Boolean*/preventSetHash){
+		if(slide == this._slide){
+			return;
+		}
+
+		if(!this._slides[slide]){
+			// slide: string
+			for(var i = 0, child; child = this._slides[i]; i++){
+				if(child.title == slide){
+					slide = i;
+					break;
+				}
+			}
+		}
+		
+		if(!this._slides[slide]){
+			return;
+		}
+
+		if(this.debugPane){
+			if(this._slides[slide].debug){
+				this.debugPane.show();
+			}else{
+				this.debugPane.hide();
+			}
+		}
+		
+		if(this._slide != -1){
+			while(this._slides[this._slide].previousAction()){}
+		}
+
+		if(!preventSetHash){
+			window.location.href = "#"+this.widgetId+"_SlideNo_"+slide;
+		}
+		if(this._slides[this._slide]){
+			this._slides[this._slide].hide();
+		}
+		
+		this._slide = slide;
+		this.select.selectedIndex = slide;
+		var cn = this.contentNode;
+		while(cn.firstChild){ cn.removeChild(cn.firstChild); }
+		cn.appendChild(this._slides[slide].domNode);
+		this._slides[slide].show();
+	},
+	gotoSlideByEvent: function(/*Event*/ event){
+		var node = event.target;
+		var type = event.type;
+		if(type == "click"){
+			if(node.tagName == "OPTION" && node.parentNode == this.select){
+				this.gotoSlide(node.index);
+			}else if(node == this.select){
+				this.gotoSlide(node.selectedIndex);
+			}else{
+				this.nextSlide(event);
+			}
+		}else if (type=="keydown" || type=="keypress") {
+			var key = event.keyCode;
+			var ch = event.charCode;
+			if(key == 63234 || key == 37){
+				this.previousSlide(event);
+			}else if(key == 63235 || key == 39 || ch == 32){
+				this.nextSlide(event);
+			}
+		}
+	},
+	nextSlide: function(/*Event?*/ event){
+		if(!this.stopEvent(event)){
+			return false;
+		}
+		if(!this._slides[this._slide].nextAction(event)){
+			if((this._slide + 1) != this._slides.length){
+				this.gotoSlide(this._slide + 1);
+				return true; // boolean
+			}
+			return false; // boolean
+		}
+	},
+	previousSlide: function(/*Event?*/ event){
+		if(!this.stopEvent(event)){
+			return false;
+		}
+		if(!this._slides[this._slide].previousAction(event)){
+			if((this._slide - 1) != -1){
+				this.gotoSlide(this._slide - 1);
+				return true; // boolean
+			}
+			return false; // boolean
+		}
+	},
+
+	stopEvent: function(/*Event*/ ev){
+		if(!ev){
+			return true;
+		}
+	
+		if (ev.type == "click" && (this._slides[this._slide].noClick || this.noClick)) {
+			return false;
+		}	
+		var target = ev.target;
+		// Check to see if the target is below the show domNode
+		while(target != null){
+			if(target == this.domNode){
+				target = ev.target;
+				break;
+			}
+			target = target.parentNode;
+		}
+		// Now that we know it's below this widget's domNode, we bubble up until we get to our domNode
+		while(target && target != this.domNode){
+			if(target.tagName == "A" || target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT"){
+				return false;
+			}
+			if(typeof target.onclick == "function" || typeof target.onkeypress == "function"){
+				return false;
+			}
+			target = target.parentNode;
+		}
+		
+		if(window.event){
+			ev.returnValue = false;
+			ev.cancelBubble = true;
+		}else{
+			ev.preventDefault();
+			ev.stopPropagation();
+		}
+		
+		return true;
+	},
+	popUpNav: function(){
+		if(!this.inNav){
+			// dojo.widget.Show.node = this.nav;
+			dojo.lfx.propertyAnimation(this.nav, {
+				"height": { start: 5, end: 30 }
+			}, 250).play();
+		}
+		clearTimeout(this.inNav);
+		this.inNav = setTimeout(dojo.lang.hitch(this, "hideNav"), 2000);
+	},
+	hideNav: function(){
+		clearTimeout(this.inNav);
+		this.inNav = false;
+		// dojo.widget.Show.node = this.nav;
+		dojo.lfx.propertyAnimation(this.nav, {
+			"height": { start: 30, end: 5 }
+		}, 250).play();
+	},
+	resizeWindow: function(/*Event*/ ev){
+		dojo.body().style.height = "auto";
+		var h = Math.max(
+			document.documentElement.scrollHeight || dojo.body().scrollHeight,
+			dojo.html.getViewport().height);
+		dojo.body().style.height = h + "px";
+	}
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowAction.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowAction.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowAction.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowAction.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,14 @@
+dojo.provide("dojo.widget.ShowAction");
+dojo.require("dojo.widget.*");
+
+dojo.widget.defineWidget(
+	"dojo.widget.ShowAction",
+	dojo.widget.HtmlWidget,
+{
+	on: "",
+	action: "fade",
+	duration: 350,
+	from: "",
+	to: "",
+	auto: "false"
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowSlide.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowSlide.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowSlide.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/ShowSlide.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,219 @@
+dojo.provide("dojo.widget.ShowSlide");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lfx.html");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.animation.Animation");
+dojo.require("dojo.gfx.color");
+
+dojo.widget.defineWidget(
+	"dojo.widget.ShowSlide",
+	dojo.widget.HtmlWidget,
+{
+	title: "",
+	_action: -1,
+	isContainer: true,
+	_components: {},
+	_actions: [],
+
+	gotoAction: function(/*int*/ action){
+		this._action = action;
+	},
+	_nextAction: function(/*Event?*/ event){
+		if((this._action + 1) != this._actions.length){
+			++this._action;
+			return true; // boolean
+		}
+		return false; // boolean
+	},
+	_previousAction: function(/*Event?*/ event){
+		if((this._action - 1) != -1){
+			--this._action;
+			return true; // boolean
+		}
+		return false; // boolean
+	},
+
+	htmlTitle: null,
+	debug: false,
+	noClick: false,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/ShowSlide.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/ShowSlide.css"),
+	postCreate: function(){
+		this.htmlTitle.innerHTML = this.title;
+
+		var actions = this.getChildrenOfType("ShowAction", false);
+		var atypes = {};
+		dojo.lang.forEach(actions, function(act){ atypes[act.on] = true; });
+
+		this._components = {};
+		var cn = this.containerNode;
+		var nodes = dojo.render.html.ie ? cn.all : cn.getElementsByTagName('*');
+		dojo.lang.forEach(nodes, function(node){
+			var as = node.getAttribute("as");
+			if(as){
+				if(!this._components[as]){
+					this._components[as] = [];
+				}
+				this._components[as].push(node);
+				if(!atypes[as]){
+					var tmpAction = dojo.widget.createWidget("ShowAction", { on: as });
+					this.addChild(tmpAction);
+					atypes[as] = true;
+				}
+			}
+		}, this);
+
+		this._actions = [];
+		actions = this.getChildrenOfType("ShowAction", false);
+		dojo.lang.forEach(actions, function(child){
+			this._actions.push(child);
+			var components = this._components[child.on];
+			for(var j = 0, component; component = components[j]; j++){
+				if(child["action"] && (
+					(child.action != "remove")&&
+					(child.action != "fadeout")&&
+					(child.action != "wipeout")
+				) ){
+					this.hideComponent(component);
+				}
+			}
+		}, this);
+	},
+
+	previousAction: function(/*Event?*/ event){
+		if(!this.parent.stopEvent(event)){
+			return false;
+		}
+
+		var action = this._actions[this._action];
+		if(!action){
+			return false;
+		}
+
+		var on = action.on;
+		while(action.on == on){
+			var components = this._components[on];
+			for(var i = 0, component; component = components[i]; i++){
+				if(
+					(action.action == "remove")||
+					(action.action == "fadeout")||
+					(action.action == "wipeout")
+				){
+					if(component.style.display == "none"){
+						component.style.display = "";
+						component.style.visibility = "visible";
+						var exits = true;
+					}
+					dojo.html.setOpacity(component, 1);
+				}else if(action.action){
+					this.hideComponent(component);
+				}
+			}
+
+			--this._action;
+
+			if(exits){
+				return true;
+			}	
+
+			if(action.auto == "true"){
+				on = this._actions[this._action].on;
+			}
+
+			action = this._actions[this._action];
+			if(!action){
+				return false;
+			}
+		}
+		return true;
+	},
+	hideComponent: function(/*Node*/ component){
+		component.style.visibility = "hidden";
+		component.style.backgroundColor = "transparent";
+		var parent = component.parentNode;
+		if((parent)&&(parent.tagName.toLowerCase() == "li")){
+			parent.oldType = parent.style.listStyleType;
+			parent.style.listStyleType = "none";
+		}
+	},
+	nextAction: function(/*Event?*/ event){
+		if(!this.parent.stopEvent(event)){
+			return false;
+		}
+
+		if(!this._nextAction(this)){
+			return false;
+		}
+
+		var action = this._actions[this._action];
+		if(!action){
+			return false;
+		}
+		var tmpAction = action["action"];
+		
+		var components = this._components[action.on];
+		for(var i = 0, component; component = components[i]; i++){
+			if(tmpAction){
+				var duration = action.duration || 1000;
+				if((tmpAction == "fade")||(tmpAction == "fadeIn")){
+					dojo.html.setOpacity(component, 0);
+					dojo.lfx.html.fadeShow(component, duration).play(true);
+				}else if(tmpAction == "fadeout"){
+					dojo.lfx.html.fadeHide(component, duration).play(true);
+				}else if(tmpAction == "fly"){
+					var width = dojo.html.getMarginBox(component).width;
+					var position = dojo.html.getAbsolutePosition(component);
+					// alert(position);
+					component.style.position = "relative";
+					component.style.left = -(width + position.x) + "px";
+					dojo.lfx.html.slideBy(component, { top: 0, left: (width + position.x)}, duration, -1, this.callWith).play(true);
+				}else if((tmpAction == "wipe")||(tmpAction == "wipein")){
+					dojo.lfx.html.wipeIn(component, duration).play();
+				}else if(tmpAction == "wipeout"){
+					dojo.lfx.html.wipeOut(component, duration).play();
+				}else if(tmpAction == "color"){
+					var from = new dojo.gfx.color.Color(action.from).toRgb();
+					var to = new dojo.gfx.color.Color(action.to).toRgb();
+					var anim = new dojo.animation.Animation(new dojo.math.curves.Line(from, to), duration, 0);
+					var node = component;
+					dojo.event.connect(anim, "onAnimate", function(e) {
+						node.style.color = "rgb(" + e.coordsAsInts().join(",") + ")";
+					});
+					anim.play(true);
+				}else if(tmpAction == "bgcolor"){
+					dojo.lfx.html.unhighlight(component, action.to, duration).play();
+				}else if(tmpAction == "remove"){
+					component.style.display = "none";
+				}
+
+				if(tmpAction == "hide"){
+					component.style.visibility = "hidden";
+				}else{
+					component.style.visibility = "visible";
+				}
+			}
+		}
+		
+		action = this._actions[this._action + 1];
+		if(action && action.auto == "true"){
+			this.nextAction();
+		}
+
+		return true;
+	},
+	callWith: function(/*Node*/ node){
+		if(!node){ return; }
+		if(dojo.lang.isArray(node)){
+			dojo.lang.forEach(node, arguments.callee);
+			return;
+		}
+		var parent = node.parentNode;
+		if((parent)&&(parent.tagName.toLowerCase() == "li")){
+			parent.style.listStyleType = parent.oldType;
+		}
+	}
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SlideShow.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SlideShow.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SlideShow.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SlideShow.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,131 @@
+dojo.provide("dojo.widget.SlideShow");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.html.display");
+
+dojo.widget.defineWidget(
+	"dojo.widget.SlideShow",
+	dojo.widget.HtmlWidget,
+	{
+		templatePath: dojo.uri.dojoUri("src/widget/templates/SlideShow.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/SlideShow.css"),
+
+		// useful properties
+		imgUrls: [],		// the images we'll go through
+		imgUrlBase: "",
+		urlsIdx: 0,		// where in the images we are
+		delay: 4000, 		// give it 4 seconds
+		transitionInterval: 2000, // 2 seconds
+		imgWidth: 800,	// img width
+		imgHeight: 600,	// img height
+		background: "img2", // what's in the bg
+		foreground: "img1", // what's in the fg
+		stopped: false,	// should I stay or should I go?
+		fadeAnim: null, // references our animation
+
+		// our DOM nodes:
+		imagesContainer: null,
+		startStopButton: null,
+		controlsContainer: null,
+		img1: null,
+		img2: null,
+		preventCache: false,
+
+		fillInTemplate: function(){
+			// safari will cache the images and not fire an image onload event if
+			// there are only two images in the slideshow
+			if(dojo.render.html.safari && this.imgUrls.length == 2) {
+				this.preventCache = true;
+			}
+			dojo.html.setOpacity(this.img1, 0.9999);
+			dojo.html.setOpacity(this.img2, 0.9999);
+			with(this.imagesContainer.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			with(this.img1.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			with(this.img2.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			if(this.imgUrls.length>1){
+				this.img2.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++] + this.getUrlSuffix();
+				this.endTransition();
+			}else{
+				this.img1.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++] + this.getUrlSuffix();
+			}
+		},
+
+		getUrlSuffix: function() {
+			if(this.preventCache) {
+				return "?ts=" + (new Date()).getTime();
+			} else {
+				return "";
+			}
+		},
+		
+		togglePaused: function(){
+			dojo.debug("pause");
+			if(this.stopped){
+				this.stopped = false;
+				this.backgroundImageLoaded();
+				this.startStopButton.value= "pause";
+			}else{
+				this.stopped = true;
+				this.startStopButton.value= "play";
+			}
+		},
+
+		backgroundImageLoaded: function(){
+			// start fading out the foreground image
+			if(this.stopped){ return; }
+
+			// actually start the fadeOut effect
+			// NOTE: if we wanted to use other transition types, we'd set them up
+			// 		 here as well
+			if(this.fadeAnim) {
+				this.fadeAnim.stop();
+			}
+			this.fadeAnim = dojo.lfx.fadeOut(this[this.foreground], 
+				this.transitionInterval, null);
+			dojo.event.connect(this.fadeAnim,"onEnd",this,"endTransition");
+			this.fadeAnim.play();
+		},
+
+		endTransition: function(){
+			// move the foreground image to the background 
+			with(this[this.background].style){ zIndex = parseInt(zIndex)+1; }
+			with(this[this.foreground].style){ zIndex = parseInt(zIndex)-1; }
+
+			// fg/bg book-keeping
+			var tmp = this.foreground;
+			this.foreground = this.background;
+			this.background = tmp;
+			// keep on truckin
+			this.loadNextImage();
+		},
+
+		loadNextImage: function(){
+			// load a new image in that container, and make sure it informs
+			// us when it finishes loading
+			dojo.event.kwConnect({
+				srcObj: this[this.background],
+				srcFunc: "onload",
+				adviceObj: this,
+				adviceFunc: "backgroundImageLoaded",
+				once: true, // make sure we only ever hear about it once
+				delay: this.delay
+			});
+			dojo.html.setOpacity(this[this.background], 1.0);
+			this[this.background].src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+			if(this.urlsIdx>(this.imgUrls.length-1)){
+				this.urlsIdx = 0;
+			}
+		}
+	}
+);

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Slider.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Slider.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Slider.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Slider.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,878 @@
+dojo.provide("dojo.widget.Slider");
+
+// load dependencies
+dojo.require("dojo.event.*");
+dojo.require("dojo.dnd.*");
+// dojo.dnd.* doesn't include this package, because it's not in __package__.js
+dojo.require("dojo.dnd.HtmlDragMove");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html.layout");
+
+
+// summary
+//	Slider Widget.
+//	
+//	The slider widget comes in three forms:
+//	 1. Base Slider widget which supports movement in x and y dimensions
+//	 2. Vertical Slider (SliderVertical) widget which supports movement
+//	    only in the y dimension.
+//	 3. Horizontal Slider (SliderHorizontal) widget which supports movement
+//	    only in the x dimension.
+//
+//	The key objects in the widget are:
+//	 - a container div which displays a bar in the background (Slider object)
+//	 - a handle inside the container div, which represents the value
+//	   (sliderHandle DOM node)
+//	 - the object which moves the handle (_handleMove is of type 
+//	   SliderDragMoveSource)
+//
+//	The values for the slider are calculated by grouping pixels together, 
+//	based on the number of values to be represented by the slider.
+//	The number of pixels in a group is called the _valueSize
+//	 e.g. if slider is 150 pixels long, and is representing the values
+//	      0,1,...10 then pixels are grouped into lots of 15 (_valueSize), where:
+//	        value 0 maps to pixels  0 -  7
+//	              1                 8 - 22
+//	              2                23 - 37 etc.
+//	The accuracy of the slider is limited to the number of pixels
+//	(i.e tiles > pixels will result in the slider not being able to
+//	 represent some values).
+dojo.widget.defineWidget (
+	"dojo.widget.Slider",
+	dojo.widget.HtmlWidget,
+	{
+		// Number
+		//	minimum value to be represented by slider in the horizontal direction
+		minimumX: 0,
+		// Number
+		//	minimum value to be represented by slider in the vertical direction
+		minimumY: 0,
+		// Number
+		//	maximum value to be represented by slider in the horizontal direction
+		maximumX: 10,
+		// Number
+		//	maximum value to be represented by slider in the vertical direction
+		maximumY: 10,
+		// Number
+		//	number of values to be represented by slider in the horizontal direction
+		//	=0 means no snapping
+		snapValuesX: 0,
+		// Number
+		//	number of values to be represented by slider in the vertical direction
+		//	=0 means no snapping
+		snapValuesY: 0,
+		// should the handle snap to the grid or remain where it was dragged to?
+		// FIXME: snapToGrid=false is logically in conflict with setting snapValuesX and snapValuesY
+		_snapToGrid: true,
+		// Boolean
+		//	enables (disables) sliding in the horizontal direction
+		isEnableX: true,
+		// Boolean
+		//	enables (disables) sliding in the vertical direction
+		isEnableY: true,
+		// value size (pixels) in the x dimension
+		_valueSizeX: 0.0,
+		// value size (pixels) in the y dimension
+		_valueSizeY: 0.0,
+		// left most edge of constraining container (pixels) in the X dimension
+		_minX: 0,
+		// top most edge of constraining container (pixels) in the Y dimension
+		_minY: 0,
+		// constrained slider size (pixels) in the x dimension
+		_constraintWidth: 0,
+		// constrained slider size (pixels) in the y dimension
+		_constraintHeight: 0,
+		// progress image right clip value (pixels) in the X dimension
+		_clipLeft: 0,
+		// progress image left clip value (pixels) in the X dimension
+		_clipRight: 0,
+		// progress image top clip value (pixels) in the Y dimension
+		_clipTop: 0,
+		// progress image bottom clip value (pixels) in the Y dimension
+		_clipBottom: 0,
+		// half the size of the slider handle (pixels) in the X dimension
+		_clipXdelta: 0,
+		// half the size of the slider handle (pixels) in the Y dimension
+		_clipYdelta: 0,
+		// Number
+		//	initial value in the x dimension
+		initialValueX: 0,
+		// Number
+		//	initial value in the y dimension
+		initialValueY: 0,
+		// Boolean
+		//	values decrease in the X dimension
+		flipX: false,
+		// Boolean
+		//	values decrease in the Y dimension
+		flipY: false,
+		// Boolean
+		//	enables (disables) the user to click on the slider to set the position
+		clickSelect: true,
+		// Boolean
+		//	disables (enables) the value to change while you are dragging, or just after drag finishes
+		activeDrag: false,
+
+		templateCssPath: dojo.uri.dojoUri ("src/widget/templates/Slider.css"),
+		templatePath: dojo.uri.dojoUri ("src/widget/templates/Slider.html"),
+
+		// This is set to true when a drag is started, so that it is not confused
+		// with a click
+		_isDragInProgress: false,
+
+		// default user style attributes
+		// String
+		//	down arrow graphic URL
+		bottomButtonSrc: dojo.uri.dojoUri("src/widget/templates/images/slider_down_arrow.png"),
+		// String
+		//	up arrow graphic URL
+		topButtonSrc: dojo.uri.dojoUri("src/widget/templates/images/slider_up_arrow.png"),
+		// String
+		//	left arrow graphic URL
+		leftButtonSrc: dojo.uri.dojoUri("src/widget/templates/images/slider_left_arrow.png"),
+		// String
+		//	right arrow graphic URL
+		rightButtonSrc: dojo.uri.dojoUri("src/widget/templates/images/slider_right_arrow.png"),
+		// String
+		//	slider background graphic URL
+		backgroundSrc: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+		// String
+		//	slider background graphic URL to overlay the normal background to show progress
+		progressBackgroundSrc: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+		// String
+		//	sizing style attributes for the background image
+		backgroundSize: "width:200px;height:200px;",
+		// String
+		//	style attributes (other than sizing) for the background image
+		backgroundStyle: "",
+		// String
+		//	style attributes for the left and right arrow images
+		buttonStyleX: "",
+		// String
+		//	style attributes for the up and down arrow images
+		buttonStyleY: "",
+		// String
+		//	style attributes for the moveable slider image
+		handleStyle: "",
+		// String
+		//	moveable slider graphic URL
+		handleSrc: dojo.uri.dojoUri("src/widget/templates/images/slider-button.png"),
+		// Boolean
+		//	show (don't show) the arrow buttons
+		showButtons: true,
+		_eventCount: 0,
+		_typamaticTimer: null,
+		_typamaticFunction: null,
+		// Number
+		//	number of milliseconds before a held key or button becomes typematic 
+		defaultTimeout: 500,
+		// Number
+		//	fraction of time used to change the typematic timer between events
+		//	1.0 means that each typematic event fires at defaultTimeout intervals
+		//	< 1.0 means that each typematic event fires at an increasing faster rate
+		timeoutChangeRate: 0.90,
+		_currentTimeout: this.defaultTimeout,
+
+		// does the keyboard related stuff
+		_handleKeyEvents: function(/*Event*/ evt){
+			if(!evt.key){ return; }
+
+			if(!evt.ctrlKey && !evt.altKey){
+				switch(evt.key){
+					case evt.KEY_LEFT_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._leftButtonPressed(evt);
+						return;
+					case evt.KEY_RIGHT_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._rightButtonPressed(evt);
+						return;
+					case evt.KEY_DOWN_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._bottomButtonPressed(evt);
+						return;
+					case evt.KEY_UP_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._topButtonPressed(evt);
+						return;
+				}
+			}
+			this._eventCount++;
+
+		},
+
+		_pressButton: function(/*DomNode*/ buttonNode){
+			buttonNode.className = buttonNode.className.replace("Outset","Inset");
+		},
+
+		_releaseButton: function(/*DomNode*/ buttonNode){
+			buttonNode.className = buttonNode.className.replace("Inset","Outset");
+		},
+
+		_buttonPressed: function(/*Event*/ evt, /*DomNode*/ buttonNode){
+			this._setFocus();
+			if(typeof evt == "object"){
+				if(this._typamaticTimer != null){
+					if(this._typamaticNode == buttonNode){
+						return;
+					}
+					clearTimeout(this._typamaticTimer);
+				}
+				this._buttonReleased(null);
+				this._eventCount++;
+				this._typamaticTimer = null;
+				this._currentTimeout = this.defaultTimeout;
+				dojo.event.browser.stopEvent(evt);
+			}else if (evt != this._eventCount){
+				this._buttonReleased(null);
+				return false;
+			}
+			if (buttonNode == this.leftButtonNode && this.isEnableX){
+				this._snapX(dojo.html.getPixelValue (this.sliderHandleNode,"left") - this._valueSizeX);
+			}
+			else if (buttonNode == this.rightButtonNode && this.isEnableX){
+				this._snapX(dojo.html.getPixelValue (this.sliderHandleNode,"left") + this._valueSizeX);
+			}
+			else if (buttonNode == this.topButtonNode && this.isEnableY){
+				this._snapY(dojo.html.getPixelValue (this.sliderHandleNode,"top") - this._valueSizeY);
+			}
+			else if (buttonNode == this.bottomButtonNode && this.isEnableY){
+				this._snapY(dojo.html.getPixelValue (this.sliderHandleNode,"top") + this._valueSizeY);
+			}
+			else {
+				return false;
+			}
+			this._pressButton(buttonNode);
+			this.notifyListeners();
+			this._typamaticNode = buttonNode;
+			this._typamaticTimer = dojo.lang.setTimeout(this, "_buttonPressed", this._currentTimeout, this._eventCount, buttonNode);
+			this._currentTimeout = Math.round(this._currentTimeout * this.timeoutChangeRate);
+			return false;
+		},
+
+		_bottomButtonPressed: function(/*Event*/ evt){
+			return this._buttonPressed(evt,this.bottomButtonNode);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_bottomButtonDoubleClicked: function(/*Event*/ evt){
+			var rc = this._bottomButtonPressed(evt);
+			dojo.lang.setTimeout( this, "_buttonReleased", 50, null);
+			return rc;
+		},
+
+		_topButtonPressed: function(/*Event*/ evt){
+			return this._buttonPressed(evt,this.topButtonNode);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_topButtonDoubleClicked: function(/*Event*/ evt){
+			var rc = this._topButtonPressed(evt);
+			dojo.lang.setTimeout( this, "_buttonReleased", 50, null);
+			return rc;
+		},
+
+		_leftButtonPressed: function(/*Event*/ evt){
+			return this._buttonPressed(evt,this.leftButtonNode);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_leftButtonDoubleClicked: function(/*Event*/ evt){
+			var rc = this._leftButtonPressed(evt);
+			dojo.lang.setTimeout( this, "_buttonReleased", 50, null);
+			return rc;
+		},
+
+		_rightButtonPressed: function(/*Event*/ evt){
+			return this._buttonPressed(evt,this.rightButtonNode);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_rightButtonDoubleClicked: function(/*Event*/ evt){
+			var rc = this._rightButtonPressed(evt);
+			dojo.lang.setTimeout( this, "_buttonReleased", 50, null);
+			return rc;
+		},
+
+		_buttonReleased: function(/*Event*/ evt){
+			if(typeof evt == "object" && evt != null && typeof evt.keyCode != "undefined" && evt.keyCode != null){
+				var keyCode = evt.keyCode;
+
+				switch(keyCode){
+					case evt.KEY_LEFT_ARROW:
+					case evt.KEY_RIGHT_ARROW:
+					case evt.KEY_DOWN_ARROW:
+					case evt.KEY_UP_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						break;
+				}
+			}
+			this._releaseButton(this.topButtonNode);
+			this._releaseButton(this.bottomButtonNode);
+			this._releaseButton(this.leftButtonNode);
+			this._releaseButton(this.rightButtonNode);
+			this._eventCount++;
+			if(this._typamaticTimer != null){
+				clearTimeout(this._typamaticTimer);
+			}
+			this._typamaticTimer = null;
+			this._currentTimeout = this.defaultTimeout;
+		},
+
+		_mouseWheeled: function(/*Event*/ evt){
+			var scrollAmount = 0;
+			if(typeof evt.wheelDelta == 'number'){ // IE
+				scrollAmount = evt.wheelDelta;
+			}else if (typeof evt.detail == 'number'){ // Mozilla+Firefox
+				scrollAmount = -evt.detail;
+			}
+			if (this.isEnableY){
+				if(scrollAmount > 0){
+					this._topButtonPressed(evt);
+					this._buttonReleased(evt);
+				}else if (scrollAmount < 0){
+					this._bottomButtonPressed(evt);
+					this._buttonReleased(evt);
+				}
+			} else if (this.isEnableX){
+				if(scrollAmount > 0){
+					this._rightButtonPressed(evt);
+					this._buttonReleased(evt);
+				}else if (scrollAmount < 0){
+					this._leftButtonPressed(evt);
+					this._buttonReleased(evt);
+				}
+			}
+		},
+
+		_discardEvent: function(/*Event*/ evt){
+			dojo.event.browser.stopEvent(evt);
+		},
+
+		_setFocus: function(){
+			if (this.focusNode.focus){
+				this.focusNode.focus();
+			}
+		},
+
+		// This function is called when the template is loaded
+		fillInTemplate: function (/*Object*/ args, /*Object*/ frag) 
+		{
+			var source = this.getFragNodeRef(frag);
+			dojo.html.copyStyle(this.domNode, source);
+			// the user's style for the widget might include border and padding
+			// unfortunately, border isn't supported for inline elements
+			// so I get to fake everyone out by setting the border and padding
+			// of the outer table cells
+			var padding = this.domNode.style.padding;
+			if (dojo.lang.isString(padding) && padding != "" && padding != "0px" && padding != "0px 0px 0px 0px"){
+				this.topBorderNode.style.padding = 
+					this.bottomBorderNode.style.padding = padding;
+				this.topBorderNode.style.paddingBottom = "0px";
+				this.bottomBorderNode.style.paddingTop = "0px";
+				this.rightBorderNode.style.paddingRight = this.domNode.style.paddingRight;
+				this.leftBorderNode.style.paddingLeft= this.domNode.style.paddingLeft;
+				this.domNode.style.padding = "0px 0px 0px 0px";
+			}
+			var borderWidth = this.domNode.style.borderWidth;
+			if (dojo.lang.isString(borderWidth) && borderWidth != "" && borderWidth != "0px" && borderWidth != "0px 0px 0px 0px"){
+				this.topBorderNode.style.borderStyle = 
+					this.rightBorderNode.style.borderStyle = 
+					this.bottomBorderNode.style.borderStyle = 
+					this.leftBorderNode.style.borderStyle = 
+						this.domNode.style.borderStyle;
+				this.topBorderNode.style.borderColor = 
+					this.rightBorderNode.style.borderColor = 
+					this.bottomBorderNode.style.borderColor = 
+					this.leftBorderNode.style.borderColor = 
+						this.domNode.style.borderColor;
+				this.topBorderNode.style.borderWidth = 
+					this.bottomBorderNode.style.borderWidth = borderWidth;
+				this.topBorderNode.style.borderBottomWidth = "0px";
+				this.bottomBorderNode.style.borderTopWidth = "0px";
+				this.rightBorderNode.style.borderRightWidth = this.domNode.style.borderRightWidth;
+				this.leftBorderNode.style.borderLeftWidth = this.domNode.style.borderLeftWidth;
+				this.domNode.style.borderWidth = "0px 0px 0px 0px";
+			}
+
+			// dojo.debug ("fillInTemplate - className = " + this.domNode.className);
+
+			// setup drag-n-drop for the sliderHandle
+			this._handleMove = new dojo.widget._SliderDragMoveSource (this.sliderHandleNode);
+			this._handleMove.setParent (this);
+
+			if (this.clickSelect){
+				dojo.event.connect (this.constrainingContainerNode, "onmousedown", this, "_onClick");
+			} 
+
+			if (this.isEnableX){
+				this.setValueX (!isNaN(this.initialValueX) ? this.initialValueX : (!isNaN(this.minimumX) ? this.minimumX : 0));
+			}
+			if (!this.isEnableX || !this.showButtons){
+				this.rightButtonNode.style.width = "1px"; // allow the border to show
+				this.rightButtonNode.style.visibility = "hidden";
+				this.leftButtonNode.style.width = "1px"; // allow the border to show
+				this.leftButtonNode.style.visibility = "hidden";
+			}
+			if (this.isEnableY){
+				this.setValueY (!isNaN(this.initialValueY) ? this.initialValueY : (!isNaN(this.minimumY) ? this.minimumY : 0));
+			}
+			if (!this.isEnableY || !this.showButtons){
+				this.bottomButtonNode.style.width = "1px"; // allow the border to show
+				this.bottomButtonNode.style.visibility = "hidden";
+				this.topButtonNode.style.width = "1px"; // allow the border to show
+				this.topButtonNode.style.visibility = "hidden";
+			}
+			if(this.focusNode.addEventListener){
+				// dojo.event.connect() doesn't seem to work with DOMMouseScroll
+				this.focusNode.addEventListener('DOMMouseScroll', dojo.lang.hitch(this, "_mouseWheeled"), false); // Mozilla + Firefox + Netscape
+			}
+		},
+
+		// move the X value to the closest allowable value
+		_snapX: function(/*Number*/ x){
+			if (x < 0){ x = 0; }
+			else if (x > this._constraintWidth){ x = this._constraintWidth; }
+			else {
+				var selectedValue = Math.round (x / this._valueSizeX);
+				x = Math.round (selectedValue * this._valueSizeX);
+			}
+			this.sliderHandleNode.style.left = x + "px";
+			if (this.flipX){
+				this._clipLeft = x + this._clipXdelta;
+			} else {
+				this._clipRight = x + this._clipXdelta;
+			}
+			this.progressBackgroundNode.style.clip = "rect("+this._clipTop+"px,"+this._clipRight+"px,"+this._clipBottom+"px,"+this._clipLeft+"px)";
+		},
+
+		// compute _valueSizeX & _constraintWidth & default snapValuesX
+		_calc_valueSizeX: function (){
+			var constrainingCtrBox = dojo.html.getContentBox(this.constrainingContainerNode);
+			var sliderHandleBox = dojo.html.getContentBox(this.sliderHandleNode);
+			if (isNaN(constrainingCtrBox.width) || isNaN(sliderHandleBox.width) || constrainingCtrBox.width <= 0 || sliderHandleBox.width <= 0){ 
+				return false; 
+			}
+
+			this._constraintWidth = constrainingCtrBox.width 
+				+ dojo.html.getPadding(this.constrainingContainerNode).width
+				- sliderHandleBox.width;
+
+			if (this.flipX){
+				this._clipLeft = this._clipRight = constrainingCtrBox.width;
+			} else {
+				this._clipLeft = this._clipRight = 0;
+			}
+			this._clipXdelta = sliderHandleBox.width >> 1;
+			if (!this.isEnableY){
+				this._clipTop = 0;
+				this._clipBottom = constrainingCtrBox.height;
+			}
+
+			if (this._constraintWidth <= 0){ return false; }
+			if (this.snapValuesX == 0){
+				this.snapValuesX = this._constraintWidth + 1;
+			}
+
+			this._valueSizeX = this._constraintWidth / (this.snapValuesX - 1);
+			return true;
+		},
+
+		// summary
+		//	move the handle horizontally to the specified value
+		setValueX: function (/*Number*/ value){
+			if (0.0 == this._valueSizeX){
+				if (this._calc_valueSizeX () == false){
+					dojo.lang.setTimeout(this, "setValueX", 100, value);
+					return;
+				}
+			}
+			if (isNaN(value)){
+				value = 0;
+			}
+			if (value > this.maximumX){
+				value = this.maximumX;
+			}
+			else if (value < this.minimumX){
+				value = this.minimumX;
+			}
+			var pixelPercent = (value-this.minimumX) / (this.maximumX-this.minimumX);
+			if (this.flipX){
+				pixelPercent = 1.0 - pixelPercent;
+			}
+			this._snapX (pixelPercent * this._constraintWidth);
+			this.notifyListeners();
+		},
+
+
+		// summary
+		//	return the X value that the matches the position of the handle
+		getValueX: function (){
+			var pixelPercent = dojo.html.getPixelValue (this.sliderHandleNode,"left") / this._constraintWidth;
+			if (this.flipX){
+				pixelPercent = 1.0 - pixelPercent;
+			}
+			return Math.round (pixelPercent * (this.snapValuesX-1)) * ((this.maximumX-this.minimumX) / (this.snapValuesX-1)) + this.minimumX;
+		},
+
+		// move the Y value to the closest allowable value
+		_snapY: function (/*Number*/ y){
+			if (y < 0){ y = 0; }
+			else if (y > this._constraintHeight){ y = this._constraintHeight; }
+			else {
+				var selectedValue = Math.round (y / this._valueSizeY);
+				y = Math.round (selectedValue * this._valueSizeY);
+			}
+			this.sliderHandleNode.style.top = y + "px";
+			if (this.flipY){
+				this._clipTop = y + this._clipYdelta;
+			} else {
+				this._clipBottom = y + this._clipYdelta;
+			}
+			this.progressBackgroundNode.style.clip = "rect("+this._clipTop+"px,"+this._clipRight+"px,"+this._clipBottom+"px,"+this._clipLeft+"px)";
+		},
+		// compute _valueSizeY & _constraintHeight & default snapValuesY
+		_calc_valueSizeY: function (){
+			var constrainingCtrBox = dojo.html.getContentBox(this.constrainingContainerNode);
+			var sliderHandleBox = dojo.html.getContentBox(this.sliderHandleNode);
+			if (isNaN(constrainingCtrBox.height) || isNaN(sliderHandleBox.height) || constrainingCtrBox.height <= 0 || sliderHandleBox.height <= 0){ 
+				return false; 
+			}
+
+			this._constraintHeight = constrainingCtrBox.height
+				+ dojo.html.getPadding(this.constrainingContainerNode).height
+				- sliderHandleBox.height;
+
+			if (this.flipY){
+				this._clipTop = this._clipBottom = constrainingCtrBox.height;
+			} else {
+				this._clipTop = this._clipBottom = 0;
+			}
+			this._clipYdelta = sliderHandleBox.height >> 1;
+			if (!this.isEnableX){
+				this._clipLeft = 0;
+				this._clipRight = constrainingCtrBox.width;
+			}
+
+			if (this._constraintHeight <= 0){ return false; }
+			if (this.snapValuesY == 0){
+				this.snapValuesY = this._constraintHeight + 1;
+			}
+
+			this._valueSizeY = this._constraintHeight / (this.snapValuesY - 1);
+			return true;
+		},
+
+		// summary
+		//	move the handle vertically to the specified value
+		setValueY: function (/*Number*/ value){
+			if (0.0 == this._valueSizeY){
+				if (this._calc_valueSizeY () == false){
+					dojo.lang.setTimeout(this, "setValueY", 100, value);
+					return;
+				}
+			}
+			if (isNaN(value)){
+				value = 0;
+			}
+			if (value > this.maximumY){
+				value = this.maximumY;
+			}
+			else if (value < this.minimumY){
+				value = this.minimumY;
+			}
+			var pixelPercent = (value-this.minimumY) / (this.maximumY-this.minimumY);
+			if (this.flipY){
+				pixelPercent = 1.0 - pixelPercent;
+			}
+			this._snapY (pixelPercent * this._constraintHeight);
+			this.notifyListeners();
+		},
+
+		// summary
+		//	return the Y value that the matches the position of the handle
+		getValueY: function (){
+			var pixelPercent = dojo.html.getPixelValue (this.sliderHandleNode,"top") / this._constraintHeight;
+			if (this.flipY){
+				pixelPercent = 1.0 - pixelPercent;
+			}
+			return Math.round (pixelPercent * (this.snapValuesY-1)) * ((this.maximumY-this.minimumY) / (this.snapValuesY-1)) + this.minimumY;
+		},
+
+		// set the position of the handle
+		_onClick: function(/*Event*/ evt){
+			if (this._isDragInProgress){
+				return;
+			}
+
+			var parent = dojo.html.getAbsolutePosition(this.constrainingContainerNode, true, dojo.html.boxSizing.MARGIN_BOX);
+			var content = dojo.html.getContentBox(this._handleMove.domNode);			
+			if (this.isEnableX){
+				var x = evt.pageX - parent.x - (content.width >> 1);
+				this._snapX(x);
+			}
+			if (this.isEnableY){
+				var y = evt.pageY - parent.y - (content.height >> 1);
+				this._snapY(y);
+			}
+			this.notifyListeners();
+		},
+
+		// summary
+		//	method to invoke user's onValueChanged method
+		notifyListeners: function(){
+			this.onValueChanged(this.getValueX(), this.getValueY());
+		},
+
+		// summary
+		//	empty method to be overridden by user
+		onValueChanged: function(/*Number*/ x, /*Number*/ y){
+		}
+	}
+);
+
+
+
+// summary
+//	the horizontal slider widget subclass
+dojo.widget.defineWidget (
+	"dojo.widget.SliderHorizontal",
+	dojo.widget.Slider,
+	{
+		widgetType: "SliderHorizontal",
+
+		isEnableX: true,
+		isEnableY: false,
+		// Number
+		//	sets initialValueX
+		initialValue: "",
+		// Number
+		//	sets snapValuesX
+		snapValues: "",
+		// Number
+		//	sets minimumX
+		minimum: "",
+		// Number
+		//	sets maximumX
+		maximum: "",
+		// String
+		//	sets buttonStyleX
+		buttonStyle: "",
+		backgroundSize: "height:10px;width:200px;",
+		backgroundSrc: dojo.uri.dojoUri("src/widget/templates/images/slider-bg.gif"),
+		// Boolean
+		//	sets flipX
+		flip: false,
+
+		postMixInProperties: function(){
+			dojo.widget.SliderHorizontal.superclass.postMixInProperties.apply(this, arguments);
+			if (!isNaN(parseFloat(this.initialValue))){
+				this.initialValueX = parseFloat(this.initialValue);
+			}
+			if (!isNaN(parseFloat(this.minimum))){
+				this.minimumX = parseFloat(this.minimum);
+			}
+			if (!isNaN(parseFloat(this.maximum))){
+				this.maximumX = parseFloat(this.maximum);
+			}
+			if (!isNaN(parseInt(this.snapValues))){
+				this.snapValuesX = parseInt(this.snapValues);
+			}
+			if (dojo.lang.isString(this.buttonStyle) && this.buttonStyle != ""){
+				this.buttonStyleX = this.buttonStyle;
+			}
+			if (dojo.lang.isBoolean(this.flip)){
+				this.flipX = this.flip;
+			}
+		},
+
+		notifyListeners: function(){
+			this.onValueChanged(this.getValueX());
+		},
+
+		// summary
+		//	wrapper for getValueX()
+		getValue: function (){
+			return this.getValueX ();
+		},
+
+		// summary
+		//	wrapper for setValueX()
+		setValue: function (/*Number*/ value){
+			this.setValueX (value);
+		},
+
+		onValueChanged: function(/*Number*/ value){
+		}
+	}
+);
+
+
+/* ------------------------------------------------------------------------- */
+
+
+// summary
+//	the vertical slider widget subclass
+dojo.widget.defineWidget (
+	"dojo.widget.SliderVertical",
+	dojo.widget.Slider,
+	{
+		widgetType: "SliderVertical",
+
+		isEnableX: false,
+		isEnableY: true,
+		// Number
+		//	sets initialValueY
+		initialValue: "",
+		// Number
+		//	sets snapValuesY
+		snapValues: "",
+		// Number
+		//	sets minimumY
+		minimum: "",
+		// Number
+		//	sets maximumY
+		maximum: "",
+		// String
+		//	sets buttonStyleY
+		buttonStyle: "",
+		backgroundSize: "width:10px;height:200px;",
+		backgroundSrc: dojo.uri.dojoUri("src/widget/templates/images/slider-bg-vert.gif"),
+		// Boolean
+		//	sets flipY
+		flip: false,
+
+		postMixInProperties: function(){
+			dojo.widget.SliderVertical.superclass.postMixInProperties.apply(this, arguments);
+			if (!isNaN(parseFloat(this.initialValue))){
+				this.initialValueY = parseFloat(this.initialValue);
+			}
+			if (!isNaN(parseFloat(this.minimum))){
+				this.minimumY = parseFloat(this.minimum);
+			}
+			if (!isNaN(parseFloat(this.maximum))){
+				this.maximumY = parseFloat(this.maximum);
+			}
+			if (!isNaN(parseInt(this.snapValues))){
+				this.snapValuesY = parseInt(this.snapValues);
+			}
+			if (dojo.lang.isString(this.buttonStyle) && this.buttonStyle != ""){
+				this.buttonStyleY = this.buttonStyle;
+			}
+			if (dojo.lang.isBoolean(this.flip)){
+				this.flipY = this.flip;
+			}
+		},
+
+		notifyListeners: function(){
+			this.onValueChanged(this.getValueY());
+		},
+
+		// summary
+		//	wrapper for getValueY()
+		getValue: function (){
+			return this.getValueY ();
+		},
+
+		// summary
+		//	wrapper for setValueY()
+		setValue: function (/*Number*/ value){
+			this.setValueY (value);
+		},
+
+		onValueChanged: function(/*Number*/ value){
+		}
+	}
+);
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * This class extends the HtmlDragMoveSource class to provide
+ * features for the slider handle.
+ */
+dojo.declare (
+	"dojo.widget._SliderDragMoveSource",
+	dojo.dnd.HtmlDragMoveSource,
+{
+	slider: null,
+
+	/** Setup the handle for drag
+	 *  Extends dojo.dnd.HtmlDragMoveSource by creating a SliderDragMoveSource */
+	onDragStart: function(/*Event*/ evt){
+		this.slider._isDragInProgress = true;
+		var pos = dojo.html.getAbsolutePosition(this.slider.constrainingContainerNode, true, dojo.html.boxSizing.MARGIN_BOX);
+		if (this.slider.isEnableX){
+			this.slider._minX = pos.x;
+		}
+		if (this.slider.isEnableY){
+			this.slider._minY = pos.y;
+		}
+
+		var dragObj = this.createDragMoveObject ();
+
+		this.slider.notifyListeners();
+		return dragObj;
+	},
+
+	onDragEnd: function(/*Event*/ evt){
+		this.slider._isDragInProgress = false;
+		this.slider.notifyListeners();
+	},
+
+	createDragMoveObject: function (){
+		//dojo.debug ("SliderDragMoveSource#createDragMoveObject - " + this.slider);
+		var dragObj = new dojo.widget._SliderDragMoveObject (this.dragObject, this.type);
+		dragObj.slider = this.slider;
+
+		// this code copied from dojo.dnd.HtmlDragSource#onDragStart
+		if (this.dragClass){ 
+			dragObj.dragClass = this.dragClass; 
+		}
+
+		return dragObj;
+	},
+
+
+	setParent: function (/*Widget*/ slider){
+		this.slider = slider;
+	}
+});
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * This class extends the HtmlDragMoveObject class to provide
+ * features for the slider handle.
+ */
+dojo.declare (
+	"dojo.widget._SliderDragMoveObject",
+	dojo.dnd.HtmlDragMoveObject,
+{
+	// reference to dojo.widget.Slider
+	slider: null,
+
+	/** Moves the node to follow the mouse.
+	 *  Extends functon HtmlDragObject by adding functionality to snap handle
+	 *  to a discrete value */
+	onDragMove: function(/*Event*/ evt){
+		this.updateDragOffset ();
+
+		if (this.slider.isEnableX){
+			var x = this.dragOffset.x + evt.pageX - this.slider._minX;
+			this.slider._snapX(x);
+		}
+
+		if (this.slider.isEnableY){
+			var y = this.dragOffset.y + evt.pageY - this.slider._minY;
+			this.slider._snapY(y);
+		}
+		if(this.slider.activeDrag){
+			this.slider.notifyListeners();
+		}
+	}
+});

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SortableTable.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SortableTable.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SortableTable.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/SortableTable.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,605 @@
+dojo.provide("dojo.widget.SortableTable");
+
+dojo.deprecated("SortableTable will be removed in favor of FilteringTable.", "0.5");
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.date.format");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.util");
+dojo.require("dojo.html.style");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.defineWidget(
+	"dojo.widget.SortableTable",
+	dojo.widget.HtmlWidget,
+	function(){
+		this.data=[];
+		this.selected=[];		//	always an array to handle multiple selections.
+		this.columns=[];
+	},
+	{
+		//	custom properties
+		enableMultipleSelect: false,
+		maximumNumberOfSelections: 0,	//	0 for unlimited, is the default.
+		enableAlternateRows: false,
+		minRows: 0,	//	0 means ignore.
+		defaultDateFormat: "%D",
+		sortIndex: 0,		//	index of the column sorted on, first is the default.
+		sortDirection: 0,	//	0==asc, 1==desc
+		valueField: "Id",	//	if a JSON structure is parsed and there is a field of this name,
+							//	a value attribute will be added to the row (tr value="{Id}")
+
+		headClass: "",
+		tbodyClass: "",
+		headerClass: "",
+		headerSortUpClass: "selected",
+		headerSortDownClass: "selected",
+		rowClass: "",
+		rowAlternateClass: "alt",
+		rowSelectedClass: "selected",
+		columnSelected: "sorted-column",
+
+		isContainer: false,
+		templatePath:null,
+		templateCssPath:null,
+
+		getTypeFromString:function(/* string */ s){
+			//	summary
+			//	Find the constructor that matches param s by searching through the entire object tree.
+			var parts=s.split("."),i=0,obj=dj_global; 
+			do{obj=obj[parts[i++]];}while(i<parts.length&&obj); 
+			return(obj!=dj_global)?obj:null;	//	function
+		},
+		compare:function(/* object */ o1, /* object */ o2){
+			//	summary
+			//	Compare two objects using a shallow property compare
+			for(var p in o1){
+				if(!(p in o2)) return false;	//	boolean
+				if(o1[p].valueOf()!=o2[p].valueOf()) return false;	//	boolean
+			}
+			return true;	// boolean
+		},
+		isSelected:function(/* object */ o){
+			//	summary
+			//	checked to see if the passed object is in the current selection.
+			for(var i=0;i<this.selected.length;i++){
+				if(this.compare(this.selected[i],o)){
+					return true; // boolean
+				}
+			}
+			return false;	// boolean
+		},
+		removeFromSelected:function(/* object */ o){
+			//	summary
+			//	remove the passed object from the current selection.
+			var idx=-1;
+			for(var i=0;i<this.selected.length;i++){
+				if(this.compare(this.selected[i],o)){
+					idx=i;
+					break;
+				}
+			}
+			if(idx>=0){
+				this.selected.splice(idx,1);
+			}
+		},
+		getSelection:function(){
+			//	summary
+			//	return the array of currently selected objects (JSON format)
+			return this.selected;	//	array
+		},
+		getValue:function(){
+			//	summary
+			//	return a comma-delimited list of selected valueFields.
+			var a=[];
+			for(var i=0;i<this.selected.length;i++){
+				if (this.selected[i][this.valueField]){
+					a.push(this.selected[i][this.valueField]);
+				}
+			}
+			return a.join();	//	string
+		},
+		reset:function(){
+			//	summary
+			//	completely resets the internal representations.
+			this.columns=[];
+			this.data=[];
+			this.resetSelections(this.domNode.getElementsByTagName("tbody")[0]);
+		},
+		resetSelections:function(/* HTMLTableBodyElement */ body){
+			this.selected=[];
+			var idx=0;
+			var rows=body.getElementsByTagName("tr");
+			for(var i=0; i<rows.length; i++){
+				if(rows[i].parentNode==body){
+					rows[i].removeAttribute("selected");
+					if(this.enableAlternateRows&&idx%2==1){
+						rows[i].className=this.rowAlternateClass;
+					}else{
+						rows[i].className="";
+					}
+					idx++;
+				}
+			}
+		},
+
+		getObjectFromRow:function(/* HTMLTableRowElement */ row){
+			//	summary
+			//	creates a JSON object based on the passed row
+			var cells=row.getElementsByTagName("td");
+			var o={};
+			for(var i=0; i<this.columns.length;i++){
+				if(this.columns[i].sortType=="__markup__"){
+					//	FIXME: should we parse this instead?  Because if the user may not get back the markup they put in...
+					o[this.columns[i].getField()]=cells[i].innerHTML;
+				}else{
+					var text=dojo.html.renderedTextContent(cells[i]);
+					var val=text;
+					if (this.columns[i].getType() != String){
+						var val=new (this.columns[i].getType())(text);
+					}
+					o[this.columns[i].getField()]=val;
+				}
+			}
+			if(dojo.html.hasAttribute(row,"value")){
+				o[this.valueField]=dojo.html.getAttribute(row,"value");
+			}
+			return o;	//	object
+		},
+		setSelectionByRow:function(/* HTMLTableElementRow */ row){
+			//	summary
+			//	create the selection object based on the passed row, makes sure it's unique.
+			//	note that you need to call render manually (because of multi-select operations)
+			var o=this.getObjectFromRow(row);
+			var b=false;
+			for(var i=0;i<this.selected.length;i++){
+				if(this.compare(this.selected[i], o)){
+					b=true;
+					break;
+				}
+			}
+			if(!b){
+				this.selected.push(o);
+			}
+		},
+
+		parseColumns:function(/* HTMLTableHeadElement */ node){
+			//	summary
+			//	parses the passed element to create column objects
+			this.reset();
+			var row=node.getElementsByTagName("tr")[0];
+			var cells=row.getElementsByTagName("td");
+			if (cells.length==0) cells=row.getElementsByTagName("th");
+			for(var i=0; i<cells.length; i++){
+				var o={
+					field:null,
+					format:null,
+					noSort:false,
+					sortType:"String",
+					dataType:String,
+					sortFunction:null,
+					label:null,
+					align:"left",
+					valign:"middle",
+					getField:function(){ return this.field||this.label; },
+					getType:function(){ return this.dataType; }
+				};
+				//	presentation attributes
+				if(dojo.html.hasAttribute(cells[i], "align")){
+					o.align=dojo.html.getAttribute(cells[i],"align");
+				}
+				if(dojo.html.hasAttribute(cells[i], "valign")){
+					o.valign=dojo.html.getAttribute(cells[i],"valign");
+				}
+
+				//	sorting features.
+				if(dojo.html.hasAttribute(cells[i], "nosort")){
+					o.noSort=dojo.html.getAttribute(cells[i],"nosort")=="true";
+				}
+				if(dojo.html.hasAttribute(cells[i], "sortusing")){
+					var trans=dojo.html.getAttribute(cells[i],"sortusing");
+					var f=this.getTypeFromString(trans);
+					if (f!=null && f!=window && typeof(f)=="function") 
+						o.sortFunction=f;
+				}
+
+				if(dojo.html.hasAttribute(cells[i], "field")){
+					o.field=dojo.html.getAttribute(cells[i],"field");
+				}
+				if(dojo.html.hasAttribute(cells[i], "format")){
+					o.format=dojo.html.getAttribute(cells[i],"format");
+				}
+				if(dojo.html.hasAttribute(cells[i], "dataType")){
+					var sortType=dojo.html.getAttribute(cells[i],"dataType");
+					if(sortType.toLowerCase()=="html"||sortType.toLowerCase()=="markup"){
+						o.sortType="__markup__";	//	always convert to "__markup__"
+						o.noSort=true;
+					}else{
+						var type=this.getTypeFromString(sortType);
+						if(type){
+							o.sortType=sortType;
+							o.dataType=type;
+						}
+					}
+				}
+				o.label=dojo.html.renderedTextContent(cells[i]);
+				this.columns.push(o);
+
+				//	check to see if there's a default sort, and set the properties necessary
+				if(dojo.html.hasAttribute(cells[i], "sort")){
+					this.sortIndex=i;
+					var dir=dojo.html.getAttribute(cells[i], "sort");
+					if(!isNaN(parseInt(dir))){
+						dir=parseInt(dir);
+						this.sortDirection=(dir!=0)?1:0;
+					}else{
+						this.sortDirection=(dir.toLowerCase()=="desc")?1:0;
+					}
+				}
+			}
+		},
+
+		parseData:function(/* array */ data){
+			//	summary
+			//	Parse the passed JSON data structure, and cast based on columns.
+			this.data=[];
+			this.selected=[];
+			for(var i=0; i<data.length; i++){
+				var o={};	//	new data object.
+				for(var j=0; j<this.columns.length; j++){
+					var field=this.columns[j].getField();
+					if(this.columns[j].sortType=="__markup__"){
+						o[field]=String(data[i][field]);
+					}else{
+						var type=this.columns[j].getType();
+						var val=data[i][field];
+						var t=this.columns[j].sortType.toLowerCase();
+						if(type == String) {
+							o[field]=val;
+						} else {
+							if(val!=null){
+								o[field]=new type(val);
+							}else{
+								o[field]=new type();	//	let it use the default.
+							}
+						}
+					}
+				}
+				//	check for the valueField if not already parsed.
+				if(data[i][this.valueField]&&!o[this.valueField]){
+					o[this.valueField]=data[i][this.valueField];
+				}
+				this.data.push(o);
+			}
+		}, 
+
+		parseDataFromTable:function(/* HTMLTableBodyElement */ tbody){
+			//	summary
+			//	parses the data in the tbody of a table to create a set of objects.
+			//	Will add objects to this.selected if an attribute 'selected="true"' is present on the row.
+			this.data=[];
+			this.selected=[];
+			var rows=tbody.getElementsByTagName("tr");
+			for(var i=0; i<rows.length; i++){
+				if(dojo.html.getAttribute(rows[i],"ignoreIfParsed")=="true"){
+					continue;
+				}
+				var o={};	//	new data object.
+				var cells=rows[i].getElementsByTagName("td");
+				for(var j=0; j<this.columns.length; j++){
+					var field=this.columns[j].getField();
+					if(this.columns[j].sortType=="__markup__"){
+						//	FIXME: parse this?
+						o[field]=cells[j].innerHTML;
+					}else{
+						var type=this.columns[j].getType();
+						var val=dojo.html.renderedTextContent(cells[j]); //	should be the same index as the column.
+						if(type == String){
+							o[field]=val;
+						} else {
+							if (val!=null){
+								o[field]=new type(val);
+							} else {
+								o[field]=new type();	//	let it use the default.
+							}
+						}
+					}
+				}
+				if(dojo.html.hasAttribute(rows[i],"value")&&!o[this.valueField]){
+					o[this.valueField]=dojo.html.getAttribute(rows[i],"value");
+				}
+				//	FIXME: add code to preserve row attributes in __metadata__ field?
+				this.data.push(o);
+				
+				//	add it to the selections if selected="true" is present.
+				if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+					this.selected.push(o);
+				}
+			}
+		},
+		
+		showSelections:function(){
+			var body=this.domNode.getElementsByTagName("tbody")[0];
+			var rows=body.getElementsByTagName("tr");
+			var idx=0;
+			for(var i=0; i<rows.length; i++){
+				if(rows[i].parentNode==body){
+					if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+						rows[i].className=this.rowSelectedClass;
+					} else {
+						if(this.enableAlternateRows&&idx%2==1){
+							rows[i].className=this.rowAlternateClass;
+						}else{
+							rows[i].className="";
+						}
+					}
+					idx++;
+				}
+			}
+		},
+		render:function(bDontPreserve){
+			//	summary
+			//	renders the table to the browser
+			var data=[];
+			var body=this.domNode.getElementsByTagName("tbody")[0];
+
+			if(!bDontPreserve){
+				//	rebuild data and selection
+				this.parseDataFromTable(body);
+			}
+
+			//	clone this.data for sorting purposes.
+			for(var i=0; i<this.data.length; i++){
+				data.push(this.data[i]);
+			}
+			
+			var col=this.columns[this.sortIndex];
+			if(!col.noSort){
+				var field=col.getField();
+				if(col.sortFunction){
+					var sort=col.sortFunction;
+				}else{
+					var sort=function(a,b){
+						if (a[field]>b[field]) return 1;
+						if (a[field]<b[field]) return -1;
+						return 0;
+					}
+				}
+				data.sort(sort);
+				if(this.sortDirection!=0) data.reverse();
+			}
+
+			//	build the table and pop it in.
+			while(body.childNodes.length>0) body.removeChild(body.childNodes[0]);
+			for(var i=0; i<data.length;i++){
+				var row=document.createElement("tr");
+				dojo.html.disableSelection(row);
+				if (data[i][this.valueField]){
+					row.setAttribute("value",data[i][this.valueField]);
+				}
+				if(this.isSelected(data[i])){
+					row.className=this.rowSelectedClass;
+					row.setAttribute("selected","true");
+				} else {
+					if(this.enableAlternateRows&&i%2==1){
+						row.className=this.rowAlternateClass;
+					}
+				}
+				for(var j=0;j<this.columns.length;j++){
+					var cell=document.createElement("td");
+					cell.setAttribute("align", this.columns[j].align);
+					cell.setAttribute("valign", this.columns[j].valign);
+					dojo.html.disableSelection(cell);
+					if(this.sortIndex==j){
+						cell.className=this.columnSelected;
+					}
+					if(this.columns[j].sortType=="__markup__"){
+						cell.innerHTML=data[i][this.columns[j].getField()];
+						for(var k=0; k<cell.childNodes.length; k++){
+							var node=cell.childNodes[k];
+							if(node&&node.nodeType==dojo.html.ELEMENT_NODE){
+								dojo.html.disableSelection(node);
+							}
+						}
+					}else{
+						if(this.columns[j].getType()==Date){
+							var format=this.defaultDateFormat;
+							if(this.columns[j].format) format=this.columns[j].format;
+							cell.appendChild(document.createTextNode(dojo.date.strftime(data[i][this.columns[j].getField()], format)));
+						}else{
+							cell.appendChild(document.createTextNode(data[i][this.columns[j].getField()]));
+						}
+					}
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+				dojo.event.connect(row, "onclick", this, "onUISelect");
+			}
+			
+			//	if minRows exist.
+			var minRows=parseInt(this.minRows);
+			if (!isNaN(minRows) && minRows>0 && data.length<minRows){
+				var mod=0;
+				if(data.length%2==0) mod=1;
+				var nRows=minRows-data.length;
+				for(var i=0; i<nRows; i++){
+					var row=document.createElement("tr");
+					row.setAttribute("ignoreIfParsed","true");
+					if(this.enableAlternateRows&&i%2==mod){
+						row.className=this.rowAlternateClass;
+					}
+					for(var j=0;j<this.columns.length;j++){
+						var cell=document.createElement("td");
+						cell.appendChild(document.createTextNode("\u00A0"));
+						row.appendChild(cell);
+					}
+					body.appendChild(row);
+				}
+			}
+		},
+
+		//	the following the user can override.
+		onSelect:function(/* DomEvent */ e){ 
+			//	summary
+			//	empty function for the user to attach code to, fired by onUISelect
+		},
+		onUISelect:function(/* DomEvent */ e){
+			//	summary
+			//	fired when a user selects a row
+			var row=dojo.html.getParentByType(e.target,"tr");
+			var body=dojo.html.getParentByType(row,"tbody");
+			if(this.enableMultipleSelect){
+				if(e.metaKey||e.ctrlKey){
+					if(this.isSelected(this.getObjectFromRow(row))){
+						this.removeFromSelected(this.getObjectFromRow(row));
+						row.removeAttribute("selected");
+					}else{
+						//	push onto the selection stack.
+						this.setSelectionByRow(row);
+						row.setAttribute("selected","true");
+					}
+				}else if(e.shiftKey){
+					//	the tricky one.  We need to figure out the *last* selected row above, 
+					//	and select all the rows in between.
+					var startRow;
+					var rows=body.getElementsByTagName("tr");
+					//	if there's a selection above, we go with that first. 
+					for(var i=0;i<rows.length;i++){
+						if(rows[i].parentNode==body){
+							if(rows[i]==row) break;
+							if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+								startRow=rows[i];
+							}
+						}
+					}
+					//	if there isn't a selection above, we continue with a selection below.
+					if(!startRow){
+						startRow=row;
+						for(;i<rows.length;i++){
+							if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+								row=rows[i];
+								break;
+							}
+						}
+					}
+					this.resetSelections(body);
+					if(startRow==row){
+						//	this is the only selection
+						row.setAttribute("selected","true");
+						this.setSelectionByRow(row);
+					}else{
+						var doSelect=false;
+						for(var i=0; i<rows.length; i++){
+							if(rows[i].parentNode==body){
+								rows[i].removeAttribute("selected");
+								if(rows[i]==startRow){
+									doSelect=true;
+								}
+								if(doSelect){
+									this.setSelectionByRow(rows[i]);
+									rows[i].setAttribute("selected","true");
+								}
+								if(rows[i]==row){
+									doSelect=false;
+								}
+							}
+						}
+					}
+				}else{
+					//	reset the selection
+					this.resetSelections(body);
+					row.setAttribute("selected","true");
+					this.setSelectionByRow(row);
+				}
+			}else{
+				//	reset the data selection and go.
+				this.resetSelections(body);
+				row.setAttribute("selected","true");
+				this.setSelectionByRow(row);
+			}
+			this.showSelections();
+			this.onSelect(e);
+			e.stopPropagation();
+			e.preventDefault();
+		},
+		onHeaderClick:function(/* DomEvent */ e){
+			//	summary
+			//	Main handler function for each header column click.
+			var oldIndex=this.sortIndex;
+			var oldDirection=this.sortDirection;
+			var source=e.target;
+			var row=dojo.html.getParentByType(source,"tr");
+			var cellTag="td";
+			if(row.getElementsByTagName(cellTag).length==0) cellTag="th";
+
+			var headers=row.getElementsByTagName(cellTag);
+			var header=dojo.html.getParentByType(source,cellTag);
+			
+			for(var i=0; i<headers.length; i++){
+				if(headers[i]==header){
+					if(i!=oldIndex){
+						//	new col.
+						this.sortIndex=i;
+						this.sortDirection=0;
+						headers[i].className=this.headerSortDownClass
+					}else{
+						this.sortDirection=(oldDirection==0)?1:0;
+						if(this.sortDirection==0){
+							headers[i].className=this.headerSortDownClass;
+						}else{
+							headers[i].className=this.headerSortUpClass;
+						}
+					}
+				}else{
+					//	reset the header class.
+					headers[i].className=this.headerClass;
+				}
+			}
+			this.render();
+		},
+
+		postCreate:function(){ 
+			// 	summary
+			//	overridden from HtmlWidget, initializes and renders the widget.
+			var thead=this.domNode.getElementsByTagName("thead")[0];
+			if(this.headClass.length>0){
+				thead.className=this.headClass;
+			}
+
+			//	disable selections
+			dojo.html.disableSelection(this.domNode);
+
+			//	parse the columns.
+			this.parseColumns(thead);
+
+			//	attach header handlers.
+			var header="td";
+			if(thead.getElementsByTagName(header).length==0) header="th";
+			var headers=thead.getElementsByTagName(header);
+			for(var i=0; i<headers.length; i++){
+				if(!this.columns[i].noSort){
+					dojo.event.connect(headers[i], "onclick", this, "onHeaderClick");
+				}
+				if(this.sortIndex==i){
+					if(this.sortDirection==0){
+						headers[i].className=this.headerSortDownClass;
+					}else{
+						headers[i].className=this.headerSortUpClass;
+					}
+				}
+			}
+
+			//	parse the tbody element and re-render it.
+			var tbody=this.domNode.getElementsByTagName("tbody")[0];
+			if (this.tbodyClass.length>0) {
+				tbody.className=this.tbodyClass;
+			}
+
+			this.parseDataFromTable(tbody);
+			this.render(true);
+		}
+	}
+);

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

Added: jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Spinner.js
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Spinner.js?view=auto&rev=473755
==============================================================================
--- jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Spinner.js (added)
+++ jackrabbit/trunk/contrib/jcr-browser/src/main/webapp/dojo/src/widget/Spinner.js Sat Nov 11 08:44:22 2006
@@ -0,0 +1,612 @@
+dojo.provide("dojo.widget.Spinner");
+
+dojo.require("dojo.io.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.IntegerTextbox");
+dojo.require("dojo.widget.RealNumberTextbox");
+dojo.require("dojo.widget.DateTextbox");
+
+dojo.require("dojo.experimental");
+
+// summary: Mixin for validation widgets with a spinner
+// description: This class basically (conceptually) extends dojo.widget.ValidationTextbox.
+//	It modifies the template to have up/down arrows, and provides related handling code.
+dojo.declare(
+	"dojo.widget.Spinner",
+	null, 
+	{
+		_typamaticTimer: null,
+		_typamaticFunction: null,
+		_currentTimeout: this.defaultTimeout,
+		_eventCount: 0,
+		// Number
+		//      number of milliseconds before a held key or button becomes typematic
+		defaultTimeout: 500,
+		// Number
+		//      fraction of time used to change the typematic timer between events
+		//      1.0 means that each typematic event fires at defaultTimeout intervals
+		//      < 1.0 means that each typematic event fires at an increasing faster rate
+		timeoutChangeRate: 0.90,
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/Spinner.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/Spinner.css"),
+		// String
+		//      up arrow graphic URL
+		incrementSrc: dojo.uri.dojoUri("src/widget/templates/images/spinnerIncrement.gif"),
+		// String
+		//      down arrow graphic URL
+		decrementSrc: dojo.uri.dojoUri("src/widget/templates/images/spinnerDecrement.gif"),
+
+		// does the keyboard related stuff
+		_handleKeyEvents: function(/*Event*/ evt){
+			if(!evt.key){ return; }
+
+			if(!evt.ctrlKey && !evt.altKey){
+			        switch(evt.key){
+					case evt.KEY_DOWN_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._downArrowPressed(evt);
+						return;
+					case evt.KEY_UP_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						this._upArrowPressed(evt);
+						return;
+				}
+			}
+			this._eventCount++;
+		},
+
+		_onSpinnerKeyUp: function(/*Event*/ evt){
+			this._arrowReleased(evt);
+			this.onkeyup(evt);
+		},
+
+		// reset button size; this function is called when the input area has changed size
+		_resize: function(){
+			var inputSize = dojo.html.getBorderBox(this.textbox);
+			this.buttonSize = { width: inputSize.height / 2, height: inputSize.height / 2 };
+			if(this.upArrowNode){
+				dojo.html.setMarginBox(this.upArrowNode, this.buttonSize);
+				dojo.html.setMarginBox(this.downArrowNode, this.buttonSize);
+			}
+		},
+
+		_pressButton: function(/*DomNode*/ node){
+			node.style.borderWidth = "1px 0px 0px 1px";
+			node.style.borderStyle = "inset";
+		},
+
+		_releaseButton: function(/*DomNode*/ node){
+			node.style.borderWidth = "0px 1px 1px 0px";
+			node.style.borderStyle = "outset";
+		},
+
+		_arrowPressed: function(/*Event*/ evt, /*Number*/ direction){
+			var nodePressed = (direction == -1) ? this.downArrowNode : this.upArrowNode;
+			var nodeReleased = (direction == +1) ? this.downArrowNode : this.upArrowNode;
+			if(typeof evt != "number"){
+				if(this._typamaticTimer != null){
+					if(this._typamaticNode == nodePressed){
+						return;
+					}
+					dojo.lang.clearTimeout(this._typamaticTimer);
+				}
+				this._releaseButton(nodeReleased);
+				this._eventCount++;
+				this._typamaticTimer = null;
+				this._currentTimeout = this.defaultTimeout;
+
+			}else if (evt != this._eventCount){
+				this._releaseButton(nodePressed);
+				return;
+			}
+			this._pressButton(nodePressed);
+			this._setCursorX(this.adjustValue(direction,this._getCursorX()));
+			this._typamaticNode = nodePressed;
+			this._typamaticTimer = dojo.lang.setTimeout(this, "_arrowPressed", this._currentTimeout, this._eventCount, direction);
+			this._currentTimeout = Math.round(this._currentTimeout * this.timeoutChangeRate);
+		},
+
+		_downArrowPressed: function(/*Event*/ evt){
+			return this._arrowPressed(evt,-1);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_downArrowDoubleClicked: function(/*Event*/ evt){
+			var rc = this._downArrowPressed(evt);
+			dojo.lang.setTimeout(this, "_arrowReleased", 50, null);
+			return rc;
+		},
+
+		_upArrowPressed: function(/*Event*/ evt){
+			return this._arrowPressed(evt,+1);
+		},
+
+		// IE sends these events when rapid clicking, mimic an extra single click
+		_upArrowDoubleClicked: function(/*Event*/ evt){
+			var rc = this._upArrowPressed(evt);
+			dojo.lang.setTimeout(this, "_arrowReleased", 50, null);
+			return rc;
+		},
+
+		_arrowReleased: function(/*Event*/ evt){
+			this.textbox.focus();
+			if(evt != null && typeof evt == "object" && evt.keyCode && evt.keyCode != null){
+				var keyCode = evt.keyCode;
+				var k = dojo.event.browser.keys;
+
+				switch(keyCode){
+					case k.KEY_DOWN_ARROW:
+					case k.KEY_UP_ARROW:
+						dojo.event.browser.stopEvent(evt);
+						break;
+				}
+			}
+			this._releaseButton(this.upArrowNode);
+			this._releaseButton(this.downArrowNode);
+			this._eventCount++;
+			if(this._typamaticTimer != null){
+				dojo.lang.clearTimeout(this._typamaticTimer);
+			}
+			this._typamaticTimer = null;
+			this._currentTimeout = this.defaultTimeout;
+		},
+
+		_mouseWheeled: function(/*Event*/ evt){
+			var scrollAmount = 0;
+			if(typeof evt.wheelDelta == 'number'){ // IE
+				scrollAmount = evt.wheelDelta;
+			}else if (typeof evt.detail == 'number'){ // Mozilla+Firefox
+				scrollAmount = -evt.detail;
+			}
+			if(scrollAmount > 0){
+				this._upArrowPressed(evt);
+				this._arrowReleased(evt);
+			}else if (scrollAmount < 0){
+				this._downArrowPressed(evt);
+				this._arrowReleased(evt);
+			}
+		},
+
+		_discardEvent: function(/*Event*/ evt){
+			dojo.event.browser.stopEvent(evt);
+		},
+
+		_getCursorX: function(){
+			var x = -1;
+			try{
+				this.textbox.focus();
+				if (typeof this.textbox.selectionEnd == "number"){
+					x = this.textbox.selectionEnd;
+				}else if (document.selection && document.selection.createRange){
+					var range = document.selection.createRange().duplicate();
+					if(range.parentElement() == this.textbox){
+						range.moveStart('textedit', -1);
+						x = range.text.length;
+					}
+				}
+			}catch(e){ /* squelch! */ }
+			return x;
+		},
+
+		_setCursorX: function(/*Number*/ x){
+			try{
+				this.textbox.focus();
+				if(!x){ x = 0; }
+				if(typeof this.textbox.selectionEnd == "number"){
+				this.textbox.selectionEnd = x;
+				}else if(this.textbox.createTextRange){
+				var range = this.textbox.createTextRange();
+				range.collapse(true);
+				range.moveEnd('character', x);
+				range.moveStart('character', x);
+				range.select();
+				}
+			}catch(e){ /* squelch! */ }
+		},
+
+		_spinnerPostMixInProperties: function(/*Object*/ args, /*Object*/ frag){
+			// summary: the widget's postMixInProperties() method should call this method
+
+			// set image size before instantiating template;
+			// changing it aftwards doesn't work on FF
+			var inputNode = this.getFragNodeRef(frag);
+			var inputSize = dojo.html.getBorderBox(inputNode);
+			this.buttonSize = { width: inputSize.height / 2 - 1, height: inputSize.height / 2 - 1};
+		},
+
+		_spinnerPostCreate: function(/*Object*/ args, /*Object*/ frag){
+			// summary: the widget's postCreate() method should call this method
+
+			// extra listeners
+			if(this.textbox.addEventListener){
+				// dojo.event.connect() doesn't seem to work with DOMMouseScroll
+				this.textbox.addEventListener('DOMMouseScroll', dojo.lang.hitch(this, "_mouseWheeled"), false); // Mozilla + Firefox + Netscape
+			}else{
+				dojo.event.connect(this.textbox, "onmousewheel", this, "_mouseWheeled"); // IE + Safari
+			}
+			//dojo.event.connect(window, "onchange", this, "_resize");
+		}
+	}
+);
+
+// summary
+//	create spinable, single integer, input field
+dojo.widget.defineWidget(
+	"dojo.widget.IntegerSpinner",
+	[dojo.widget.IntegerTextbox, dojo.widget.Spinner],
+{
+	// summary: an IntegerSpinner with +/- buttons
+
+	// Number
+	//	increment amount
+	delta: "1",
+
+	postMixInProperties: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.IntegerSpinner.superclass.postMixInProperties.apply(this, arguments);
+		this._spinnerPostMixInProperties(args, frag);
+	},
+
+	postCreate: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.IntegerSpinner.superclass.postCreate.apply(this, arguments);
+		this._spinnerPostCreate(args, frag);
+	},
+
+	// sumary
+	//	spin the input field
+	//	direction < 0: spin down
+	//	direction > 0: spin up
+	//	direction = 0: revalidate existing value
+	adjustValue: function(/*Number*/ direction, /*Number*/ x){
+		var val = this.getValue().replace(/[^\-+\d]/g, "");
+		if(val.length == 0){ return; }
+
+		var num = Math.min(Math.max((parseInt(val)+(parseInt(this.delta) * direction)), (this.flags.min?this.flags.min:-Infinity)), (this.flags.max?this.flags.max:+Infinity));
+		val = num.toString();
+
+		if(num >= 0){
+			val = ((this.flags.signed == true)?'+':' ')+val; // make sure first char is a nondigit
+		}
+
+		if(this.flags.separator.length > 0){
+			for (var i=val.length-3; i > 1; i-=3){
+				val = val.substr(0,i)+this.flags.separator+val.substr(i);
+			}
+		}
+
+		if(val.substr(0,1) == ' '){ val = val.substr(1); } // remove space
+
+		this.setValue(val);
+
+		return val.length;
+	}
+});
+
+/*
+  ****** RealNumberSpinner ******
+
+  A subclass of RealNumberTextbox.
+  @attr places    The exact number of decimal places.  If omitted, it's unlimited and optional.
+  @attr exponent  Can be true or false.  If omitted the exponential part is optional.
+  @attr eSigned   Is the exponent signed?  Can be true or false, if omitted the sign is optional.
+*/
+dojo.widget.defineWidget(
+	"dojo.widget.RealNumberSpinner",
+	[dojo.widget.RealNumberTextbox, dojo.widget.Spinner],
+	function(){ dojo.experimental("dojo.widget.RealNumberSpinner"); },
+{
+	// new subclass properties
+	delta: "1e1",
+
+	postMixInProperties: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.RealNumberSpinner.superclass.postMixInProperties.apply(this, arguments);
+		this._spinnerPostMixInProperties(args, frag);
+	},
+
+	postCreate: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.RealNumberSpinner.superclass.postCreate.apply(this, arguments);
+		this._spinnerPostCreate(args, frag);
+	},
+
+	adjustValue: function(/*Number*/ direction, /*Number*/ x){
+			var val = this.getValue().replace(/[^\-+\.eE\d]/g, "");
+			if(!val.length){ return; }
+
+			var num = parseFloat(val);
+			if(isNaN(num)){ return; }
+			var delta = this.delta.split(/[eE]/);
+			if(!delta.length){
+				delta = [1, 1];
+			}else{
+				delta[0] = parseFloat(delta[0].replace(/[^\-+\.\d]/g, ""));
+				if(isNaN(delta[0])){ delta[0] = 1; }
+				if(delta.length > 1){
+					delta[1] = parseInt(delta[1]);
+				}
+				if(isNaN(delta[1])){ delta[1] = 1; }
+			}
+			val = this.getValue().split(/[eE]/);
+			if(!val.length){ return; }
+			var numBase = parseFloat(val[0].replace(/[^\-+\.\d]/g, ""));
+			if(val.length == 1){
+				var numExp = 0;
+			}else{
+				var numExp = parseInt(val[1].replace(/[^\-+\d]/g, ""));
+			}
+			if(x <= val[0].length){
+				x = 0;
+				numBase += delta[0] * direction;
+			}else{
+				x = Number.MAX_VALUE;
+				numExp += delta[1] * direction;
+				if(this.flags.eSigned == false && numExp < 0){
+					numExp = 0;
+				}
+			}
+			num = Math.min(Math.max((numBase * Math.pow(10,numExp)), (this.flags.min?this.flags.min:-Infinity)), (this.flags.max?this.flags.max:+Infinity));
+			if((this.flags.exponent == true || (this.flags.exponent != false && x != 0)) && num.toExponential){
+				if (isNaN(this.flags.places) || this.flags.places == Infinity){
+					val = num.toExponential();
+				}else{
+					val = num.toExponential(this.flags.places);
+				}
+			}else if(num.toFixed && num.toPrecision){
+				if(isNaN(this.flags.places) || this.flags.places == Infinity){
+					val = num.toPrecision((1/3).toString().length-1);
+				}else{
+					val = num.toFixed(this.flags.places);
+				}
+			}else{
+				val = num.toString();
+			}
+
+			if(num >= 0){
+				if(this.flags.signed == true){
+					val = '+' + val;
+				}
+			}
+			val = val.split(/[eE]/);
+			if(this.flags.separator.length > 0){
+				if(num >= 0 && val[0].substr(0,1) != '+'){
+					val[0] = ' ' + val[0]; // make sure first char is nondigit for easy algorithm
+				}
+				var i = val[0].lastIndexOf('.');
+				if(i >= 0){
+					i -= 3;
+				}else{
+					i = val[0].length-3;
+				}
+				for (; i > 1; i-=3){
+					val[0] = val[0].substr(0,i)+this.flags.separator+val[0].substr(i);
+				}
+				if(val[0].substr(0,1) == ' '){ val[0] = val[0].substr(1); } // remove space
+			}
+			if(val.length > 1){
+				if((this.flags.eSigned == true)&&(val[1].substr(0,1) != '+')){
+					val[1] = '+' + val[1];
+				}else if((!this.flags.eSigned)&&(val[1].substr(0,1) == '+')){
+					val[1] = val[1].substr(1);
+				}else if((!this.flags.eSigned)&&(val[1].substr(0,1) == '-')&&(num.toFixed && num.toPrecision)){
+					if(isNaN(this.flags.places)){
+						val[0] = num.toPrecision((1/3).toString().length-1);
+					}else{
+						val[0] = num.toFixed(this.flags.places).toString();
+					}
+					val[1] = "0";
+				}
+				val[0] += 'e' + val[1];
+			}
+			this.setValue(val[0]);
+			if(x > val[0].length){ x = val[0].length; }
+			return x;
+	}
+});
+
+dojo.widget.defineWidget(
+	"dojo.widget.TimeSpinner",
+	[dojo.widget.TimeTextbox, dojo.widget.Spinner],
+	function(){ dojo.experimental("dojo.widget.TimeSpinner"); },
+{
+	postMixInProperties: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.TimeSpinner.superclass.postMixInProperties.apply(this, arguments);
+		this._spinnerPostMixInProperties(args, frag);
+	},
+
+	postCreate: function(/*Object*/ args, /*Object*/ frag){
+		dojo.widget.TimeSpinner.superclass.postCreate.apply(this, arguments);
+		this._spinnerPostCreate(args, frag);
+	},
+
+	adjustValue: function(/*Number*/ direction, /*Number*/ x){
+	//FIXME: formatting should make use of dojo.date.format?
+		var val = this.getValue();
+		var format = (this.flags.format && this.flags.format.search(/[Hhmst]/) >= 0) ? this.flags.format : "hh:mm:ss t";
+		if(direction == 0 || !val.length || !this.isValid()){ return; }
+		if (!this.flags.amSymbol){
+			this.flags.amSymbol = "AM";
+		}
+		if (!this.flags.pmSymbol){
+			this.flags.pmSymbol = "PM";
+		}
+		var re = dojo.regexp.time(this.flags);
+		var qualifiers = format.replace(/H/g,"h").replace(/[^hmst]/g,"").replace(/([hmst])\1/g,"$1");
+		var hourPos = qualifiers.indexOf('h') + 1;
+		var minPos = qualifiers.indexOf('m') + 1;
+		var secPos = qualifiers.indexOf('s') + 1;
+		var ampmPos = qualifiers.indexOf('t') + 1;
+		// tweak format to match the incoming data exactly to help find where the cursor is
+		var cursorFormat = format;
+		var ampm = "";
+		if (ampmPos > 0){
+			ampm = val.replace(new RegExp(re),"$"+ampmPos);
+			cursorFormat = cursorFormat.replace(/t+/, ampm.replace(/./g,"t"));
+		}
+		var hour = 0;
+		var deltaHour = 1;
+		if (hourPos > 0){
+			hour = val.replace(new RegExp(re),"$"+hourPos);
+			if (dojo.lang.isString(this.delta)){
+				deltaHour = this.delta.replace(new RegExp(re),"$"+hourPos);
+			}
+			if (isNaN(deltaHour)){
+				deltaHour = 1;
+			} else {
+				deltaHour = parseInt(deltaHour);
+			}
+			if (hour.length == 2){
+				cursorFormat = cursorFormat.replace(/([Hh])+/, "$1$1");
+			} else {
+				cursorFormat = cursorFormat.replace(/([Hh])+/, "$1");
+			}
+			if (isNaN(hour)){
+				hour = 0;
+			} else {
+				hour = parseInt(hour.replace(/^0(\d)/,"$1"));
+			}
+		}
+		var min = 0;
+		var deltaMin = 1;
+		if (minPos > 0){
+			min = val.replace(new RegExp(re),"$"+minPos);
+			if (dojo.lang.isString(this.delta)){
+				deltaMin = this.delta.replace(new RegExp(re),"$"+minPos);
+			}
+			if (isNaN(deltaMin)){
+				deltaMin = 1;
+			} else {
+				deltaMin = parseInt(deltaMin);
+			}
+			cursorFormat = cursorFormat.replace(/m+/, min.replace(/./g,"m"));
+			if (isNaN(min)){
+				min = 0;
+			} else {
+				min = parseInt(min.replace(/^0(\d)/,"$1"));
+			}
+		}
+		var sec = 0;
+		var deltaSec = 1;
+		if (secPos > 0){
+			sec = val.replace(new RegExp(re),"$"+secPos);
+			if (dojo.lang.isString(this.delta)){
+				deltaSec = this.delta.replace(new RegExp(re),"$"+secPos);
+			}
+			if (isNaN(deltaSec)){
+				deltaSec = 1;
+			} else {
+				deltaSec = parseInt(deltaSec);
+			}
+			cursorFormat = cursorFormat.replace(/s+/, sec.replace(/./g,"s"));
+			if (isNaN(sec)){
+				sec = 0;
+			} else {
+				sec = parseInt(sec.replace(/^0(\d)/,"$1"));
+			}
+		}
+		if (isNaN(x) || x >= cursorFormat.length){
+			x = cursorFormat.length-1;
+		}
+		var cursorToken = cursorFormat.charAt(x);
+
+		switch(cursorToken){
+			case 't':
+				if (ampm == this.flags.amSymbol){
+					ampm = this.flags.pmSymbol;
+				}
+				else if (ampm == this.flags.pmSymbol){
+					ampm = this.flags.amSymbol;
+				}
+				break;
+			default:
+				if (hour >= 1 && hour < 12 && ampm == this.flags.pmSymbol){
+					hour += 12;
+				}
+				if (hour == 12 && ampm == this.flags.amSymbol){
+					hour = 0;
+				}
+				switch(cursorToken){
+					case 's':
+						sec += deltaSec * direction;
+						while (sec < 0){
+							min--;
+							sec += 60;
+						}
+						while (sec >= 60){
+							min++;
+							sec -= 60;
+						}
+					case 'm':
+						if (cursorToken == 'm'){
+							min += deltaMin * direction;
+						}
+						while (min < 0){
+							hour--;
+							min += 60;
+						}
+						while (min >= 60){
+							hour++;
+							min -= 60;
+						}
+					case 'h':
+					case 'H':
+						if (cursorToken == 'h' || cursorToken == 'H'){
+							hour += deltaHour * direction;
+						}
+						while (hour < 0){
+							hour += 24;
+						}
+						while (hour >= 24){
+							hour -= 24;
+						}
+						break;
+					default: // should never get here
+						return;
+				}
+				if (hour >= 12){
+					ampm = this.flags.pmSymbol;
+					if (format.indexOf('h') >= 0 && hour >= 13){
+						hour -= 12;
+					}
+				} else {
+					ampm = this.flags.amSymbol;
+					if (format.indexOf('h') >= 0 && hour == 0){
+						hour = 12;
+					}
+				}
+		}
+
+		cursorFormat = format;
+		if (hour >= 0 && hour < 10 && format.search(/[hH]{2}/) >= 0){
+			hour = "0" + hour.toString();
+		}
+		if (hour >= 10 && cursorFormat.search(/[hH]{2}/) < 0 ){
+			cursorFormat = cursorFormat.replace(/(h|H)/, "$1$1");
+		}
+		if (min >= 0 && min < 10 && cursorFormat.search(/mm/) >= 0){
+			min = "0" + min.toString();
+		}
+		if (min >= 10 && cursorFormat.search(/mm/) < 0 ){
+			cursorFormat = cursorFormat.replace(/m/, "$1$1");
+		}
+		if (sec >= 0 && sec < 10 && cursorFormat.search(/ss/) >= 0){
+			sec = "0" + sec.toString();
+		}
+		if (sec >= 10 && cursorFormat.search(/ss/) < 0 ){
+			cursorFormat = cursorFormat.replace(/s/, "$1$1");
+		}
+		x = cursorFormat.indexOf(cursorToken);
+		if (x == -1){
+			x = format.length;
+		}
+		format = format.replace(/[hH]+/, hour);
+		format = format.replace(/m+/, min);
+		format = format.replace(/s+/, sec);
+		format = format.replace(/t/, ampm);
+		this.setValue(format);
+		if(x > format.length){ x = format.length; }
+		return x;
+	}
+});

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