You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ca...@apache.org on 2007/01/11 23:36:18 UTC

svn commit: r495409 [32/47] - in /myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource: ./ src/ src/animation/ src/cal/ src/charting/ src/charting/svg/ src/charting/vml/ src/collections/ src/crypto/ src/data/ src/data/...

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Plugin/__package__.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Plugin/__package__.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Plugin/__package__.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Plugin/__package__.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,15 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: [ "dojo.widget.Editor2", 
+			 "dojo.widget.Editor2Toolbar"]
+});
+dojo.provide("dojo.widget.Editor2Plugin.*");

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Toolbar.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Toolbar.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Toolbar.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Editor2Toolbar.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,682 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Editor2Toolbar");
+
+dojo.require("dojo.lang.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.PopupContainer");
+dojo.require("dojo.widget.ColorPalette");
+
+dojo.lang.declare("dojo.widget.HandlerManager", null,
+	function(){
+		this._registeredHandlers=[];
+	},
+{
+	// summary: internal base class for handler function management
+	registerHandler: function(/*Object*/obj, /*String*/func){
+		// summary: register a handler
+		// obj: object which has the function to call
+		// func: the function in the object
+		if(arguments.length == 2){
+			this._registeredHandlers.push(function(){return obj[func].apply(obj, arguments);});
+		}else{
+			/* obj: Function
+			    func: null
+			    pId: f */
+			this._registeredHandlers.push(obj);
+		}
+	},
+	removeHandler: function(func){
+		// summary: remove a registered handler
+		for(var i=0;i<this._registeredHandlers.length;i++){
+			if(func === this._registeredHandlers[i]){
+				delete this._registeredHandlers[i];
+				return;
+			}
+		}
+		dojo.debug("HandlerManager handler "+func+" is not registered, can not remove.");
+	},
+	destroy: function(){
+		for(var i=0;i<this._registeredHandlers.length;i++){
+			delete this._registeredHandlers[i];
+		}
+	}
+});
+
+dojo.widget.Editor2ToolbarItemManager = new dojo.widget.HandlerManager;
+dojo.lang.mixin(dojo.widget.Editor2ToolbarItemManager,
+{
+	getToolbarItem: function(/*String*/name){
+		// summary: return a toobar item with the given name
+		var item;
+		name = name.toLowerCase();
+		for(var i=0;i<this._registeredHandlers.length;i++){
+			item = this._registeredHandlers[i](name);
+			if(item){
+				return item;
+			}
+		}
+
+		switch(name){
+			//button for builtin functions
+			case 'bold':
+			case 'copy':
+			case 'cut':
+			case 'delete':
+			case 'indent':
+			case 'inserthorizontalrule':
+			case 'insertorderedlist':
+			case 'insertunorderedlist':
+			case 'italic':
+			case 'justifycenter':
+			case 'justifyfull':
+			case 'justifyleft':
+			case 'justifyright':
+			case 'outdent':
+			case 'paste':
+			case 'redo':
+			case 'removeformat':
+			case 'selectall':
+			case 'strikethrough':
+			case 'subscript':
+			case 'superscript':
+			case 'underline':
+			case 'undo':
+			case 'unlink':
+			case 'createlink':
+			case 'insertimage':
+			//extra simple buttons
+			case 'htmltoggle':
+				item = new dojo.widget.Editor2ToolbarButton(name);
+				break;
+			case 'forecolor':
+			case 'hilitecolor':
+				item = new dojo.widget.Editor2ToolbarColorPaletteButton(name);
+				break;
+			case 'plainformatblock':
+				item = new dojo.widget.Editor2ToolbarFormatBlockPlainSelect("formatblock");
+				break;
+			case 'formatblock':
+				item = new dojo.widget.Editor2ToolbarFormatBlockSelect("formatblock");
+				break;
+			case 'fontsize':
+				item = new dojo.widget.Editor2ToolbarFontSizeSelect("fontsize");
+				break;
+			case 'fontname':
+				item = new dojo.widget.Editor2ToolbarFontNameSelect("fontname");
+				break;
+			case 'inserttable':
+			case 'insertcell':
+			case 'insertcol':
+			case 'insertrow':
+			case 'deletecells':
+			case 'deletecols':
+			case 'deleterows':
+			case 'mergecells':
+			case 'splitcell':
+				dojo.debug(name + " is implemented in dojo.widget.Editor2Plugin.TableOperation, please require it first.");
+				break;
+			//TODO:
+			case 'inserthtml':
+			case 'blockdirltr':
+			case 'blockdirrtl':
+			case 'dirltr':
+			case 'dirrtl':
+			case 'inlinedirltr':
+			case 'inlinedirrtl':
+				dojo.debug("Not yet implemented toolbar item: "+name);
+				break;
+			default:
+				dojo.debug("dojo.widget.Editor2ToolbarItemManager.getToolbarItem: Unknown toolbar item: "+name);
+		}
+		return item;
+	}
+});
+
+dojo.addOnUnload(dojo.widget.Editor2ToolbarItemManager, "destroy");
+
+dojo.declare("dojo.widget.Editor2ToolbarButton", null,
+	function(name){
+		this._name = name;
+//		this._command = editor.getCommand(name);
+	},
+{
+	// summary:
+	//		dojo.widget.Editor2ToolbarButton is the base class for all toolbar item in Editor2Toolbar
+	create: function(/*DomNode*/node, /*dojo.widget.Editor2Toolbar*/toolbar, /*Boolean*/nohover){
+		// summary: create the item
+		// node: the dom node which is the root of this toolbar item
+		// toolbar: the Editor2Toolbar widget this toolbar item belonging to
+		// nohover: whether this item in charge of highlight this item
+		this._domNode = node;
+		var cmd = toolbar.parent.getCommand(this._name); //FIXME: maybe an issue if different instance has different language
+		if(cmd){
+			this._domNode.title = cmd.getText();
+		}
+		//make this unselectable: different browsers
+		//use different properties for this, so use
+		//js do it automatically
+		this.disableSelection(this._domNode);
+
+		this._parentToolbar = toolbar;
+		dojo.event.connect(this._domNode, 'onclick', this, 'onClick');
+		if(!nohover){
+			dojo.event.connect(this._domNode, 'onmouseover', this, 'onMouseOver');
+			dojo.event.connect(this._domNode, 'onmouseout', this, 'onMouseOut');
+		}
+	},
+	disableSelection: function(/*DomNode*/rootnode){
+		// summary: disable selection on the passed node and all its children
+		dojo.html.disableSelection(rootnode);
+		var nodes = rootnode.all || rootnode.getElementsByTagName("*");
+		for(var x=0; x<nodes.length; x++){
+			dojo.html.disableSelection(nodes[x]);
+		}
+	},
+	onMouseOver: function(){
+		var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+		if(curInst){
+			var _command = curInst.getCommand(this._name);
+			if(_command && _command.getState() != dojo.widget.Editor2Manager.commandState.Disabled){
+				this.highlightToolbarItem();
+			}
+		}
+	},
+	onMouseOut: function(){
+		this.unhighlightToolbarItem();
+	},
+	destroy: function(){
+		// summary: destructor
+		this._domNode = null;
+//		delete this._command;
+		this._parentToolbar = null;
+	},
+	onClick: function(e){
+		if(this._domNode && !this._domNode.disabled && this._parentToolbar.checkAvailability()){
+			e.preventDefault();
+			e.stopPropagation();
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					_command.execute();
+				}
+			}
+		}
+	},
+	refreshState: function(){
+		// summary: update the state of the toolbar item
+		var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+		var em = dojo.widget.Editor2Manager;
+		if(curInst){
+			var _command = curInst.getCommand(this._name);
+			if(_command){
+				var state = _command.getState();
+				if(state != this._lastState){
+					switch(state){
+						case em.commandState.Latched:
+							this.latchToolbarItem();
+							break;
+						case em.commandState.Enabled:
+							this.enableToolbarItem();
+							break;
+						case em.commandState.Disabled:
+						default:
+							this.disableToolbarItem();
+					}
+					this._lastState = state;
+				}
+			}
+		}
+		return em.commandState.Enabled;
+	},
+
+	latchToolbarItem: function(){
+		this._domNode.disabled = false;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarLatchedItemStyle);
+	},
+
+	enableToolbarItem: function(){
+		this._domNode.disabled = false;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarEnabledItemStyle);
+	},
+
+	disableToolbarItem: function(){
+		this._domNode.disabled = true;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarDisabledItemStyle);
+	},
+
+	highlightToolbarItem: function(){
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarHighlightedItemStyle);
+	},
+
+	unhighlightToolbarItem: function(){
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarHighlightedItemStyle);
+	},
+
+	removeToolbarItemStyle: function(){
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarEnabledItemStyle);
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarLatchedItemStyle);
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarDisabledItemStyle);
+		this.unhighlightToolbarItem();
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarDropDownButton", dojo.widget.Editor2ToolbarButton, {
+	// summary: dojo.widget.Editor2ToolbarDropDownButton extends the basic button with a dropdown list
+
+	onClick: function(){
+		if(this._domNode && !this._domNode.disabled && this._parentToolbar.checkAvailability()){
+			if(!this._dropdown){
+				this._dropdown = dojo.widget.createWidget("PopupContainer", {});
+				this._domNode.appendChild(this._dropdown.domNode);
+			}
+			if(this._dropdown.isShowingNow){
+				this._dropdown.close();
+			}else{
+				this.onDropDownShown();
+				this._dropdown.open(this._domNode, null, this._domNode);
+			}
+		}
+	},
+	destroy: function(){
+		this.onDropDownDestroy();
+		if(this._dropdown){
+			this._dropdown.destroy();
+		}
+		dojo.widget.Editor2ToolbarDropDownButton.superclass.destroy.call(this);
+	},
+	onDropDownShown: function(){},
+	onDropDownDestroy: function(){}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarColorPaletteButton", dojo.widget.Editor2ToolbarDropDownButton, {
+	// summary: dojo.widget.Editor2ToolbarColorPaletteButton provides a dropdown color palette picker
+
+	onDropDownShown: function(){
+		if(!this._colorpalette){
+			this._colorpalette = dojo.widget.createWidget("ColorPalette", {});
+			this._dropdown.addChild(this._colorpalette);
+
+			this.disableSelection(this._dropdown.domNode);
+			this.disableSelection(this._colorpalette.domNode);
+			//do we need a destory to delete this._colorpalette manually?
+			//I assume as it is added to this._dropdown via addChild, it
+			//should be deleted when this._dropdown is destroyed
+
+			dojo.event.connect(this._colorpalette, "onColorSelect", this, 'setColor');
+			dojo.event.connect(this._dropdown, "open", this, 'latchToolbarItem');
+			dojo.event.connect(this._dropdown, "close", this, 'enableToolbarItem');
+		}
+	},
+	setColor: function(color){
+		this._dropdown.close();
+		var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+		if(curInst){
+			var _command = curInst.getCommand(this._name);
+			if(_command){
+				_command.execute(color);
+			}
+		}
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFormatBlockPlainSelect", dojo.widget.Editor2ToolbarButton, {
+	// summary: dojo.widget.Editor2ToolbarFormatBlockPlainSelect provides a simple select for setting block format
+
+	create: function(node, toolbar){
+//		dojo.widget.Editor2ToolbarFormatBlockPlainSelect.superclass.create.apply(this, arguments);
+		this._domNode = node;
+		this._parentToolbar = toolbar;
+		//TODO: check node is a select
+		this._domNode = node;
+		this.disableSelection(this._domNode);
+		dojo.event.connect(this._domNode, 'onchange', this, 'onChange');
+	},
+
+	destroy: function(){
+		this._domNode = null;
+	},
+
+	onChange: function(){
+		if(this._parentToolbar.checkAvailability()){
+			var sv = this._domNode.value.toLowerCase();
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					_command.execute(sv);
+				}
+			}
+		}
+	},
+
+	refreshState: function(){
+		if(this._domNode){
+			dojo.widget.Editor2ToolbarFormatBlockPlainSelect.superclass.refreshState.call(this);
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					var format = _command.getValue();
+					if(!format){ format = ""; }
+					dojo.lang.forEach(this._domNode.options, function(item){
+						if(item.value.toLowerCase() == format.toLowerCase()){
+							item.selected = true;
+						}
+					});
+				}
+			}
+		}
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarComboItem", dojo.widget.Editor2ToolbarDropDownButton,{
+	// summary: dojo.widget.Editor2ToolbarComboItem provides an external loaded dropdown list
+
+	href: null,
+	create: function(node, toolbar){
+		dojo.widget.Editor2ToolbarComboItem.superclass.create.apply(this, arguments);
+		//do not use lazy initilization, as we need the local names in refreshState()
+		if(!this._contentPane){
+			dojo.require("dojo.widget.ContentPane");
+			this._contentPane = dojo.widget.createWidget("ContentPane", {preload: 'true'});
+			this._contentPane.addOnLoad(this, "setup");
+			this._contentPane.setUrl(this.href);
+		}
+	},
+
+	onMouseOver: function(e){
+		if(this._lastState != dojo.widget.Editor2Manager.commandState.Disabled){
+			dojo.html.addClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectStyle);
+		}
+	},
+	onMouseOut:function(e){
+		dojo.html.removeClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectStyle);
+	},
+
+	onDropDownShown: function(){
+		if(!this._dropdown.__addedContentPage){
+			this._dropdown.addChild(this._contentPane);
+			this._dropdown.__addedContentPage = true;
+		}
+	},
+
+	setup: function(){
+		// summary: overload this to connect event
+	},
+
+	onChange: function(e){
+		if(this._parentToolbar.checkAvailability()){
+			var name = e.currentTarget.getAttribute("dropDownItemName");
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					_command.execute(name);
+				}
+			}
+		}
+		this._dropdown.close();
+	},
+
+	onMouseOverItem: function(e){
+		dojo.html.addClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectItemStyle);
+	},
+
+	onMouseOutItem: function(e){
+		dojo.html.removeClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectItemStyle);
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFormatBlockSelect", dojo.widget.Editor2ToolbarComboItem, {
+	// summary: dojo.widget.Editor2ToolbarFormatBlockSelect is an improved format block setting item
+
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FormatBlock.html"),
+
+	setup: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.setup.call(this);
+
+		var nodes = this._contentPane.domNode.all || this._contentPane.domNode.getElementsByTagName("*");
+		this._blockNames = {};
+		this._blockDisplayNames = {};
+		for(var x=0; x<nodes.length; x++){
+			var node = nodes[x];
+			dojo.html.disableSelection(node);
+			var name=node.getAttribute("dropDownItemName")
+			if(name){
+				this._blockNames[name] = node;
+				var childrennodes = node.getElementsByTagName(name);
+				this._blockDisplayNames[name] = childrennodes[childrennodes.length-1].innerHTML;
+			}
+		}
+		for(var name in this._blockNames){
+			dojo.event.connect(this._blockNames[name], "onclick", this, "onChange");
+			dojo.event.connect(this._blockNames[name], "onmouseover", this, "onMouseOverItem");
+			dojo.event.connect(this._blockNames[name], "onmouseout", this, "onMouseOutItem");
+		}
+	},
+
+	onDropDownDestroy: function(){
+		if(this._blockNames){
+			for(var name in this._blockNames){
+				delete this._blockNames[name];
+				delete this._blockDisplayNames[name];
+			}
+		}
+	},
+
+	refreshState: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.refreshState.call(this);
+		if(this._lastState != dojo.widget.Editor2Manager.commandState.Disabled){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					var format = _command.getValue();
+					if(format == this._lastSelectedFormat && this._blockDisplayNames){
+						return this._lastState;
+					}
+					this._lastSelectedFormat = format;
+					var label = this._domNode.getElementsByTagName("label")[0];
+					var isSet = false;
+					if(this._blockDisplayNames){
+						for(var name in this._blockDisplayNames){
+							if(name == format){
+								label.innerHTML = 	this._blockDisplayNames[name];
+								isSet = true;
+								break;
+							}
+						}
+						if(!isSet){
+							label.innerHTML = "&nbsp;";
+						}
+					}
+				}
+			}
+		}
+
+		return this._lastState;
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFontSizeSelect", dojo.widget.Editor2ToolbarComboItem,{
+	// summary: dojo.widget.Editor2ToolbarFontSizeSelect provides a dropdown list for setting fontsize
+
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FontSize.html"),
+
+	setup: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.setup.call(this);
+
+		var nodes = this._contentPane.domNode.all || this._contentPane.domNode.getElementsByTagName("*");
+		this._fontsizes = {};
+		this._fontSizeDisplayNames = {};
+		for(var x=0; x<nodes.length; x++){
+			var node = nodes[x];
+			dojo.html.disableSelection(node);
+			var name=node.getAttribute("dropDownItemName")
+			if(name){
+				this._fontsizes[name] = node;
+				this._fontSizeDisplayNames[name] = node.getElementsByTagName('font')[0].innerHTML;
+			}
+		}
+		for(var name in this._fontsizes){
+			dojo.event.connect(this._fontsizes[name], "onclick", this, "onChange");
+			dojo.event.connect(this._fontsizes[name], "onmouseover", this, "onMouseOverItem");
+			dojo.event.connect(this._fontsizes[name], "onmouseout", this, "onMouseOutItem");
+		}
+	},
+
+	onDropDownDestroy: function(){
+		if(this._fontsizes){
+			for(var name in this._fontsizes){
+				delete this._fontsizes[name];
+				delete this._fontSizeDisplayNames[name];
+			}
+		}
+	},
+
+	refreshState: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.refreshState.call(this);
+		if(this._lastState != dojo.widget.Editor2Manager.commandState.Disabled){
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(curInst){
+				var _command = curInst.getCommand(this._name);
+				if(_command){
+					var size = _command.getValue();
+					if(size == this._lastSelectedSize && this._fontSizeDisplayNames){
+						return this._lastState;
+					}
+					this._lastSelectedSize = size;
+					var label = this._domNode.getElementsByTagName("label")[0];
+					var isSet = false;
+					if(this._fontSizeDisplayNames){
+						for(var name in this._fontSizeDisplayNames){
+							if(name == size){
+								label.innerHTML = 	this._fontSizeDisplayNames[name];
+								isSet = true;
+								break;
+							}
+						}
+						if(!isSet){
+							label.innerHTML = "&nbsp;";
+						}
+					}
+				}
+			}
+		}
+		return this._lastState;
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFontNameSelect", dojo.widget.Editor2ToolbarFontSizeSelect, {
+	// summary: dojo.widget.Editor2ToolbarFontNameSelect provides a dropdown list for setting fontname
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FontName.html")
+});
+
+dojo.widget.defineWidget(
+	"dojo.widget.Editor2Toolbar",
+	dojo.widget.HtmlWidget,
+	function(){
+		dojo.event.connect(this, "fillInTemplate", dojo.lang.hitch(this, function(){
+			if(dojo.render.html.ie){
+				this.domNode.style.zoom = 1.0;
+			}
+		}));
+	},
+	{
+		// summary:
+		//		dojo.widget.Editor2Toolbar is the main widget for the toolbar associated with an Editor2
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.css"),
+
+		// ToolbarLatchedItemStyle: String: class name for latched toolbar button items
+		ToolbarLatchedItemStyle: "ToolbarButtonLatched",
+
+		// ToolbarEnabledItemStyle: String: class name for enabled toolbar button items
+		ToolbarEnabledItemStyle: "ToolbarButtonEnabled",
+
+		// ToolbarDisabledItemStyle: String: class name for disabled toolbar button items
+		ToolbarDisabledItemStyle: "ToolbarButtonDisabled",
+
+		// ToolbarHighlightedItemStyle: String: class name for highlighted toolbar button items
+		ToolbarHighlightedItemStyle: "ToolbarButtonHighlighted",
+
+		// ToolbarHighlightedSelectStyle: String: class name for highlighted toolbar select items
+		ToolbarHighlightedSelectStyle: "ToolbarSelectHighlighted",
+
+		// ToolbarHighlightedSelectItemStyle: String: class name for highlighted toolbar select dropdown items
+		ToolbarHighlightedSelectItemStyle: "ToolbarSelectHighlightedItem",
+
+//		itemNodeType: 'span', //all the items (with attribute dojoETItemName set) defined in the toolbar should be a of this type
+
+		postCreate: function(){
+			var nodes = dojo.html.getElementsByClass("dojoEditorToolbarItem", this.domNode/*, this.itemNodeType*/);
+
+			this.items = {};
+			for(var x=0; x<nodes.length; x++){
+				var node = nodes[x];
+				var itemname = node.getAttribute("dojoETItemName");
+				if(itemname){
+					var item = dojo.widget.Editor2ToolbarItemManager.getToolbarItem(itemname);
+					if(item){
+						item.create(node, this);
+						this.items[itemname.toLowerCase()] = item;
+					}else{
+						//hide unsupported toolbar items
+						node.style.display = "none";
+					}
+				}
+			}
+		},
+
+		update: function(){
+			// summary: update all the toolbar items
+			for(var cmd in this.items){
+				this.items[cmd].refreshState();
+			}
+		},
+
+		shareGroup: '',
+		checkAvailability: function(){
+			// summary: returns whether items in this toolbar can be executed
+			// description: 
+			//		For unshared toolbar, when clicking on a toolbar, the corresponding
+			//		editor will be focused, and this function always return true. For shared
+			//		toolbar, if the current focued editor is not one of the instances sharing
+			//		this toolbar, this function return false, otherwise true.
+			if(!this.shareGroup){
+				this.parent.focus();
+				return true;
+			}
+			var curInst = dojo.widget.Editor2Manager.getCurrentInstance();
+			if(this.shareGroup == curInst.toolbarGroup){
+				return true;
+			}
+			return false;
+		},
+		destroy: function(){
+			for(var it in this.items){
+				this.items[it].destroy();
+				delete this.items[it];
+			}
+			dojo.widget.Editor2Toolbar.superclass.destroy.call(this);
+		}
+	}
+);

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FilteringTable.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FilteringTable.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FilteringTable.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FilteringTable.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,1003 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.FilteringTable");
+
+dojo.require("dojo.date.format");
+dojo.require("dojo.collections.Store");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.util");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.defineWidget(
+	"dojo.widget.FilteringTable", 
+	dojo.widget.HtmlWidget, 
+	function(){
+		// summary: A basic tabular data widget that supports sorting and filtering mechanisms.
+		// description:
+		//		FilteringTable is a 2D data view that supports multiple column sorting and filtering
+		//		functionality.  It can get its data in one of two ways: via HTML (i.e. degradable
+		//		data), or from an external JSON source through widget.store.setData.  Records in
+		//		a FilteringTable can be selected as if it were a select list.
+		// store: dojo.collections.Store
+		//		The underlying Store for all data represented by the widget.
+		// valueField: String
+		// 		The name of the field used as a unique key for each row, defaults to "Id".
+		// multiple: boolean
+		//		Allow multiple selections.
+		// maxSelect: Integer
+		//		Maximum number of rows that can be selected at once.  0 == no limit.
+		// maxSortable: Integer
+		//		Maximum number of columns allowed for sorting at one time.
+		// minRows: Integer
+		//		The minimum number of rows to show.  Default is 0.
+		// defaultDateFormat: String
+		//		The default format for a date column, as used by dojo.date.format.
+		// alternateRows: Boolean
+		//		Use alternate row CSS classes to show zebra striping.
+		// headClass: String
+		//		CSS Class name for the head of the table.
+		// tbodyClass: String
+		//		CSS Class name for the body of the table.
+		// headerClass: String
+		//		CSS Class name for headers that are not sorted.
+		// headerUpClass: String
+		//		CSS Class name for headers that are for ascending sorted columns. Default is "selectedUp".
+		// headerDownClass: String
+		//		CSS Class name for headers that are for descending sorted columns. Default is "selectedDown".
+		// rowClass: String
+		//		CSS Class name for body rows.
+		// rowAlternateClass: String
+		//		CSS Class name for alternate rows.  Default is "alt".
+		// rowSelectedClass: String
+		//		CSS Class name for selected rows.  Default is "selected".
+		// columnSelectedClass: String
+		//		CSS Class name for any columns being sorted on.  Unimplemented.
+		this.store=new dojo.collections.Store();
+
+		//declare per instance changeable widget properties
+		this.valueField="Id";
+		this.multiple=false;
+		this.maxSelect=0;
+		this.maxSortable=1;  // how many columns can be sorted at once.
+		this.minRows=0;
+		this.defaultDateFormat = "%D";
+		this.isInitialized=false;
+		this.alternateRows=false;
+
+		this.columns=[];
+		this.sortInformation=[{
+			index:0,
+			direction:0
+		}];
+
+		// CSS definitions
+		this.headClass="";
+		this.tbodyClass="";
+		this.headerClass="";
+		this.headerUpClass="selectedUp";
+		this.headerDownClass="selectedDown";
+		this.rowClass="";
+		this.rowAlternateClass="alt";
+		this.rowSelectedClass="selected";
+		this.columnSelected="sorted-column";
+	},
+{
+	//	dojo widget properties
+	isContainer: false,
+	templatePath: null,
+	templateCssPath: null,
+
+	//	methods.
+	getTypeFromString: function(/* string */s){
+		//	summary
+		//	Gets a function based on the passed string.
+		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
+	},
+
+	//	custom data access.
+	getByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Returns the data object based on the passed row.
+		return this.store.getByKey(dojo.html.getAttribute(row, "value"));	//	object
+	},
+	getDataByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Returns the source data object based on the passed row.
+		return this.store.getDataByKey(dojo.html.getAttribute(row, "value")); // object
+	},
+
+	getRow: function(/* Object */ obj){
+		//	summary
+		//	Finds the row in the table based on the passed data object.
+		var rows = this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			if(this.store.getDataByKey(dojo.html.getAttribute(rows[i], "value")) == obj){
+				return rows[i];	//	HTMLTableRow
+			}
+		}
+		return null;	//	HTMLTableRow
+	},
+	getColumnIndex: function(/* string */fieldPath){
+		//	summary
+		//	Returns index of the column that represents the passed field path.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == fieldPath){
+				return i;	//	integer
+			}
+		}
+		return -1;	//	integer
+	},
+
+	getSelectedData: function(){
+		//	summary
+		//	returns all objects that are selected.
+		var data=this.store.get();
+		var a=[];
+		for(var i=0; i<data.length; i++){
+			if(data[i].isSelected){
+				a.push(data[i].src);
+			}
+		}
+		if(this.multiple){
+			return a;		//	array
+		} else {
+			return a[0];	//	object
+		}
+	},
+	
+	isSelected: function(/* object */obj){
+		//	summary
+		//	Returns whether the passed object is currently selected.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				return true;	//	boolean
+			}
+		}
+		return false;	//	boolean
+	},
+	isValueSelected: function(/* string */val){
+		//	summary
+		//	Returns the object represented by key "val" is selected.
+		var v = this.store.getByKey(val);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+	isIndexSelected: function(/* number */idx){
+		//	summary
+		//	Returns the object represented by integer "idx" is selected.
+		var v = this.store.getByIndex(idx);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+	isRowSelected: function(/* HTMLTableRow */row){
+		//	summary
+		//	Returns if the passed row is selected.
+		var v = this.getByRow(row);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+
+	reset: function(){
+		//	summary
+		//	Resets the widget to its initial internal state.
+		this.store.clearData();
+		this.columns = [];
+		this.sortInformation = [ {index:0, direction:0} ];
+		this.resetSelections();
+		this.isInitialized = false;
+		this.onReset();
+	},
+	resetSelections: function(){
+		//	summary
+		//	Unselects all data objects.
+		this.store.forEach(function(element){
+			element.isSelected = false;
+		});
+	},
+	onReset:function(){ 
+		//	summary
+		//	Stub for onReset event.
+	},
+
+	//	selection and toggle functions
+	select: function(/*object*/ obj){
+		//	summary
+		//	selects the passed object.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				data[i].isSelected = true;
+				break;
+			}
+		}
+		this.onDataSelect(obj);
+	},
+	selectByValue: function(/*string*/ val){
+		//	summary
+		//	selects the object represented by key "val".
+		this.select(this.store.getDataByKey(val));
+	},
+	selectByIndex: function(/*number*/ idx){
+		//	summary
+		//	selects the object represented at index "idx".
+		this.select(this.store.getDataByIndex(idx));
+	},
+	selectByRow: function(/*HTMLTableRow*/ row){
+		//	summary
+		//	selects the object represented by HTMLTableRow row.
+		this.select(this.getDataByRow(row));
+	},
+	selectAll: function(){
+		//	summary
+		//	selects all objects.
+		this.store.forEach(function(element){
+			element.isSelected = true;
+		});
+	},
+	onDataSelect: function(/* object */obj){ 
+		//	summary
+		//	Stub for onDataSelect event.
+	},
+
+	toggleSelection: function(/*object*/obj){
+		//	summary
+		//	Flips the selection state of passed obj.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				data[i].isSelected = !data[i].isSelected;
+				break;
+			}
+		}
+		this.onDataToggle(obj);
+	},
+	toggleSelectionByValue: function(/*string*/val){
+		//	summary
+		//	Flips the selection state of object represented by val.
+		this.toggleSelection(this.store.getDataByKey(val));
+	},
+	toggleSelectionByIndex: function(/*number*/idx){
+		//	summary
+		//	Flips the selection state of object at index idx.
+		this.toggleSelection(this.store.getDataByIndex(idx));
+	},
+	toggleSelectionByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Flips the selection state of object represented by row.
+		this.toggleSelection(this.getDataByRow(row));
+	},
+	toggleAll: function(){
+		//	summary
+		//	Flips the selection state of all objects.
+		this.store.forEach(function(element){
+			element.isSelected = !element.isSelected;
+		});
+	},
+	onDataToggle: function(/* object */obj){ 
+		//	summary
+		//	Stub for onDataToggle event.
+	},
+
+	//	parsing functions, from HTML to metadata/SimpleStore
+	_meta:{
+		field:null,
+		format:null,
+		filterer:null,
+		noSort:false,
+		sortType:"String",
+		dataType:String,
+		sortFunction:null,
+		filterFunction:null,
+		label:null,
+		align:"left",
+		valign:"middle",
+		getField:function(){ 
+			return this.field || this.label; 
+		},
+		getType:function(){ 
+			return this.dataType; 
+		}
+	},
+	createMetaData: function(/* object */obj){
+		//	summary
+		//	Take a JSON-type structure and make it into a ducktyped metadata object.
+		for(var p in this._meta){
+			//	rudimentary mixin
+			if(!obj[p]){
+				obj[p] = this._meta[p];
+			}
+		}
+		if(!obj.label){
+			obj.label=obj.field;
+		}
+		if(!obj.filterFunction){
+			obj.filterFunction=this._defaultFilter;
+		}
+		return obj;	//	object
+	},
+	parseMetadata: function(/* HTMLTableHead */head){
+		//	summary
+		//	Parses the passed HTMLTableHead element to create meta data.
+		this.columns=[];
+		this.sortInformation=[];
+		var row = head.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 = this.createMetaData({ });
+			
+			//	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");
+			}
+			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;
+				}
+			}
+			o.label = dojo.html.renderedTextContent(cells[i]);
+			if(dojo.html.hasAttribute(cells[i], "field")){
+				o.field=dojo.html.getAttribute(cells[i],"field");
+			} else if(o.label.length > 0){
+				o.field=o.label;
+			} else {
+				o.field = "field" + i;
+			}
+			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__"
+				}else{
+					var type = this.getTypeFromString(sortType);
+					if(type){
+						o.sortType = sortType;
+						o.dataType = type;
+					}
+				}
+			}
+
+			//	TODO: set up filtering mechanisms here.
+			if(dojo.html.hasAttribute(cells[i], "filterusing")){
+				var trans = dojo.html.getAttribute(cells[i],"filterusing");
+				var f = this.getTypeFromString(trans);
+				if (f != null && f != window && typeof(f)=="function"){
+					o.filterFunction=f;
+				}
+			}
+			
+			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")){
+				var info = {
+					index:i,
+					direction:0
+				};
+				var dir = dojo.html.getAttribute(cells[i], "sort");
+				if(!isNaN(parseInt(dir))){
+					dir = parseInt(dir);
+					info.direction = (dir != 0) ? 1 : 0;
+				}else{
+					info.direction = (dir.toLowerCase() == "desc") ? 1 : 0;
+				}
+				this.sortInformation.push(info);
+			}
+		}
+		if(this.sortInformation.length == 0){
+			this.sortInformation.push({
+				index:0,
+				direction:0
+			});
+		} else if (this.sortInformation.length > this.maxSortable){
+			this.sortInformation.length = this.maxSortable;
+		}
+	},
+	parseData: function(/* HTMLTableBody */body){
+		//	summary
+		//	Parse HTML data into native JSON structure for the store.
+		if(body.rows.length == 0 && this.columns.length == 0){
+			return;	//	there's no data, ignore me.
+		}
+
+		//	create a data constructor based on what we've got for the fields.
+		var self=this;
+		this["__selected__"] = [];
+		var arr = this.store.getFromHtml(this.columns, body, function(obj, row){
+			if(typeof(obj[self.valueField])=="undefined" || obj[self.valueField]==null){
+				obj[self.valueField] = dojo.html.getAttribute(row, "value");
+			}
+			if(dojo.html.getAttribute(row, "selected")=="true"){
+				self["__selected__"].push(obj);
+			}
+		});
+		
+		this.store.setData(arr, true);
+		this.render();
+		
+		for(var i=0; i<this["__selected__"].length; i++){
+			this.select(this["__selected__"][i]);
+		}
+		this.renderSelections();
+
+		delete this["__selected__"];
+
+		//	say that we are already initialized so that we don't kill anything
+		this.isInitialized=true;
+	},
+
+	//	standard events
+	onSelect: function(/* HTMLEvent */e){
+		//	summary
+		//	Handles the onclick event of any element.
+		var row = dojo.html.getParentByType(e.target,"tr");
+		if(dojo.html.hasAttribute(row,"emptyRow")){
+			return;
+		}
+		var body = dojo.html.getParentByType(row,"tbody");
+		if(this.multiple){
+			if(e.shiftKey){
+				var startRow;
+				var rows=body.rows;
+				for(var i=0;i<rows.length;i++){
+					if(rows[i]==row){
+						break;
+					}
+					if(this.isRowSelected(rows[i])){
+						startRow=rows[i];
+					}
+				}
+				if(!startRow){
+					startRow = row;
+					for(; i<rows.length; i++){
+						if(this.isRowSelected(rows[i])){
+							row = rows[i];
+							break;
+						}
+					}
+				}
+				this.resetSelections();
+				if(startRow == row){
+					this.toggleSelectionByRow(row);
+				} else {
+					var doSelect = false;
+					for(var i=0; i<rows.length; i++){
+						if(rows[i] == startRow){
+							doSelect=true;
+						}
+						if(doSelect){
+							this.selectByRow(rows[i]);
+						}
+						if(rows[i] == row){
+							doSelect = false;
+						}
+					}
+				}
+			} else {
+				this.toggleSelectionByRow(row);
+			}
+		} else {
+			this.resetSelections();
+			this.toggleSelectionByRow(row);
+		}
+		this.renderSelections();
+	},
+	onSort: function(/* HTMLEvent */e){
+		//	summary
+		//	Sort the table based on the column selected.
+		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++){
+			dojo.html.setClass(headers[i], this.headerClass);
+			if(headers[i]==header){
+				if(this.sortInformation[0].index != i){
+					this.sortInformation.unshift({ 
+						index:i, 
+						direction:0
+					});
+				} else {
+					this.sortInformation[0] = {
+						index:i,
+						direction:(~this.sortInformation[0].direction)&1
+					};
+				}
+			}
+		}
+
+		this.sortInformation.length = Math.min(this.sortInformation.length, this.maxSortable);
+		for(var i=0; i<this.sortInformation.length; i++){
+			var idx=this.sortInformation[i].index;
+			var dir=(~this.sortInformation[i].direction)&1;
+			dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass);
+		}
+		this.render();
+	},
+	onFilter: function(){
+		//	summary
+		//	show or hide rows based on the parameters of the passed filter.
+	},
+
+	//	Filtering methods
+	_defaultFilter: function(/* Object */obj){
+		//	summary
+		//	Always return true as the result of the default filter.
+		return true;
+	},
+	setFilter: function(/* string */field, /* function */fn){
+		//	summary
+		//	set a filtering function on the passed field.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == field){
+				this.columns[i].filterFunction=fn;
+				break;
+			}
+		}
+		this.applyFilters();
+	},
+	setFilterByIndex: function(/* number */idx, /* function */fn){
+		//	summary
+		//	set a filtering function on the passed column index.
+		this.columns[idx].filterFunction=fn;
+		this.applyFilters();
+	},
+	clearFilter: function(/* string */field){
+		//	summary
+		//	clear a filtering function on the passed field.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == field){
+				this.columns[i].filterFunction=this._defaultFilter;
+				break;
+			}
+		}
+		this.applyFilters();
+	}, 
+	clearFilterByIndex: function(/* number */idx){
+		//	summary
+		//	clear a filtering function on the passed column index.
+		this.columns[idx].filterFunction=this._defaultFilter;
+		this.applyFilters();
+	}, 
+	clearFilters: function(){
+		//	summary
+		//	clears all filters.
+		for(var i=0; i<this.columns.length; i++){
+			this.columns[i].filterFunction=this._defaultFilter;
+		}
+		//	we'll do the clear manually, it will be faster.
+		var rows=this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			rows[i].style.display="";
+			if(this.alternateRows){
+				dojo.html[((i % 2 == 1)?"addClass":"removeClass")](rows[i], this.rowAlternateClass);
+			}
+		}
+		this.onFilter();
+	},
+	applyFilters: function(){
+		//	summary
+		//	apply all filters to the table.
+		var alt=0;
+		var rows=this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			var b=true;
+			var row=rows[i];
+			for(var j=0; j<this.columns.length; j++){
+				var value = this.store.getField(this.getDataByRow(row), this.columns[j].getField());
+				if(this.columns[j].getType() == Date && value != null && !value.getYear){
+					value = new Date(value);
+				}
+				if(!this.columns[j].filterFunction(value)){
+					b=false;
+					break;
+				}
+			}
+			row.style.display=(b?"":"none");
+			if(b && this.alternateRows){
+				dojo.html[((alt++ % 2 == 1)?"addClass":"removeClass")](row, this.rowAlternateClass);
+			}
+		}
+		this.onFilter();
+	},
+
+	//	sorting functionality
+	createSorter: function(/* array */info){
+		//	summary
+		//	creates a custom function to be used for sorting.
+		var self=this;
+		var sortFunctions=[];	//	our function stack.
+	
+		function createSortFunction(fieldIndex, dir){
+			var meta=self.columns[fieldIndex];
+			var field=meta.getField();
+			return function(rowA, rowB){
+				if(dojo.html.hasAttribute(rowA,"emptyRow")){ return 1; }
+				if(dojo.html.hasAttribute(rowB,"emptyRow")){ return -1; }
+
+				//	TODO: check for markup and compare by rendered text.
+				var a = self.store.getField(self.getDataByRow(rowA), field);
+				var b = self.store.getField(self.getDataByRow(rowB), field);
+				var ret = 0;
+				if(a > b) ret = 1;
+				if(a < b) ret = -1;
+				return dir * ret;
+			}
+		}
+
+		var current=0;
+		var max = Math.min(info.length, this.maxSortable, this.columns.length);
+		while(current < max){
+			var direction = (info[current].direction == 0) ? 1 : -1;
+			sortFunctions.push(
+				createSortFunction(info[current].index, direction)
+			);
+			current++;
+		}
+
+		return function(rowA, rowB){
+			var idx=0;
+			while(idx < sortFunctions.length){
+				var ret = sortFunctions[idx++](rowA, rowB);
+				if(ret != 0) return ret;
+			}
+			//	if we got here then we must be equal.
+			return 0; 	
+		};	//	function
+	},
+
+	//	rendering
+	createRow: function(/* object */obj){
+		//	summary
+		//	Create an HTML row based on the passed object
+		var row=document.createElement("tr");
+		dojo.html.disableSelection(row);
+		if(obj.key != null){
+			row.setAttribute("value", obj.key);
+		}
+		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);
+			var val = this.store.getField(obj.src, this.columns[j].getField());
+			if(typeof(val)=="undefined"){
+				val="";
+			}
+			this.fillCell(cell, this.columns[j], val);
+			row.appendChild(cell);
+		}
+		return row;	//	HTMLTableRow
+	},
+	fillCell: function(/* HTMLTableCell */cell, /* object */meta, /* object */val){
+		//	summary
+		//	Fill the passed cell with value, based on the passed meta object.
+		if(meta.sortType=="__markup__"){
+			cell.innerHTML=val;
+		} else {
+			if(meta.getType()==Date) {
+				val=new Date(val);
+				if(!isNaN(val)){
+					var format = this.defaultDateFormat;
+					if(meta.format){
+						format = meta.format;
+					}
+					cell.innerHTML = dojo.date.strftime(val, format);
+				} else {
+					cell.innerHTML = val;
+				}
+			} else if ("Number number int Integer float Float".indexOf(meta.getType())>-1){
+				//	TODO: number formatting
+				if(val.length == 0){
+					val="0";
+				}
+				var n = parseFloat(val, 10) + "";
+				//	TODO: numeric formatting + rounding :)
+				if(n.indexOf(".")>-1){
+					n = dojo.math.round(parseFloat(val,10),2);
+				}
+				cell.innerHTML = n;
+			}else{
+				cell.innerHTML = val;
+			}
+		}
+	},
+	prefill: function(){
+		//	summary
+		//	if there's no data in the table, then prefill it with this.minRows.
+		this.isInitialized = false;
+		var body = this.domNode.tBodies[0];
+		while (body.childNodes.length > 0){
+			body.removeChild(body.childNodes[0]);
+		}
+		
+		if(this.minRows>0){
+			for(var i=0; i < this.minRows; i++){
+				var row = document.createElement("tr");
+				if(this.alternateRows){
+					dojo.html[((i % 2 == 1)?"addClass":"removeClass")](row, this.rowAlternateClass);
+				}
+				row.setAttribute("emptyRow","true");
+				for(var j=0; j<this.columns.length; j++){
+					var cell = document.createElement("td");
+					cell.innerHTML = "&nbsp;";
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+	},
+	init: function(){
+		//	summary
+		//	initializes the table of data
+		this.isInitialized=false;
+
+		//	if there is no thead, create it now.
+		var head=this.domNode.getElementsByTagName("thead")[0];
+		if(head.getElementsByTagName("tr").length == 0){
+			//	render the column code.
+			var row=document.createElement("tr");
+			for(var i=0; i<this.columns.length; i++){
+				var cell=document.createElement("td");
+				cell.setAttribute("align", this.columns[i].align);
+				cell.setAttribute("valign", this.columns[i].valign);
+				dojo.html.disableSelection(cell);
+				cell.innerHTML=this.columns[i].label;
+				row.appendChild(cell);
+
+				//	attach the events.
+				if(!this.columns[i].noSort){
+					dojo.event.connect(cell, "onclick", this, "onSort");
+				}
+			}
+			dojo.html.prependChild(row, head);
+		}
+		
+		if(this.store.get().length == 0){
+			return false;
+		}
+
+		var idx=this.domNode.tBodies[0].rows.length;
+		if(!idx || idx==0 || this.domNode.tBodies[0].rows[0].getAttribute("emptyRow")=="true"){
+			idx = 0;
+			var body = this.domNode.tBodies[0];
+			while(body.childNodes.length>0){
+				body.removeChild(body.childNodes[0]);
+			}
+
+			var data = this.store.get();
+			for(var i=0; i<data.length; i++){
+				var row = this.createRow(data[i]);
+				body.appendChild(row);
+				idx++;
+			}
+		}
+
+		//	add empty rows
+		if(this.minRows > 0 && idx < this.minRows){
+			idx = this.minRows - idx;
+			for(var i=0; i<idx; i++){
+				row=document.createElement("tr");
+				row.setAttribute("emptyRow","true");
+				for(var j=0; j<this.columns.length; j++){
+					cell=document.createElement("td");
+					cell.innerHTML="&nbsp;";
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+
+		//	last but not least, show any columns that have sorting already on them.
+		var row=this.domNode.getElementsByTagName("thead")[0].rows[0];
+		var cellTag="td";
+		if(row.getElementsByTagName(cellTag).length==0) cellTag="th";
+		var headers=row.getElementsByTagName(cellTag);
+		for(var i=0; i<headers.length; i++){
+			dojo.html.setClass(headers[i], this.headerClass);
+		}
+		for(var i=0; i<this.sortInformation.length; i++){
+			var idx=this.sortInformation[i].index;
+			var dir=(~this.sortInformation[i].direction)&1;
+			dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass);
+		}
+
+		this.isInitialized=true;
+		return this.isInitialized;
+	},
+	render: function(){
+		//	summary
+		//	Renders the actual table data.
+
+	/*	The method that should be called once underlying changes
+	 *	are made, including sorting, filtering, data changes.
+	 *	Rendering the selections themselves are a different method,
+	 *	which render() will call as the last step.
+	 ****************************************************************/
+		if(!this.isInitialized){
+			var b = this.init();
+			if(!b){
+				this.prefill();
+				return;
+			}
+		}
+
+		//	do the sort
+		var rows=[];
+		var body=this.domNode.tBodies[0];
+		var emptyRowIdx=-1;
+		for(var i=0; i<body.rows.length; i++){
+			rows.push(body.rows[i]);
+		}
+
+		//	build the sorting function, and do the sorting.
+		var sortFunction = this.createSorter(this.sortInformation);
+		if(sortFunction){
+			rows.sort(sortFunction);
+		}
+
+		//	append the rows without killing them, this should help with the HTML problems.
+		for(var i=0; i<rows.length; i++){
+			if(this.alternateRows){
+				dojo.html[((i%2==1)?"addClass":"removeClass")](rows[i], this.rowAlternateClass);
+			}
+			dojo.html[(this.isRowSelected(body.rows[i])?"addClass":"removeClass")](body.rows[i], this.rowSelectedClass);
+			body.appendChild(rows[i]);
+		}
+	},
+	renderSelections: function(){
+		//	summary
+		//	Render all selected objects using CSS.
+		var body=this.domNode.tBodies[0];
+		for(var i=0; i<body.rows.length; i++){
+			dojo.html[(this.isRowSelected(body.rows[i])?"addClass":"removeClass")](body.rows[i], this.rowSelectedClass);
+		}
+	},
+
+	//	widget lifetime handlers
+	initialize: function(){ 
+		//	summary
+		//	Initializes the widget.
+		var self=this;
+		//	connect up binding listeners here.
+		dojo.event.connect(this.store, "onSetData", function(){
+			self.store.forEach(function(element){
+				element.isSelected = false;
+			});
+			self.isInitialized=false;
+			var body = self.domNode.tBodies[0];
+			if(body){
+				while(body.childNodes.length>0){
+					body.removeChild(body.childNodes[0]);
+				}
+			}
+			self.render();
+		});
+		dojo.event.connect(this.store, "onClearData", function(){
+			self.isInitialized = false;
+			self.render();
+		});
+		dojo.event.connect(this.store, "onAddData", function(addedObject){
+			var row=self.createRow(addedObject);
+			self.domNode.tBodies[0].appendChild(row);
+			self.render();
+		});
+		dojo.event.connect(this.store, "onAddDataRange", function(arr){
+			for(var i=0; i<arr.length; i++){
+				arr[i].isSelected=false;
+				var row=self.createRow(arr[i]);
+				self.domNode.tBodies[0].appendChild(row);
+			};
+			self.render();
+		});
+		dojo.event.connect(this.store, "onRemoveData", function(removedObject){
+			var rows = self.domNode.tBodies[0].rows;
+			for(var i=0; i<rows.length; i++){
+				if(self.getDataByRow(rows[i]) == removedObject.src){
+					rows[i].parentNode.removeChild(rows[i]);
+					break;
+				}
+			}
+			self.render();
+		});
+		dojo.event.connect(this.store, "onUpdateField", function(obj, fieldPath, val){
+			var row = self.getRow(obj);
+			var idx = self.getColumnIndex(fieldPath);
+			if(row && row.cells[idx] && self.columns[idx]){
+				self.fillCell(row.cells[idx], self.columns[idx], val);
+			}
+		});
+	},
+	postCreate: function(){
+		//	summary
+		//	finish widget initialization.
+		this.store.keyField = this.valueField;
+
+		if(this.domNode){
+			//	start by making sure domNode is a table element;
+			if(this.domNode.nodeName.toLowerCase() != "table"){
+			}
+
+			//	see if there is columns set up already
+			if(this.domNode.getElementsByTagName("thead")[0]){
+				var head=this.domNode.getElementsByTagName("thead")[0];
+				if(this.headClass.length > 0){
+					head.className = this.headClass;
+				}
+				dojo.html.disableSelection(this.domNode);
+				this.parseMetadata(head);
+
+				var header="td";
+				if(head.getElementsByTagName(header).length==0){
+					header="th";
+				}
+				var headers = head.getElementsByTagName(header);
+				for(var i=0; i<headers.length; i++){
+					if(!this.columns[i].noSort){
+						dojo.event.connect(headers[i], "onclick", this, "onSort");
+					}
+				}
+			} else {
+				this.domNode.appendChild(document.createElement("thead"));
+			}
+
+			// if the table doesn't have a tbody already, add one and grab a reference to it
+			if (this.domNode.tBodies.length < 1) {
+				var body = document.createElement("tbody");
+				this.domNode.appendChild(body);
+			} else {
+				var body = this.domNode.tBodies[0];
+			}
+
+			if (this.tbodyClass.length > 0){
+				body.className = this.tbodyClass;
+			}
+			dojo.event.connect(body, "onclick", this, "onSelect");
+			this.parseData(body);
+		}
+	}
+});

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FisheyeList.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FisheyeList.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FisheyeList.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/FisheyeList.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,745 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.FisheyeList");
+
+//
+// TODO
+// fix SVG support, and turn it on only if the browser supports it
+// fix really long labels in vertical mode
+//
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.util");
+dojo.require("dojo.event.*");
+
+
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeList",
+	dojo.widget.HtmlWidget,
+function(){
+	/*
+	 * summary
+	 *	Menu similar to the fish eye menu on the Mac OS
+	 * usage
+	 *	<div dojoType="FisheyeList"
+	 *	itemWidth="40" itemHeight="40"
+	 *	itemMaxWidth="150" itemMaxHeight="150"
+	 *	orientation="horizontal"
+	 *	effectUnits="2"
+	 *	itemPadding="10"
+	 *	attachEdge="center"
+	 *	labelEdge="bottom">
+	 *
+	 *		<div dojoType="FisheyeListItem"
+	 *			id="item1"
+	 *			onclick="alert('click on' + this.caption + '(from widget id ' + this.widgetId + ')!');"
+	 *			caption="Item 1"
+	 *			iconsrc="images/fisheye_1.png">
+	 *		</div>
+	 *		...
+	 *	</div>
+	 */
+	 
+	this.pos = {x: -1, y: -1};		// current cursor position, relative to the grid
+
+	this.EDGE = {
+		CENTER: 0,
+		LEFT: 1,
+		RIGHT: 2,
+		TOP: 3,
+		BOTTOM: 4
+	};
+	
+	// for conservative trigger mode, when triggered, timerScale is gradually increased from 0 to 1
+	this.timerScale = 1.0;
+
+},
+{
+	templateString: '<div class="dojoHtmlFisheyeListBar"></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/FisheyeList.css"),
+
+	isContainer: true,
+	snarfChildDomOutput: true,
+
+	// itemWidth: Integer
+	//	width of menu item (in pixels) in it's dormant state (when the mouse is far away)
+	itemWidth: 40,
+
+	// itemHeight: Integer
+	//	height of menu item (in pixels) in it's dormant state (when the mouse is far away)
+	itemHeight: 40,
+
+	// itemMaxWidth: Integer
+	//	width of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
+	itemMaxWidth: 150,
+
+	// itemMaxHeight: Integer
+	//	height of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
+	itemMaxHeight: 150,
+
+
+	// orientation: String
+	//	orientation of the menu, either "horizontal" or "vertical"
+	orientation: 'horizontal',
+
+	// conservativeTrigger: Boolean
+	//	if true, don't start enlarging menu items until mouse is over an image;
+	//	if false, start enlarging menu items as the mouse moves near them.
+	conservativeTrigger: false,
+
+	// effectUnits: Number
+	//	controls how much reaction the menu makes, relative to the distance of the mouse from the menu
+	effectUnits: 2,
+	
+	// itemPadding: Integer
+	//	padding (in pixels) betweeen each menu item
+	itemPadding: 10,
+
+	// attachEdge: String
+	//	controls the border that the menu items don't expand past;
+	//	for example, if set to "top", then the menu items will drop downwards as they expand.
+	// values
+	//	"center", "left", "right", "top", "bottom".
+	attachEdge: 'center',
+
+	// labelEdge: String
+	//	controls were the labels show up in relation to the menu item icons
+	// values
+	//	"center", "left", "right", "top", "bottom".
+	labelEdge: 'bottom',
+
+	// enableCrappySvgSupportBoolean
+	//	for browsers that support svg, use the svg image (specified in FisheyeListIem.svgSrc)
+	//	rather than the iconSrc image attribute
+	enableCrappySvgSupport: false,
+
+	fillInTemplate: function() {
+		dojo.html.disableSelection(this.domNode);
+
+		this.isHorizontal = (this.orientation == 'horizontal');
+		this.selectedNode = -1;
+
+		this.isOver = false;
+		this.hitX1 = -1;
+		this.hitY1 = -1;
+		this.hitX2 = -1;
+		this.hitY2 = -1;
+
+		//
+		// only some edges make sense...
+		//
+		this.anchorEdge = this._toEdge(this.attachEdge, this.EDGE.CENTER);
+		this.labelEdge  = this._toEdge(this.labelEdge,  this.EDGE.TOP);
+
+		if ( this.isHorizontal && (this.anchorEdge == this.EDGE.LEFT  )){ this.anchorEdge = this.EDGE.CENTER; }
+		if ( this.isHorizontal && (this.anchorEdge == this.EDGE.RIGHT )){ this.anchorEdge = this.EDGE.CENTER; }
+		if (!this.isHorizontal && (this.anchorEdge == this.EDGE.TOP   )){ this.anchorEdge = this.EDGE.CENTER; }
+		if (!this.isHorizontal && (this.anchorEdge == this.EDGE.BOTTOM)){ this.anchorEdge = this.EDGE.CENTER; }
+
+		if (this.labelEdge == this.EDGE.CENTER){ this.labelEdge = this.EDGE.TOP; }
+		if ( this.isHorizontal && (this.labelEdge == this.EDGE.LEFT  )){ this.labelEdge = this.EDGE.TOP; }
+		if ( this.isHorizontal && (this.labelEdge == this.EDGE.RIGHT )){ this.labelEdge = this.EDGE.TOP; }
+		if (!this.isHorizontal && (this.labelEdge == this.EDGE.TOP   )){ this.labelEdge = this.EDGE.LEFT; }
+		if (!this.isHorizontal && (this.labelEdge == this.EDGE.BOTTOM)){ this.labelEdge = this.EDGE.LEFT; }
+
+		//
+		// figure out the proximity size
+		//
+		this.proximityLeft   = this.itemWidth  * (this.effectUnits - 0.5);
+		this.proximityRight  = this.itemWidth  * (this.effectUnits - 0.5);
+		this.proximityTop    = this.itemHeight * (this.effectUnits - 0.5);
+		this.proximityBottom = this.itemHeight * (this.effectUnits - 0.5);
+
+		if (this.anchorEdge == this.EDGE.LEFT){
+			this.proximityLeft = 0;
+		}
+		if (this.anchorEdge == this.EDGE.RIGHT){
+			this.proximityRight = 0;
+		}
+		if (this.anchorEdge == this.EDGE.TOP){
+			this.proximityTop = 0;
+		}
+		if (this.anchorEdge == this.EDGE.BOTTOM){
+			this.proximityBottom = 0;
+		}
+		if (this.anchorEdge == this.EDGE.CENTER){
+			this.proximityLeft   /= 2;
+			this.proximityRight  /= 2;
+			this.proximityTop    /= 2;
+			this.proximityBottom /= 2;
+		}
+	},
+	
+	postCreate: function() {
+		this._initializePositioning();
+
+		//
+		// in liberal trigger mode, activate menu whenever mouse is close
+		//
+		if( !this.conservativeTrigger ){
+			dojo.event.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
+		}
+		
+		// Deactivate the menu if mouse is moved off screen (doesn't work for FF?)
+		dojo.event.connect(document.documentElement, "onmouseout", this, "_onBodyOut");
+		dojo.event.connect(this, "addChild", this, "_initializePositioning");
+	},
+
+	_initializePositioning: function(){
+		this.itemCount = this.children.length;
+
+		this.barWidth  = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth;
+		this.barHeight = (this.isHorizontal ? 1 : this.itemCount) * this.itemHeight;
+
+		this.totalWidth  = this.proximityLeft + this.proximityRight  + this.barWidth;
+		this.totalHeight = this.proximityTop  + this.proximityBottom + this.barHeight;
+
+		//
+		// calculate effect ranges for each item
+		//
+		for (var i=0; i<this.children.length; i++){
+
+			this.children[i].posX = this.itemWidth  * (this.isHorizontal ? i : 0);
+			this.children[i].posY = this.itemHeight * (this.isHorizontal ? 0 : i);
+
+			this.children[i].cenX = this.children[i].posX + (this.itemWidth  / 2);
+			this.children[i].cenY = this.children[i].posY + (this.itemHeight / 2);
+
+			var isz = this.isHorizontal ? this.itemWidth : this.itemHeight;
+			var r = this.effectUnits * isz;
+			var c = this.isHorizontal ? this.children[i].cenX : this.children[i].cenY;
+			var lhs = this.isHorizontal ? this.proximityLeft : this.proximityTop;
+			var rhs = this.isHorizontal ? this.proximityRight : this.proximityBottom;
+			var siz = this.isHorizontal ? this.barWidth : this.barHeight;
+
+			var range_lhs = r;
+			var range_rhs = r;
+
+			if (range_lhs > c+lhs){ range_lhs = c+lhs; }
+			if (range_rhs > (siz-c+rhs)){ range_rhs = siz-c+rhs; }
+
+			this.children[i].effectRangeLeft = range_lhs / isz;
+			this.children[i].effectRangeRght = range_rhs / isz;
+
+			//dojo.debug('effect range for '+i+' is '+range_lhs+'/'+range_rhs);
+		}
+
+		//
+		// create the bar
+		//
+		this.domNode.style.width = this.barWidth + 'px';
+		this.domNode.style.height = this.barHeight + 'px';
+
+		//
+		// position the items
+		//
+		for (var i=0; i<this.children.length; i++){
+			var itm = this.children[i];
+			var elm = itm.domNode;
+			elm.style.left   = itm.posX + 'px';
+			elm.style.top    = itm.posY + 'px';
+			elm.style.width  = this.itemWidth + 'px';
+			elm.style.height = this.itemHeight + 'px';
+			
+			if ( itm.svgNode ) {
+				itm.svgNode.style.position = 'absolute';
+				itm.svgNode.style.left = this.itemPadding+'%';
+				itm.svgNode.style.top = this.itemPadding+'%';
+				itm.svgNode.style.width = (100 - 2 * this.itemPadding) + '%';
+				itm.svgNode.style.height = (100 - 2 * this.itemPadding) + '%';
+				itm.svgNode.style.zIndex = 1;
+	
+				itm.svgNode.setSize(this.itemWidth, this.itemHeight);
+			} else {
+				itm.imgNode.style.left = this.itemPadding+'%';
+				itm.imgNode.style.top = this.itemPadding+'%';
+				itm.imgNode.style.width = (100 - 2 * this.itemPadding) + '%';
+				itm.imgNode.style.height = (100 - 2 * this.itemPadding) + '%';
+			}
+		}
+
+		//
+		// calc the grid
+		//
+		this._calcHitGrid();
+	},
+
+	_onBodyOut: function(/*Event*/ e){
+		// clicking over an object inside of body causes this event to fire; ignore that case
+		if( dojo.html.overElement(dojo.body(), e) ){
+			return;
+		}
+		this._setDormant(e);
+	},
+
+	_setDormant: function(/*Event*/ e){
+		// summary: called when mouse moves out of menu's range
+
+		if( !this.isOver ){ return; }	// already dormant?
+		this.isOver = false;
+
+		if ( this.conservativeTrigger ) {
+			// user can't re-trigger the menu expansion
+			// until he mouses over a icon again
+			dojo.event.disconnect(document.documentElement, "onmousemove", this, "_onMouseMove");
+		}
+		this._onGridMouseMove(-1, -1);
+	},
+
+	_setActive: function(/*Event*/ e){
+		// summary: called when mouse is moved into menu's range
+
+		if( this.isOver ){ return; }	// already activated?
+		this.isOver = true;
+
+		if ( this.conservativeTrigger ) {
+			// switch event handlers so that we handle mouse events from anywhere near
+			// the menu
+			dojo.event.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
+
+			this.timerScale=0.0;
+
+			// call mouse handler to do some initial necessary calculations/positioning
+			this._onMouseMove(e);
+
+			// slowly expand the icon size so it isn't jumpy
+			this._expandSlowly();
+		}
+	},
+
+	_onMouseMove: function(/*Event*/ e) {
+		// summary: called when mouse is moved
+		if ((e.pageX >= this.hitX1) && (e.pageX <= this.hitX2) &&
+			(e.pageY >= this.hitY1) && (e.pageY <= this.hitY2)){
+			if( !this.isOver ){
+				this._setActive(e);
+			}
+			this._onGridMouseMove(e.pageX-this.hitX1, e.pageY-this.hitY1);
+		}else{
+			if (this.isOver){
+				this._setDormant(e);
+			}
+		}
+	},
+
+	onResized: function() {
+		this._calcHitGrid();
+	},
+
+	_onGridMouseMove: function(x, y){
+		// summary: called when mouse is moved in the vicinity of the menu
+		this.pos = {x:x, y:y};
+		this._paint();
+	},
+	
+	_paint: function(){
+		var x=this.pos.x;
+		var y=this.pos.y;
+
+		if( this.itemCount <= 0 ){ return; }
+
+		//
+		// figure out our main index
+		//
+		var pos = this.isHorizontal ? x : y;
+		var prx = this.isHorizontal ? this.proximityLeft : this.proximityTop;
+		var siz = this.isHorizontal ? this.itemWidth : this.itemHeight;
+		var sim = this.isHorizontal ? 
+			(1.0-this.timerScale)*this.itemWidth + this.timerScale*this.itemMaxWidth :
+			(1.0-this.timerScale)*this.itemHeight + this.timerScale*this.itemMaxHeight ;
+
+		var cen = ((pos - prx) / siz) - 0.5;
+		var max_off_cen = (sim / siz) - 0.5;
+
+		if (max_off_cen > this.effectUnits){ max_off_cen = this.effectUnits; }
+
+		//
+		// figure out our off-axis weighting
+		//
+		var off_weight = 0;
+
+		if (this.anchorEdge == this.EDGE.BOTTOM){
+			var cen2 = (y - this.proximityTop) / this.itemHeight;
+			off_weight = (cen2 > 0.5) ? 1 : y / (this.proximityTop + (this.itemHeight / 2));
+		}
+		if (this.anchorEdge == this.EDGE.TOP){
+			var cen2 = (y - this.proximityTop) / this.itemHeight;
+			off_weight = (cen2 < 0.5) ? 1 : (this.totalHeight - y) / (this.proximityBottom + (this.itemHeight / 2));
+		}
+		if (this.anchorEdge == this.EDGE.RIGHT){
+			var cen2 = (x - this.proximityLeft) / this.itemWidth;
+			off_weight = (cen2 > 0.5) ? 1 : x / (this.proximityLeft + (this.itemWidth / 2));
+		}
+		if (this.anchorEdge == this.EDGE.LEFT){
+			var cen2 = (x - this.proximityLeft) / this.itemWidth;
+			off_weight = (cen2 < 0.5) ? 1 : (this.totalWidth - x) / (this.proximityRight + (this.itemWidth / 2));
+		}
+		if (this.anchorEdge == this.EDGE.CENTER){
+			if (this.isHorizontal){
+				off_weight = y / (this.totalHeight);
+			}else{
+				off_weight = x / (this.totalWidth);
+			}
+
+			if (off_weight > 0.5){
+				off_weight = 1 - off_weight;
+			}
+
+			off_weight *= 2;
+		}
+
+		//
+		// set the sizes
+		//
+		for(var i=0; i<this.itemCount; i++){
+			var weight = this._weighAt(cen, i);
+			if (weight < 0){weight = 0;}
+			this._setItemSize(i, weight * off_weight);
+		}
+
+		//
+		// set the positions
+		//
+
+		var main_p = Math.round(cen);
+		var offset = 0;
+
+		if (cen < 0){
+			main_p = 0;
+
+		}else if (cen > this.itemCount - 1){
+
+			main_p = this.itemCount -1;
+
+		}else{
+
+			offset = (cen - main_p) * ((this.isHorizontal ? this.itemWidth : this.itemHeight) - this.children[main_p].sizeMain);
+		}
+
+		this._positionElementsFrom(main_p, offset);
+	},
+
+	_weighAt: function(/*Integer*/ cen, /*Integer*/ i){
+		var dist = Math.abs(cen - i);
+		var limit = ((cen - i) > 0) ? this.children[i].effectRangeRght : this.children[i].effectRangeLeft;
+		return (dist > limit) ? 0 : (1 - dist / limit);			// Integer
+	},
+
+	_setItemSize: function(p, scale){
+		scale *= this.timerScale;
+		var w = Math.round(this.itemWidth  + ((this.itemMaxWidth  - this.itemWidth ) * scale));
+		var h = Math.round(this.itemHeight + ((this.itemMaxHeight - this.itemHeight) * scale));
+
+		if (this.isHorizontal){
+
+			this.children[p].sizeW = w;
+			this.children[p].sizeH = h;
+
+			this.children[p].sizeMain = w;
+			this.children[p].sizeOff  = h;
+
+			var y = 0;
+			if (this.anchorEdge == this.EDGE.TOP){
+				y = (this.children[p].cenY - (this.itemHeight / 2));
+			}else if (this.anchorEdge == this.EDGE.BOTTOM){
+				y = (this.children[p].cenY - (h - (this.itemHeight / 2)));
+			}else{
+				y = (this.children[p].cenY - (h / 2));
+			}
+
+			this.children[p].usualX = Math.round(this.children[p].cenX - (w / 2));
+			this.children[p].domNode.style.top  = y + 'px';
+			this.children[p].domNode.style.left  = this.children[p].usualX + 'px';
+
+		}else{
+
+			this.children[p].sizeW = w;
+			this.children[p].sizeH = h;
+
+			this.children[p].sizeOff  = w;
+			this.children[p].sizeMain = h;
+
+			var x = 0;
+			if (this.anchorEdge == this.EDGE.LEFT){
+				x = this.children[p].cenX - (this.itemWidth / 2);
+			}else if (this.anchorEdge == this.EDGE.RIGHT){
+				x = this.children[p].cenX - (w - (this.itemWidth / 2));
+			}else{
+				x = this.children[p].cenX - (w / 2);
+			}
+
+			this.children[p].domNode.style.left = x + 'px';
+			this.children[p].usualY = Math.round(this.children[p].cenY - (h / 2));
+
+			this.children[p].domNode.style.top  = this.children[p].usualY + 'px';
+		}
+
+		this.children[p].domNode.style.width  = w + 'px';
+		this.children[p].domNode.style.height = h + 'px';
+
+		if (this.children[p].svgNode){
+			this.children[p].svgNode.setSize(w, h);
+		}
+	},
+
+	_positionElementsFrom: function(p, offset){
+
+		var pos = 0;
+
+		if (this.isHorizontal){
+			pos = Math.round(this.children[p].usualX + offset);
+			this.children[p].domNode.style.left = pos + 'px';
+		}else{
+			pos = Math.round(this.children[p].usualY + offset);
+			this.children[p].domNode.style.top = pos + 'px';
+		}
+		this._positionLabel(this.children[p]);
+
+
+		//
+		// position before
+		//
+		var bpos = pos;
+		for(var i=p-1; i>=0; i--){
+			bpos -= this.children[i].sizeMain;
+
+			if (this.isHorizontal){
+				this.children[i].domNode.style.left = bpos + 'px';
+			}else{
+				this.children[i].domNode.style.top = bpos + 'px';
+			}
+			this._positionLabel(this.children[i]);
+		}
+
+		//
+		// position after
+		//
+		var apos = pos;
+		for(var i=p+1; i<this.itemCount; i++){
+			apos += this.children[i-1].sizeMain;
+
+			if (this.isHorizontal){
+				this.children[i].domNode.style.left = apos + 'px';
+			}else{
+				this.children[i].domNode.style.top = apos + 'px';
+			}
+			this._positionLabel(this.children[i]);
+		}
+
+	},
+
+	_positionLabel: function(itm){
+
+		var x = 0;
+		var y = 0;
+		
+		var mb = dojo.html.getMarginBox(itm.lblNode);
+
+		if (this.labelEdge == this.EDGE.TOP){
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
+			y = -mb.height;
+		}
+
+		if (this.labelEdge == this.EDGE.BOTTOM){
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
+			y = itm.sizeH;
+		}
+
+		if (this.labelEdge == this.EDGE.LEFT){
+			x = -mb.width;
+			y = Math.round((itm.sizeH / 2) - (mb.height / 2));
+		}
+
+		if (this.labelEdge == this.EDGE.RIGHT){
+			x = itm.sizeW;
+			y = Math.round((itm.sizeH / 2) - (mb.height / 2));
+		}
+
+		itm.lblNode.style.left = x + 'px';
+		itm.lblNode.style.top  = y + 'px';
+	},
+
+	_calcHitGrid: function(){
+
+		var pos = dojo.html.getAbsolutePosition(this.domNode, true);
+
+		this.hitX1 = pos.x - this.proximityLeft;
+		this.hitY1 = pos.y - this.proximityTop;
+		this.hitX2 = this.hitX1 + this.totalWidth;
+		this.hitY2 = this.hitY1 + this.totalHeight;
+
+		//dojo.debug(this.hitX1+','+this.hitY1+' // '+this.hitX2+','+this.hitY2);
+	},
+
+	_toEdge: function(inp, def){
+		return this.EDGE[inp.toUpperCase()] || def;
+	},
+	
+	_expandSlowly: function(){
+		// summary: slowly expand the image to user specified max size
+		if( !this.isOver ){ return; }
+		this.timerScale += 0.2;
+		this._paint();
+		if ( this.timerScale<1.0 ) {
+			dojo.lang.setTimeout(this, "_expandSlowly", 10);
+		}
+	},
+
+	destroy: function(){
+		// need to disconnect when we destroy
+		dojo.event.disconnect(document.documentElement, "onmouseout", this, "_onBodyOut");
+		dojo.event.disconnect(document.documentElement, "onmousemove", this, "_onMouseMove");
+		dojo.widget.FisheyeList.superclass.destroy.call(this);
+	}
+});
+
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeListItem",
+	dojo.widget.HtmlWidget,
+{
+	/*
+	 * summary
+	 *	Menu item inside of a FisheyeList.
+	 *	See FisheyeList documentation for details on usage.
+	 */
+
+	// iconSrc: String
+	//	pathname to image file (jpg, gif, png, etc.) of icon for this menu item
+	iconSrc: "",
+
+	// svgSrc: String
+	//	pathname to svg file of icon for this menu item
+	svgSrc: "",
+	
+	// caption: String
+	//	label to print next to the icon, when it is moused-over
+	caption: "",
+
+	// id: String
+	//	will be set to the id of the orginal div element
+	id: "",
+
+	_blankImgPath: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+
+	templateString:
+		'<div class="dojoHtmlFisheyeListItem">' +
+		'  <img class="dojoHtmlFisheyeListItemImage" dojoAttachPoint="imgNode" dojoAttachEvent="onMouseOver;onMouseOut;onClick">' +
+		'  <div class="dojoHtmlFisheyeListItemLabel" dojoAttachPoint="lblNode"></div>' +
+		'</div>',
+	
+	fillInTemplate: function() {
+		//
+		// set image
+		// TODO: turn on/off SVG support based on browser version.
+		// this.parent.enableCrappySvgSupport is not available to this function
+		//
+		if (this.svgSrc != ""){
+			this.svgNode = this._createSvgNode(this.svgSrc);
+			this.domNode.appendChild(this.svgNode);
+			this.imgNode.style.display = 'none';
+		} else if((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4)==".png")&&(dojo.render.html.ie)&&(!dojo.render.html.ie70)){
+			/* we set the id of the new fisheyeListItem to the id of the div defined in the HTML */
+			if (dojo.dom.hasParent(this.imgNode) && this.id != ""){
+				var parent = this.imgNode.parentNode;
+				parent.setAttribute("id", this.id);
+			}
+			this.imgNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='scale')";
+			this.imgNode.src = this._blankImgPath.toString();
+		} else {
+			if (dojo.dom.hasParent(this.imgNode) && this.id != ""){
+				var parent = this.imgNode.parentNode;
+				parent.setAttribute("id", this.id);
+			}
+			this.imgNode.src = this.iconSrc;
+		}
+
+		//
+		// Label
+		//
+		if ( this.lblNode ) {
+			this.lblNode.appendChild(document.createTextNode(this.caption));
+		}
+		dojo.html.disableSelection(this.domNode);
+	},
+	
+	_createSvgNode: function(src){
+		var elm = document.createElement('embed');
+		elm.src = src;
+		elm.type = 'image/svg+xml';
+		//elm.style.border = '1px solid black';
+		elm.style.width = '1px';
+		elm.style.height = '1px';
+		elm.loaded = 0;
+		elm.setSizeOnLoad = false;
+
+		elm.onload = function(){
+			this.svgRoot = this.getSVGDocument().rootElement;
+			this.svgDoc = this.getSVGDocument().documentElement;
+			this.zeroWidth = this.svgRoot.width.baseVal.value;
+			this.zeroHeight = this.svgRoot.height.baseVal.value;
+			this.loaded = true;
+
+			if (this.setSizeOnLoad){
+				this.setSize(this.setWidth, this.setHeight);
+			}
+		}
+
+		elm.setSize = function(w, h){
+			if (!this.loaded){
+				this.setWidth = w;
+				this.setHeight = h;
+				this.setSizeOnLoad = true;
+				return;
+			}
+
+			this.style.width = w+'px';
+			this.style.height = h+'px';
+			this.svgRoot.width.baseVal.value = w;
+			this.svgRoot.height.baseVal.value = h;
+
+			var scale_x = w / this.zeroWidth;
+			var scale_y = h / this.zeroHeight;
+
+			for(var i=0; i<this.svgDoc.childNodes.length; i++){
+				if (this.svgDoc.childNodes[i].setAttribute){
+					this.svgDoc.childNodes[i].setAttribute( "transform", "scale("+scale_x+","+scale_y+")" );
+				}
+			}
+		}
+
+		return elm;
+	},
+
+	onMouseOver: function(/*Event*/ e) {
+		// summary: callback when user moves mouse over this menu item
+		// in conservative mode, don't activate the menu until user mouses over an icon
+		if( !this.parent.isOver ){
+			this.parent._setActive(e);
+		}
+		if ( this.caption != "" ) {
+			dojo.html.addClass(this.lblNode, "selected");
+			this.parent._positionLabel(this);
+		}
+	},
+	
+	onMouseOut: function(/*Event*/ e) {
+		// summary: callback when user moves mouse off of this menu item
+		dojo.html.removeClass(this.lblNode, "selected");
+	},
+
+	onClick: function(/*Event*/ e) {
+		// summary: user overridable callback when user clicks this menu item
+	}
+});
+