You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by he...@apache.org on 2006/11/13 23:55:14 UTC
svn commit: r474551 [36/49] - in
/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo:
./ src/ src/alg/ src/animation/ src/cal/ src/charting/ src/charting/svg/
src/charting/vml/ src/collections/ src/crypto/ src/data/ src/data/cs...
Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Menu2.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Menu2.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Menu2.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Menu2.js Mon Nov 13 14:54:45 2006
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2004-2005, The Dojo Foundation
+ Copyright (c) 2004-2006, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
@@ -9,370 +9,335 @@
*/
dojo.provide("dojo.widget.Menu2");
-dojo.provide("dojo.widget.html.Menu2");
-dojo.provide("dojo.widget.PopupMenu2");
-dojo.provide("dojo.widget.MenuItem2");
-
-dojo.require("dojo.html");
-dojo.require("dojo.style");
-dojo.require("dojo.event.*");
-dojo.require("dojo.widget.*");
-dojo.require("dojo.widget.HtmlWidget");
-
-
-dojo.widget.PopupMenu2 = function(){
- dojo.widget.HtmlWidget.call(this);
- this.items = []; // unused???
- this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it becomes context menu for those nodes
- this.queueOnAnimationFinish = [];
-}
-
-dojo.inherits(dojo.widget.PopupMenu2, dojo.widget.HtmlWidget);
-
-dojo.lang.extend(dojo.widget.PopupMenu2, {
- widgetType: "PopupMenu2",
- isContainer: true,
+dojo.require("dojo.widget.PopupContainer");
+
+// summary
+// provides a menu that can be used as a context menu (typically shown by right-click),
+// or as the drop down on a DropDownButton, ComboButton, etc.
+dojo.widget.defineWidget(
+ "dojo.widget.PopupMenu2",
+ dojo.widget.PopupContainer,
+ function(){
+ this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it becomes context menu for those nodes
+
+ this.eventNames = {
+ open: ""
+ };
+ },
+{
snarfChildDomOutput: true,
- currentSubmenu: null,
- currentSubmenuTrigger: null,
- parentMenu: null,
- isShowing: false,
- menuX: 0,
- menuY: 0,
- menuWidth: 0,
- menuHeight: 0,
- menuIndex: 0,
-
- domNode: null,
- containerNode: null,
-
- templateString: '<div><div dojoAttachPoint="containerNode"></div></div>',
- templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlMenu2.css"),
-
- itemHeight: 18,
- iconGap: 1,
- accelGap: 10,
- submenuGap: 2,
- finalGap: 5,
- submenuIconSize: 4,
- separatorHeight: 9,
+ // String
+ // if "default" event names are based on widget id, otherwise user must define
+ // TODO: write real documentation about the events
+ eventNaming: "default",
+
+ templateString: '<table class="dojoPopupMenu2" border=0 cellspacing=0 cellpadding=0 style="display: none;"><tbody dojoAttachPoint="containerNode"></tbody></table>',
+ templateCssPath: dojo.uri.dojoUri("src/widget/templates/Menu2.css"),
+ templateCssString: "",
+
+ // Integer
+ // number of milliseconds before hovering (without clicking) causes the submenu to automatically open
submenuDelay: 500,
+
+ // Integer
+ // a submenu usually appears to the right, but slightly overlapping, it's parent menu;
+ // this controls the number of pixels the two menus overlap.
submenuOverlap: 5,
+
+ // Boolean
+ // if true, right clicking anywhere on the window will cause this context menu to open;
+ // if false, must specify targetNodeIds
contextMenuForWindow: false,
- openEvent: null,
- submenuIconSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_off.gif").toString(),
- submenuIconOnSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_on.gif").toString(),
+ // Array
+ // Array of dom node ids of nodes to attach to
+ targetNodeIds: [],
+
+ initialize: function(args, frag) {
+ if (this.eventNaming == "default") {
+ for (var eventName in this.eventNames) {
+ this.eventNames[eventName] = this.widgetId+"/"+eventName;
+ }
+ }
+ },
postCreate: function(){
-
- dojo.html.addClass(this.domNode, 'dojoPopupMenu2');
- dojo.html.addClass(this.containerNode, 'dojoPopupMenu2Client');
-
- this.domNode.style.left = '-9999px'
- this.domNode.style.top = '-9999px'
-
if (this.contextMenuForWindow){
- var doc = document.documentElement || dojo.html.body();
- dojo.event.connect(doc, "oncontextmenu", this, "onOpen");
+ var doc = dojo.body();
+ this.bindDomNode(doc);
} else if ( this.targetNodeIds.length > 0 ){
- for(var i=0; i<this.targetNodeIds.length; i++){
- this.bindDomNode(this.targetNodeIds[i]);
- }
+ dojo.lang.forEach(this.targetNodeIds, this.bindDomNode, this);
}
- this.layoutMenuSoon();
+ this._subscribeSubitemsOnOpen();
+ },
+
+ _subscribeSubitemsOnOpen: function() {
+ var subItems = this.getChildrenOfType(dojo.widget.MenuItem2);
+
+ for(var i=0; i<subItems.length; i++) {
+ dojo.event.topic.subscribe(this.eventNames.open, subItems[i], "menuOpen")
+ }
},
- // get open event for current menu
getTopOpenEvent: function() {
+ // summary: get event that initially caused current chain of menus to open
var menu = this;
- while (menu.parent){ menu = menu.parent; }
- return menu.openEvent;
+ while (menu.parentPopup){ menu = menu.parentPopup; }
+ return menu.openEvent; // Event
},
- // attach menu to given node
- bindDomNode: function(node){
- dojo.event.connect(dojo.byId(node), "oncontextmenu", this, "onOpen");
- },
+ bindDomNode: function(/*String|DomNode*/ node){
+ // summary: attach menu to given node
+ node = dojo.byId(node);
- // detach menu from given node
- unBindDomNode: function(node){
- dojo.event.kwDisconnect({
- srcObj: dojo.byId(node),
+ var win = dojo.html.getElementWindow(node);
+ if(dojo.html.isTag(node,'iframe') == 'iframe'){
+ win = dojo.html.iframeContentWindow(node);
+ node = dojo.withGlobal(win, dojo.body);
+ }
+ // fixes node so that it supports oncontextmenu if not natively supported, Konqueror, Opera more?
+ dojo.widget.Menu2.OperaAndKonqFixer.fixNode(node);
+
+ dojo.event.kwConnect({
+ srcObj: node,
srcFunc: "oncontextmenu",
targetObj: this,
targetFunc: "onOpen",
once: true
});
- },
- layoutMenuSoon: function(){
-
- dojo.lang.setTimeout(this, "layoutMenu", 0);
+ //normal connect does not work if document.designMode is on in FF, use addListener instead
+ if(dojo.render.html.moz && win.document.designMode.toLowerCase() == 'on'){
+ dojo.event.browser.addListener(node, "contextmenu", dojo.lang.hitch(this, "onOpen"));
+ }
+ dojo.widget.PopupManager.registerWin(win);
},
- layoutMenu: function(){
-
- // determine menu width
-
- var max_label_w = 0;
- var max_accel_w = 0;
-
- for(var i=0; i<this.children.length; i++){
+ unBindDomNode: function(/*String|DomNode*/ nodeName){
+ // summary: detach menu from given node
+ var node = dojo.byId(nodeName);
+ dojo.event.kwDisconnect({
+ srcObj: node,
+ srcFunc: "oncontextmenu",
+ targetObj: this,
+ targetFunc: "onOpen",
+ once: true
+ });
- if (this.children[i].getLabelWidth){
+ // cleans a fixed node, konqueror and opera
+ dojo.widget.Menu2.OperaAndKonqFixer.cleanNode(node);
+ },
- max_label_w = Math.max(max_label_w, this.children[i].getLabelWidth());
- }
+ _moveToNext: function(/*Event*/ evt){
+ this._highlightOption(1);
+ return true; //do not pass to parent menu
+ },
- if (dojo.lang.isFunction(this.children[i].getAccelWidth)){
+ _moveToPrevious: function(/*Event*/ evt){
+ this._highlightOption(-1);
+ return true; //do not pass to parent menu
+ },
- max_accel_w = Math.max(max_accel_w, this.children[i].getAccelWidth());
+ _moveToParentMenu: function(/*Event*/ evt){
+ if(this._highlighted_option && this.parentPopup){
+ //only process event in the focused menu
+ //and its immediate parentPopup to support
+ //MenuBar2
+ if(evt._menu2UpKeyProcessed){
+ return true; //do not pass to parent menu
+ }else{
+ this._highlighted_option.onUnhover();
+ this.closeSubpopup();
+ evt._menu2UpKeyProcessed = true;
}
}
-
- if( isNaN(max_label_w) || isNaN(max_accel_w) ){
- // Browser needs some more time to calculate sizes
- this.layoutMenuSoon();
- return;
- }
-
- var clientLeft = dojo.style.getPixelValue(this.domNode, "padding-left", true) + dojo.style.getPixelValue(this.containerNode, "padding-left", true);
- var clientTop = dojo.style.getPixelValue(this.domNode, "padding-top", true) + dojo.style.getPixelValue(this.containerNode, "padding-top", true);
-
- if( isNaN(clientLeft) || isNaN(clientTop) ){
- // Browser needs some more time to calculate sizes
- this.layoutMenuSoon();
- return;
- }
-
- var y = clientTop;
- var max_item_width = 0;
-
- for(var i=0; i<this.children.length; i++){
-
- var ch = this.children[i];
-
- ch.layoutItem(max_label_w, max_accel_w);
-
- ch.topPosition = y;
-
- y += dojo.style.getOuterHeight(ch.domNode);
- max_item_width = Math.max(max_item_width, dojo.style.getOuterWidth(ch.domNode));
- }
-
- dojo.style.setContentWidth(this.containerNode, max_item_width);
- dojo.style.setContentHeight(this.containerNode, y-clientTop);
-
- dojo.style.setContentWidth(this.domNode, dojo.style.getOuterWidth(this.containerNode));
- dojo.style.setContentHeight(this.domNode, dojo.style.getOuterHeight(this.containerNode));
-
- this.menuWidth = dojo.style.getOuterWidth(this.domNode);
- this.menuHeight = dojo.style.getOuterHeight(this.domNode);
+ return false;
},
- open: function(x, y, parentMenu, explodeSrc){
-
- // NOTE: alex:
- // this couldn't have possibly worked. this.open wound up calling
- // this.close, which called open...etc..
- if (this.isShowing){ /* this.close(); */ return; }
-
- if ( !parentMenu ) {
- // record whenever a top level menu is opened
- dojo.widget.html.Menu2Manager.opened(this, explodeSrc);
- }
-
- //dojo.debug("open called for animation "+this.animationInProgress)
-
- // if I click right button and menu is opened, then it gets 2 commands: close -> open
- // so close enables animation and next "open" is put to queue to occur at new location
- if(this.animationInProgress){
- this.queueOnAnimationFinish.push(this.open, arguments);
- return;
+ _moveToChildMenu: function(/*Event*/ evt){
+ if(this._highlighted_option && this._highlighted_option.submenuId){
+ this._highlighted_option._onClick(true);
+ return true; //do not pass to parent menu
+ }
+ return false;
+ },
+
+ _selectCurrentItem: function(/*Event*/ evt){
+ if(this._highlighted_option){
+ this._highlighted_option._onClick();
+ return true;
+ }
+ return false;
+ },
+
+ processKey: function(/*Event*/ evt){
+ // summary
+ // callback to process key strokes
+ // return true to stop the event being processed by the
+ // parent popupmenu
+
+ if(evt.ctrlKey || evt.altKey || !evt.key){ return false; }
+
+ var rval = false;
+ switch(evt.key){
+ case evt.KEY_DOWN_ARROW:
+ rval = this._moveToNext(evt);
+ break;
+ case evt.KEY_UP_ARROW:
+ rval = this._moveToPrevious(evt);
+ break;
+ case evt.KEY_RIGHT_ARROW:
+ rval = this._moveToChildMenu(evt);
+ break;
+ case evt.KEY_LEFT_ARROW:
+ rval = this._moveToParentMenu(evt);
+ break;
+ case " ": //fall through
+ case evt.KEY_ENTER:
+ if(rval = this._selectCurrentItem(evt)){
+ break;
+ }
+ //fall through
+ case evt.KEY_ESCAPE:
+ dojo.widget.PopupManager.currentMenu.close();
+ rval = true;
+ break;
+ }
+
+ return rval;
+ },
+
+ _findValidItem: function(dir, curItem){
+ if(curItem){
+ curItem = dir>0 ? curItem.getNextSibling() : curItem.getPreviousSibling();
+ }
+
+ for(var i=0; i < this.children.length; ++i){
+ if(!curItem){
+ curItem = dir>0 ? this.children[0] : this.children[this.children.length-1];
+ }
+ //find next/previous visible menu item, not including separators
+ if(curItem.onHover && curItem.isShowing()){
+ return curItem;
+ }
+ curItem = dir>0 ? curItem.getNextSibling() : curItem.getPreviousSibling();
}
-
- var viewport = dojo.html.getViewportSize();
- var scrolloffset = dojo.html.getScrollOffset();
-
- var clientRect = {
- 'left' : scrolloffset[0],
- 'right' : scrolloffset[0] + viewport[0],
- 'top' : scrolloffset[1],
- 'bottom': scrolloffset[1] + viewport[1]
- };
-
- if (parentMenu){
- // submenu is opening
-
- if (x + this.menuWidth > clientRect.right){ x = x - (this.menuWidth + parentMenu.menuWidth - (2 * this.submenuOverlap)); }
-
- if (y + this.menuHeight > clientRect.bottom){ y = y -
- (this.menuHeight - (this.itemHeight + 5)); } // TODO: why 5?
-
+ },
+
+ _highlightOption: function(dir){
+ var item;
+ // || !this._highlighted_option.parentNode
+ if((!this._highlighted_option)){
+ item = this._findValidItem(dir);
}else{
- // top level menu is opening
- x+=scrolloffset[0];
- y+=scrolloffset[1];
- explodeSrc[0] += scrolloffset[0];
- explodeSrc[1] += scrolloffset[1];
-
- if (x < clientRect.left){ x = clientRect.left; }
- if (x + this.menuWidth > clientRect.right){ x = x - this.menuWidth; }
-
- if (y < clientRect.top){ y = clientRect.top; }
- if (y + this.menuHeight > clientRect.bottom){ y = y - this.menuHeight; }
+ item = this._findValidItem(dir, this._highlighted_option);
}
+ if(item){
+ if(this._highlighted_option) {
+ this._highlighted_option.onUnhover();
+ }
+ item.onHover();
+ dojo.html.scrollIntoView(item.domNode);
+ // navigate into the item table and select the first caption tag
+ try {
+ var node = dojo.html.getElementsByClass("dojoMenuItem2Label", item.domNode)[0];
+ node.focus();
+ } catch(e) { }
+ }
+ },
- this.parentMenu = parentMenu;
- this.explodeSrc = explodeSrc;
- this.menuIndex = parentMenu ? parentMenu.menuIndex + 1 : 1;
-
- this.menuX = x;
- this.menuY = y;
-
- // move the menu into position but make it invisible
- // (because when menus are initially constructed they are visible but off-screen)
- this.domNode.style.zIndex = 10 + this.menuIndex;
- this.domNode.style.left = x + 'px';
- this.domNode.style.top = y + 'px';
- this.domNode.style.display='none';
- this.domNode.style.position='absolute';
-
- // then use the user defined method to display it
- this.show();
-
- this.isShowing = true;
+ onItemClick: function(/*Widget*/ item) {
+ // summary: user defined function to handle clicks on an item
},
- close: function(){
- // If we are in the process of opening the menu and we are asked to close it,
- // we should really cancel the current animation, but for simplicity we will
- // just ignore the request
+ close: function(/*Boolean*/ force){
+ // summary: close the menu
if(this.animationInProgress){
- this.queueOnAnimationFinish.push(this.close, []);
+ dojo.widget.PopupMenu2.superclass.close.apply(this, arguments);
return;
}
- this.closeSubmenu();
- this.hide();
- this.isShowing = false;
- dojo.widget.html.Menu2Manager.closed(this);
- },
-
- onShow: function() {
- dojo.widget.HtmlWidget.prototype.onShow.call(this);
- this.processQueue();
- },
-
- // do events from queue
- processQueue: function() {
- if (!this.queueOnAnimationFinish.length) return;
-
- var func = this.queueOnAnimationFinish.shift();
- var args = this.queueOnAnimationFinish.shift();
-
- func.apply(this, args);
- },
-
- onHide: function() {
- dojo.widget.HtmlWidget.prototype.onHide.call(this);
-
- this.processQueue();
- },
-
-
- closeAll: function(){
-
- if (this.parentMenu){
- this.parentMenu.closeAll();
- }else{
- this.close();
+ if(this._highlighted_option){
+ this._highlighted_option.onUnhover();
}
+
+ dojo.widget.PopupMenu2.superclass.close.apply(this, arguments);
},
- closeSubmenu: function(){
- if (this.currentSubmenu == null){ return; }
+ closeSubpopup: function(force){
+ // summary: close the currently displayed submenu
+ if (this.currentSubpopup == null){ return; }
- this.currentSubmenu.close();
- this.currentSubmenu = null;
+ this.currentSubpopup.close(force);
+ this.currentSubpopup = null;
this.currentSubmenuTrigger.is_open = false;
- this.currentSubmenuTrigger.closedSubmenu();
+ this.currentSubmenuTrigger._closedSubmenu(force);
this.currentSubmenuTrigger = null;
},
- openSubmenu: function(submenu, from_item){
-
- var our_x = dojo.style.getPixelValue(this.domNode, 'left');
- var our_y = dojo.style.getPixelValue(this.domNode, 'top');
- var our_w = dojo.style.getOuterWidth(this.domNode);
- var item_y = from_item.topPosition;
-
- var x = our_x + our_w - this.submenuOverlap;
- var y = our_y + item_y;
-
- this.currentSubmenu = submenu;
- this.currentSubmenu.open(x, y, this, from_item.domNode);
+ _openSubmenu: function(submenu, from_item){
+ // summary: open the menu to the right of the current menu item
+ var fromPos = dojo.html.getAbsolutePosition(from_item.domNode, true);
+ var our_w = dojo.html.getMarginBox(this.domNode).width;
+ var x = fromPos.x + our_w - this.submenuOverlap;
+ var y = fromPos.y;
+
+ //the following is set in open, so we do not need it
+ //this.currentSubpopup = submenu;
+ submenu.open(x, y, this, from_item.domNode);
this.currentSubmenuTrigger = from_item;
this.currentSubmenuTrigger.is_open = true;
},
- onOpen: function(e){
+ onOpen: function(/*Event*/ e){
+ // summary: callback when menu is opened
this.openEvent = e;
-
- //dojo.debugShallow(e);
- this.open(e.clientX, e.clientY, null, [e.clientX, e.clientY]);
-
- if(e["preventDefault"]){
- e.preventDefault();
+ if(e["target"]){
+ this.openedForWindow = dojo.html.getElementWindow(e.target);
+ }else{
+ this.openedForWindow = null;
}
- },
-
- isPointInMenu: function(x, y){
-
- if (x < this.menuX){ return 0; }
- if (x > this.menuX + this.menuWidth){ return 0; }
+ var x = e.pageX, y = e.pageY;
- if (y < this.menuY){ return 0; }
- if (y > this.menuY + this.menuHeight){ return 0; }
+ var win = dojo.html.getElementWindow(e.target);
+ var iframe = win._frameElement || win.frameElement;
+ if(iframe){
+ var cood = dojo.html.abs(iframe, true);
+ x += cood.x - dojo.withGlobal(win, dojo.html.getScroll).left;
+ y += cood.y - dojo.withGlobal(win, dojo.html.getScroll).top;
+ }
+ this.open(x, y, null, [x, y]);
- return 1;
+ e.preventDefault();
+ e.stopPropagation();
}
});
-
-dojo.widget.MenuItem2 = function(){
- dojo.widget.HtmlWidget.call(this);
-}
-
-dojo.inherits(dojo.widget.MenuItem2, dojo.widget.HtmlWidget);
-
-dojo.lang.extend(dojo.widget.MenuItem2, {
- widgetType: "MenuItem2",
+// summary
+// A line item in a Menu2
+dojo.widget.defineWidget(
+ "dojo.widget.MenuItem2",
+ dojo.widget.HtmlWidget,
+ function(){
+ this.eventNames = {
+ engage: ""
+ };
+ },
+{
+ // Make 4 columns
+ // icon, label, accelerator-key, and right-arrow indicating sub-menu
templateString:
- '<div class="dojoMenuItem2">'
- +'<div dojoAttachPoint="iconNode" class="dojoMenuItem2Icon"></div>'
- +'<span dojoAttachPoint="labelNode" class="dojoMenuItem2Label"><span><span></span></span></span>'
- +'<span dojoAttachPoint="accelNode" class="dojoMenuItem2Accel"><span><span></span></span></span>'
- +'<div dojoAttachPoint="submenuNode" class="dojoMenuItem2Submenu"></div>'
- +'<div dojoAttachPoint="targetNode" class="dojoMenuItem2Target" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick;"> </div>'
- +'</div>',
-
- //
- // nodes
- //
-
- domNode: null,
- iconNode: null,
- labelNode: null,
- accelNode: null,
- submenuNode: null,
- targetNode: null,
+ '<tr class="dojoMenuItem2" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick; onKey:onKey;">'
+ +'<td><div class="${this.iconClass}" style="${this.iconStyle}"></div></td>'
+ +'<td tabIndex="-1" class="dojoMenuItem2Label">${this.caption}</td>'
+ +'<td class="dojoMenuItem2Accel">${this.accelKey}</td>'
+ +'<td><div class="dojoMenuItem2Submenu" style="display:${this.arrowDisplay};"></div></td>'
+ +'</tr>',
//
// internal settings
@@ -382,322 +347,419 @@
hover_timer: null,
is_open: false,
topPosition: 0,
- is_disabled: false,
//
// options
//
+ // String
+ // text of the menu item
caption: 'Untitled',
+
+ // String
+ // accelerator key (not supported yet!)
accelKey: '',
+
+ // String
+ // path to icon to display to the left of the menu text
iconSrc: '',
+
+ // String
+ // CSS class name to use for menu item (if CSS class specifies a background image then iconSrc is not necessary)
+ iconClass: 'dojoMenuItem2Icon',
+
+ // String
+ // widget ID of Menu2 widget to open when this menu item is clicked
submenuId: '',
- isDisabled: false,
+
+ // Boolean
+ // if true, this menu item cannot be selected
+ disabled: false,
+
+ // String
+ // event names for announcing when menu item is clicked.
+ // if "default", then use the default name, based on the widget ID
+ eventNaming: "default",
+
+ // String
+ // CSS class for menu item when it's hovered over
+ highlightClass: 'dojoMenuItem2Hover',
+ postMixInProperties: function(){
+ this.iconStyle="";
+ if (this.iconSrc){
+ if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4) == ".png") && (dojo.render.html.ie55 || dojo.render.html.ie60)){
+ this.iconStyle="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='image')";
+ }else{
+ this.iconStyle="background-image: url("+this.iconSrc+")";
+ }
+ }
+ this.arrowDisplay = this.submenuId ? 'block' : 'none';
+ dojo.widget.MenuItem2.superclass.postMixInProperties.apply(this, arguments);
+ },
- postCreate: function(){
-
+ fillInTemplate: function(){
dojo.html.disableSelection(this.domNode);
- if (this.isDisabled){
+ if (this.disabled){
this.setDisabled(true);
}
- this.labelNode.childNodes[0].appendChild(document.createTextNode(this.caption));
- this.accelNode.childNodes[0].appendChild(document.createTextNode(this.accelKey));
-
- this.labelShadowNode = this.labelNode.childNodes[0].childNodes[0];
- this.accelShadowNode = this.accelNode.childNodes[0].childNodes[0];
-
- this.labelShadowNode.appendChild(document.createTextNode(this.caption));
- this.accelShadowNode.appendChild(document.createTextNode(this.accelKey));
- },
-
- layoutItem: function(label_w, accel_w){
-
- var x_label = this.parent.itemHeight + this.parent.iconGap;
- var x_accel = x_label + label_w + this.parent.accelGap;
- var x_submu = x_accel + accel_w + this.parent.submenuGap;
- var total_w = x_submu + this.parent.submenuIconSize + this.parent.finalGap;
-
-
- this.iconNode.style.left = '0px';
- this.iconNode.style.top = '0px';
-
-
- if (this.iconSrc){
-
- if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4) == ".png") && (dojo.render.html.ie)){
-
- this.iconNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='image')";
- this.iconNode.style.backgroundImage = '';
- }else{
- this.iconNode.style.backgroundImage = 'url('+this.iconSrc+')';
+ if (this.eventNaming == "default") {
+ for (var eventName in this.eventNames) {
+ this.eventNames[eventName] = this.widgetId+"/"+eventName;
}
- }else{
- this.iconNode.style.backgroundImage = '';
}
-
- dojo.style.setOuterWidth(this.iconNode, this.parent.itemHeight);
- dojo.style.setOuterHeight(this.iconNode, this.parent.itemHeight);
-
- dojo.style.setOuterHeight(this.labelNode, this.parent.itemHeight);
- dojo.style.setOuterHeight(this.accelNode, this.parent.itemHeight);
-
- dojo.style.setContentWidth(this.domNode, total_w);
- dojo.style.setContentHeight(this.domNode, this.parent.itemHeight);
-
- this.labelNode.style.left = x_label + 'px';
- this.accelNode.style.left = x_accel + 'px';
- this.submenuNode.style.left = x_submu + 'px';
-
- dojo.style.setOuterWidth(this.submenuNode, this.parent.submenuIconSize);
- dojo.style.setOuterHeight(this.submenuNode, this.parent.itemHeight);
-
- this.submenuNode.style.display = this.submenuId ? 'block' : 'none';
- this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
-
- dojo.style.setOuterWidth(this.targetNode, total_w);
- dojo.style.setOuterHeight(this.targetNode, this.parent.itemHeight);
},
onHover: function(){
+ // summary: callback when mouse is moved onto menu item
+
+ //this is to prevent some annoying behavior when both mouse and keyboard are used
+ this.onUnhover();
if (this.is_hovering){ return; }
if (this.is_open){ return; }
- this.parent.closeSubmenu();
- this.highlightItem();
-
- if (this.is_hovering){ this.stopSubmenuTimer(); }
- this.is_hovering = 1;
- this.startSubmenuTimer();
+ if(this.parent._highlighted_option){
+ this.parent._highlighted_option.onUnhover();
+ }
+ this.parent.closeSubpopup();
+ this.parent._highlighted_option = this;
+ dojo.widget.PopupManager.setFocusedMenu(this.parent);
+
+ this._highlightItem();
+
+ if (this.is_hovering){ this._stopSubmenuTimer(); }
+ this.is_hovering = true;
+ this._startSubmenuTimer();
},
onUnhover: function(){
+ // summary: callback when mouse is moved off of menu item
+ if(!this.is_open){ this._unhighlightItem(); }
- if (!this.is_open){ this.unhighlightItem(); }
+ this.is_hovering = false;
- this.is_hovering = 0;
- this.stopSubmenuTimer();
- },
+ this.parent._highlighted_option = null;
- onClick: function(){
+ if(this.parent.parentPopup){
+ dojo.widget.PopupManager.setFocusedMenu(this.parent.parentPopup);
+ }
- if (this.is_disabled){ return; }
+ this._stopSubmenuTimer();
+ },
- if (this.submenuId){
+ _onClick: function(focus){
+ // summary: internal function for clicks
+ var displayingSubMenu = false;
+ if (this.disabled){ return false; }
+ if (this.submenuId){
if (!this.is_open){
- this.stopSubmenuTimer();
- this.openSubmenu();
+ this._stopSubmenuTimer();
+ this._openSubmenu();
}
-
+ displayingSubMenu = true;
}else{
+ // for some browsers the onMouseOut doesn't get called (?), so call it manually
+ this.onUnhover(); //only onUnhover when no submenu is available
+ this.parent.closeAll(true);
+ }
- this.parent.closeAll();
+ // user defined handler for click
+ this.onClick();
+
+ dojo.event.topic.publish(this.eventNames.engage, this);
+
+ if(displayingSubMenu && focus){
+ dojo.widget.getWidgetById(this.submenuId)._highlightOption(1);
}
+ return;
},
- highlightItem: function(){
-
- dojo.html.addClass(this.domNode, 'dojoMenuItem2Hover');
- this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconOnSrc+')';
+ onClick: function() {
+ // summary
+ // User defined function to handle clicks
+ // this default function call the parent
+ // menu's onItemClick
+ this.parent.onItemClick(this);
},
- unhighlightItem: function(){
+ _highlightItem: function(){
+ dojo.html.addClass(this.domNode, this.highlightClass);
+ },
- dojo.html.removeClass(this.domNode, 'dojoMenuItem2Hover');
- this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
+ _unhighlightItem: function(){
+ dojo.html.removeClass(this.domNode, this.highlightClass);
},
- startSubmenuTimer: function(){
- this.stopSubmenuTimer();
+ _startSubmenuTimer: function(){
+ this._stopSubmenuTimer();
- if (this.is_disabled){ return; }
+ if (this.disabled){ return; }
var self = this;
- var closure = function(){ return function(){ self.openSubmenu(); } }();
+ var closure = function(){ return function(){ self._openSubmenu(); } }();
- this.hover_timer = window.setTimeout(closure, this.parent.submenuDelay);
+ this.hover_timer = dojo.lang.setTimeout(closure, this.parent.submenuDelay);
},
- stopSubmenuTimer: function(){
+ _stopSubmenuTimer: function(){
if (this.hover_timer){
- window.clearTimeout(this.hover_timer);
+ dojo.lang.clearTimeout(this.hover_timer);
this.hover_timer = null;
}
},
- openSubmenu: function(){
+ _openSubmenu: function(){
+ if (this.disabled){ return; }
+
// first close any other open submenu
- this.parent.closeSubmenu();
+ this.parent.closeSubpopup();
var submenu = dojo.widget.getWidgetById(this.submenuId);
if (submenu){
-
- this.parent.openSubmenu(submenu, this);
+ this.parent._openSubmenu(submenu, this);
}
-
- //dojo.debug('open submenu for item '+this.widgetId);
},
- closedSubmenu: function(){
-
+ _closedSubmenu: function(){
this.onUnhover();
},
- setDisabled: function(value){
-
- if (value == this.is_disabled){ return; }
+ setDisabled: function(/*Boolean*/ value){
+ // summary: enable or disable this menu item
+ this.disabled = value;
- this.is_disabled = value;
-
- if (this.is_disabled){
+ if (this.disabled){
dojo.html.addClass(this.domNode, 'dojoMenuItem2Disabled');
}else{
dojo.html.removeClass(this.domNode, 'dojoMenuItem2Disabled');
}
},
- getLabelWidth: function(){
-
- var node = this.labelNode.childNodes[0];
-
- return dojo.style.getOuterWidth(node);
+ enable: function(){
+ // summary: enable this menu item so user can click it
+ this.setDisabled(false);
},
- getAccelWidth: function(){
-
- var node = this.accelNode.childNodes[0];
+ disable: function(){
+ // summary: disable this menu item so user can't click it
+ this.setDisabled(true);
+ },
- return dojo.style.getOuterWidth(node);
+ menuOpen: function(message) {
+ // summary: callback when menu is opened
+ // TODO: I don't see anyone calling this menu item
}
-});
+});
-dojo.widget.MenuSeparator2 = function(){
- dojo.widget.HtmlWidget.call(this);
-}
-
-dojo.inherits(dojo.widget.MenuSeparator2, dojo.widget.HtmlWidget);
-
-dojo.lang.extend(dojo.widget.MenuSeparator2, {
- widgetType: "MenuSeparator2",
-
- domNode: null,
- topNode: null,
- bottomNode: null,
-
- templateString: '<div>'
- +'<div dojoAttachPoint="topNode"></div>'
- +'<div dojoAttachPoint="bottomNode"></div>'
- +'</div>',
+// summary
+// A line between two menu items
+dojo.widget.defineWidget(
+ "dojo.widget.MenuSeparator2",
+ dojo.widget.HtmlWidget,
+{
+ templateString: '<tr class="dojoMenuSeparator2"><td colspan=4>'
+ +'<div class="dojoMenuSeparator2Top"></div>'
+ +'<div class="dojoMenuSeparator2Bottom"></div>'
+ +'</td></tr>',
postCreate: function(){
-
- dojo.html.addClass(this.domNode, 'dojoMenuSeparator2');
- dojo.html.addClass(this.topNode, 'dojoMenuSeparator2Top');
- dojo.html.addClass(this.bottomNode, 'dojoMenuSeparator2Bottom');
-
dojo.html.disableSelection(this.domNode);
-
- this.layoutItem();
- },
-
- layoutItem: function(label_w, accel_w){
-
- var full_width = this.parent.itemHeight
- + this.parent.iconGap
- + label_w
- + this.parent.accelGap
- + accel_w
- + this.parent.submenuGap
- + this.parent.submenuIconSize
- + this.parent.finalGap;
-
- if (isNaN(full_width)){ return; }
-
- dojo.style.setContentHeight(this.domNode, this.parent.separatorHeight);
- dojo.style.setContentWidth(this.domNode, full_width);
}
});
-//
-// the menu manager makes sure we don't have several menus
-// open at once. the root menu in an opening sequence calls
-// opened(). when a root menu closes it calls closed(). then
-// everything works. lovely.
-//
-
-dojo.widget.html.Menu2Manager = new function(){
-
- this.currentMenu = null;
- this.currentButton = null; // button that opened current menu (if any)
- this.focusNode = null;
-
- dojo.event.connect(document, 'onmousedown', this, 'onClick');
- dojo.event.connect(window, "onscroll", this, "onClick");
-
- this.closed = function(menu){
- if (this.currentMenu == menu){
- this.currentMenu = null;
- this.currentButton = null;
+// summary
+// A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
+dojo.widget.defineWidget(
+ "dojo.widget.MenuBar2",
+ dojo.widget.PopupMenu2,
+{
+ menuOverlap: 2,
+
+ templateString: '<div class="dojoMenuBar2" tabIndex="0"><table class="dojoMenuBar2Client"><tr dojoAttachPoint="containerNode"></tr></table></div>',
+
+ close: function(force){
+ if(this._highlighted_option){
+ this._highlighted_option.onUnhover();
+ }
+
+ this.closeSubpopup(force);
+ },
+
+ processKey: function(/*Event*/ evt){
+ if(evt.ctrlKey || evt.altKey){ return false; }
+
+ if (!dojo.html.hasClass(evt.target,"dojoMenuBar2")) { return false; }
+ var rval = false;
+
+ switch(evt.key){
+ case evt.KEY_DOWN_ARROW:
+ rval = this._moveToChildMenu(evt);
+ break;
+ case evt.KEY_UP_ARROW:
+ rval = this._moveToParentMenu(evt);
+ break;
+ case evt.KEY_RIGHT_ARROW:
+ rval = this._moveToNext(evt);
+ break;
+ case evt.KEY_LEFT_ARROW:
+ rval = this._moveToPrevious(evt);
+ break;
+ default:
+ rval = dojo.widget.MenuBar2.superclass.processKey.apply(this, arguments);
+ break;
}
- };
- this.opened = function(menu, button){
- if (menu == this.currentMenu){ return; }
-
- if (this.currentMenu){
- this.currentMenu.close();
- }
-
- this.currentMenu = menu;
- this.currentButton = button;
- };
-
- this.onClick = function(e){
-
- if (!this.currentMenu){ return; }
-
- var scrolloffset = dojo.html.getScrollOffset();
-
- var x = e.clientX + scrolloffset[0];
- var y = e.clientY + scrolloffset[1];
-
- var m = this.currentMenu;
+ return rval;
+ },
- // starting from the base menu, perform a hit test
- // and exit when one succeeds
+ postCreate: function(){
+ dojo.widget.MenuBar2.superclass.postCreate.apply(this, arguments);
+ dojo.widget.PopupManager.opened(this);
+ this.isShowingNow = true;
+ },
+
+ /*
+ * override PopupMenu2 to open the submenu below us rather than to our right
+ */
+ _openSubmenu: function(submenu, from_item){
+ var fromPos = dojo.html.getAbsolutePosition(from_item.domNode, true);
+ var ourPos = dojo.html.getAbsolutePosition(this.domNode, true);
+ var our_h = dojo.html.getBorderBox(this.domNode).height;
+ var x = fromPos.x;
+ var y = ourPos.y + our_h - this.menuOverlap;
- while (m){
+ submenu.open(x, y, this, from_item.domNode);
- if (m.isPointInMenu(x, y)){
+ this.currentSubmenuTrigger = from_item;
+ this.currentSubmenuTrigger.is_open = true;
+ }
+});
- return;
- }
+// summary
+// Item in a Menu2Bar
+dojo.widget.defineWidget(
+ "dojo.widget.MenuBarItem2",
+ dojo.widget.MenuItem2,
+{
+ templateString:
+ '<td class="dojoMenuBarItem2" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">'
+ +'<span>${this.caption}</span>'
+ +'</td>',
- m = m.currentSubmenu;
- }
+ highlightClass: 'dojoMenuBarItem2Hover',
- // Also, if user clicked the button that opened this menu, then
- // that button will send the menu a close() command, so this code
- // shouldn't try to close the menu. Closing twice messes up animation.
- if (this.currentButton && dojo.html.overElement(this.currentButton, e)){
- return;
+ setDisabled: function(value){
+ this.disabled = value;
+ if (this.disabled){
+ dojo.html.addClass(this.domNode, 'dojoMenuBarItem2Disabled');
+ }else{
+ dojo.html.removeClass(this.domNode, 'dojoMenuBarItem2Disabled');
}
+ }
+});
- // the click didn't fall within the open menu tree
- // so close it
-
- this.currentMenu.close();
- };
-}
-
-
-// make it a tag
-dojo.widget.tags.addParseTreeHandler("dojo:PopupMenu2");
-dojo.widget.tags.addParseTreeHandler("dojo:MenuItem2");
-dojo.widget.tags.addParseTreeHandler("dojo:MenuSeparator2");
+// ************************** make contextmenu work in konqueror and opera *********************
+dojo.widget.Menu2.OperaAndKonqFixer = new function(){
+ var implement = true;
+ var delfunc = false;
+
+ /** dom event check
+ *
+ * make a event and dispatch it and se if it calls function below,
+ * if it indeed is supported and we dont need to implement our own
+ */
+
+ // gets called if we have support for oncontextmenu
+ if (!dojo.lang.isFunction(dojo.doc().oncontextmenu)){
+ dojo.doc().oncontextmenu = function(){
+ implement = false;
+ delfunc = true;
+ }
+ }
+
+ if (dojo.doc().createEvent){ // moz, safari has contextmenu event, need to do livecheck on this env.
+ try {
+ var e = dojo.doc().createEvent("MouseEvents");
+ e.initMouseEvent("contextmenu", 1, 1, dojo.global(), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
+ dojo.doc().dispatchEvent(e);
+ } catch (e) {/* assume not supported */}
+ } else {
+ // IE no need to implement custom contextmenu
+ implement = false;
+ }
+
+ // clear this one if it wasn't there before
+ if (delfunc){
+ delete dojo.doc().oncontextmenu;
+ }
+ /***** end dom event check *****/
+
+
+ /**
+ * this fixes a dom node by attaching a custom oncontextmenu function that gets called when apropriate
+ * @param node a dom node
+ *
+ * no returns
+ */
+ this.fixNode = function(node){
+ if (implement){
+ // attach stub oncontextmenu function
+ if (!dojo.lang.isFunction(node.oncontextmenu)){
+ node.oncontextmenu = function(e){/*stub*/}
+ }
+
+ // attach control function for oncontextmenu
+ if (dojo.render.html.opera){
+ // opera
+ // listen to ctrl-click events
+ node._menufixer_opera = function(e){
+ if (e.ctrlKey){
+ this.oncontextmenu(e);
+ }
+ };
+
+ dojo.event.connect(node, "onclick", node, "_menufixer_opera");
+
+ } else {
+ // konqueror
+ // rightclick, listen to mousedown events
+ node._menufixer_konq = function(e){
+ if (e.button==2 ){
+ e.preventDefault(); // need to prevent browsers menu
+ this.oncontextmenu(e);
+ }
+ };
+
+ dojo.event.connect(node, "onmousedown", node, "_menufixer_konq");
+ }
+ }
+ }
+
+ /**
+ * this cleans up a fixed node, prevent memoryleak?
+ * @param node node to clean
+ *
+ * no returns
+ */
+ this.cleanNode = function(node){
+ if (implement){
+ // checks needed if we gets a non fixed node
+ if (node._menufixer_opera){
+ dojo.event.disconnect(node, "onclick", node, "_menufixer_opera");
+ delete node._menufixer_opera;
+ } else if(node._menufixer_konq){
+ dojo.event.disconnect(node, "onmousedown", node, "_menufixer_konq");
+ delete node._menufixer_konq;
+ }
+ if (node.oncontextmenu){
+ delete node.oncontextmenu;
+ }
+ }
+ }
+};
\ No newline at end of file
Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/MonthlyCalendar.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/MonthlyCalendar.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/MonthlyCalendar.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/MonthlyCalendar.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,188 @@
+/*
+ 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.MonthlyCalendar");
+dojo.require("dojo.date.common");
+dojo.require("dojo.date.format");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.experimental");
+
+dojo.experimental("dojo.widget.MonthlyCalendar");
+
+dojo.widget.defineWidget(
+ "dojo.widget.MonthlyCalendar",
+ dojo.widget.DatePicker,
+ {
+ dayWidth: 'wide',
+
+ templatePath: dojo.uri.dojoUri("src/widget/templates/MonthlyCalendar.html"),
+ templateCssPath: dojo.uri.dojoUri("src/widget/templates/MonthlyCalendar.css"),
+
+ initializer: function(){
+ this.iCalendars = [];
+ },
+
+ /*
+ cache: function(){
+ },
+ */
+
+ addCalendar: function(/* dojo.iCalendar */ cal) {
+ dojo.debug("Adding Calendar");
+ this.iCalendars.push(cal);
+ dojo.debug("Starting init");
+ this.initUI();
+ dojo.debug("done init");
+ },
+
+ createDayContents: function(node,mydate) {
+ dojo.html.removeChildren(node);
+ node.appendChild(document.createTextNode(mydate.getDate()));
+ for(var x=0; x<this.iCalendars.length; x++) {
+ var evts = this.iCalendars[x].getEvents(mydate);
+ if ((dojo.lang.isArray(evts)) && (evts.length>0)) {
+ for(var y=0;y<evts.length;y++) {
+ var el = document.createElement("div");
+ dojo.html.addClass(el, "dojoMonthlyCalendarEvent");
+ el.appendChild(document.createTextNode(evts[y].summary.value));
+ el.width = dojo.html.getContentBox(node).width;
+ node.appendChild(el);
+ }
+ }
+ }
+ },
+
+ initUI: function() {
+ var dayLabels = dojo.date.getNames('days', this.dayWidth, 'standAlone', this.lang);
+ var dayLabelNodes = this.dayLabelsRow.getElementsByTagName("td");
+ for(var i=0; i<7; i++) {
+ dayLabelNodes.item(i).innerHTML = dayLabels[i];
+ }
+
+ this.selectedIsUsed = false;
+ this.currentIsUsed = false;
+ var currentClassName = "";
+ var previousDate = new Date();
+ var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td");
+ var currentCalendarNode;
+ // set hours of date such that there is no chance of rounding error due to
+ // time change in local time zones
+ previousDate.setHours(8);
+ var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8);
+ var lastDay = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date + 42, 8);
+
+ if (this.iCalendars.length > 0) {
+ for (var x=0; x<this.iCalendars.length;x++) {
+ this.iCalendars[x].preComputeRecurringEvents(lastDay);
+ }
+ }
+
+ if(this.firstSaturday.date < 7) {
+ // this means there are days to show from the previous month
+ var dayInWeek = 6;
+ for (var i=this.firstSaturday.date; i>0; i--) {
+ currentCalendarNode = calendarNodes.item(dayInWeek);
+ this.createDayContents(currentCalendarNode, nextDate);
+
+ dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+ dayInWeek--;
+ previousDate = nextDate;
+ nextDate = this.incrementDate(nextDate, false);
+ }
+ for(var i=dayInWeek; i>-1; i--) {
+ currentCalendarNode = calendarNodes.item(i);
+
+ this.createDayContents(currentCalendarNode, nextDate);
+
+ dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous"));
+ previousDate = nextDate;
+ nextDate = this.incrementDate(nextDate, false);
+ }
+ } else {
+ nextDate.setDate(1);
+ for(var i=0; i<7; i++) {
+ currentCalendarNode = calendarNodes.item(i);
+ this.createDayContents(currentCalendarNode, nextDate);
+ dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+ previousDate = nextDate;
+ nextDate = this.incrementDate(nextDate, true);
+ }
+ }
+ previousDate.setDate(this.firstSaturday.date);
+ previousDate.setMonth(this.firstSaturday.month);
+ previousDate.setFullYear(this.firstSaturday.year);
+ nextDate = this.incrementDate(previousDate, true);
+ var count = 7;
+ currentCalendarNode = calendarNodes.item(count);
+ while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) {
+ this.createDayContents(currentCalendarNode, nextDate);
+ dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+ currentCalendarNode = calendarNodes.item(++count);
+ previousDate = nextDate;
+ nextDate = this.incrementDate(nextDate, true);
+ }
+
+ while(count < 42) {
+ this.createDayContents(currentCalendarNode, nextDate);
+ dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next"));
+ currentCalendarNode = calendarNodes.item(++count);
+ previousDate = nextDate;
+ nextDate = this.incrementDate(nextDate, true);
+ }
+ this.setMonthLabel(this.firstSaturday.month);
+ this.setYearLabels(this.firstSaturday.year);
+ }
+ }
+);
+
+dojo.widget.MonthlyCalendar.util= new function() {
+
+ this.toRfcDate = function(jsDate) {
+ if(!jsDate) {
+ jsDate = this.today;
+ }
+ var year = jsDate.getFullYear();
+ var month = jsDate.getMonth() + 1;
+ if (month < 10) {
+ month = "0" + month.toString();
+ }
+ var date = jsDate.getDate();
+ if (date < 10) {
+ date = "0" + date.toString();
+ }
+ // because this is a date picker and not a time picker, we treat time
+ // as zero
+ return year + "-" + month + "-" + date + "T00:00:00+00:00";
+ }
+
+ this.fromRfcDate = function(rfcDate) {
+ var tempDate = rfcDate.split("-");
+ if(tempDate.length < 3) {
+ return new Date();
+ }
+ // fullYear, month, date
+ return new Date(parseInt(tempDate[0]), (parseInt(tempDate[1], 10) - 1), parseInt(tempDate[2].substr(0,2), 10));
+ }
+
+//Note: redundant with dojo.widget.DatePicker.util
+ this.initFirstSaturday = function(month, year) {
+ if(!month) {
+ month = this.date.getMonth();
+ }
+ if(!year) {
+ year = this.date.getFullYear();
+ }
+ var firstOfMonth = new Date(year, month, 1);
+ return {year: year, month: month, date: 7 - firstOfMonth.getDay()};
+ }
+}
Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/MonthlyCalendar.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PageContainer.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PageContainer.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PageContainer.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PageContainer.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,387 @@
+/*
+ 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.PageContainer");
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.selection");
+
+// A PageContainer is a container that has multiple children, but shows only
+// one child at a time (like looking at the pages in a book one by one).
+//
+// Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and <widgetId>-selectChild
+//
+// Can be base class for container, Wizard, Show, etc.
+dojo.widget.defineWidget("dojo.widget.PageContainer", dojo.widget.HtmlWidget, {
+ isContainer: true,
+
+ // Boolean
+ // if true, change the size of my currently displayed child to match my size
+ doLayout: true,
+
+ templateString: "<div dojoAttachPoint='containerNode'></div>",
+
+ // String
+ // id of the initially shown page
+ selectedChild: "",
+
+ fillInTemplate: function(args, frag) {
+ // Copy style info from input node to output node
+ var source = this.getFragNodeRef(frag);
+ dojo.html.copyStyle(this.domNode, source);
+ dojo.widget.PageContainer.superclass.fillInTemplate.apply(this, arguments);
+ },
+
+ postCreate: function(args, frag) {
+ if(this.children.length){
+ // Setup each page panel
+ dojo.lang.forEach(this.children, this._setupChild, this);
+
+ // Figure out which child to initially display
+ var initialChild;
+ if(this.selectedChild){
+ this.selectChild(this.selectedChild);
+ }else{
+ for(var i=0; i<this.children.length; i++){
+ if(this.children[i].selected){
+ this.selectChild(this.children[i]);
+ break;
+ }
+ }
+ if(!this.selectedChildWidget){
+ this.selectChild(this.children[0]);
+ }
+ }
+ }
+ },
+
+ addChild: function(child){
+ dojo.widget.PageContainer.superclass.addChild.apply(this, arguments);
+ this._setupChild(child);
+
+ // in case the page labels have overflowed from one line to two lines
+ this.onResized();
+
+ // if this is the first child, then select it
+ if(!this.selectedChildWidget){
+ this.selectChild(child);
+ }
+ },
+
+ _setupChild: function(page){
+ // Summary: Add the given child to this page container
+
+ page.hide();
+
+ // publish the addChild event for panes added via addChild(), and the original panes too
+ dojo.event.topic.publish(this.widgetId+"-addChild", page);
+ },
+
+ removeChild: function(/* Widget */page){
+ dojo.widget.PageContainer.superclass.removeChild.apply(this, arguments);
+
+ // If we are being destroyed than don't run the code below (to select another page), because we are deleting
+ // every page one by one
+ if(this._beingDestroyed){ return; }
+
+ // this will notify any tablists to remove a button; do this first because it may affect sizing
+ dojo.event.topic.publish(this.widgetId+"-removeChild", page);
+
+ if (this.selectedChildWidget === page) {
+ this.selectedChildWidget = undefined;
+ if (this.children.length > 0) {
+ this.selectChild(this.children[0], true);
+ }
+ }
+ },
+
+ selectChild: function(/* Widget */ page, /* Widget */ callingWidget){
+ // summary
+ // Show the given widget (which must be one of my children)
+ page = dojo.widget.byId(page);
+ this.correspondingPageButton = callingWidget;
+
+ // Deselect old page and select new one
+ if(this.selectedChildWidget){
+ this._hideChild(this.selectedChildWidget);
+ }
+ this.selectedChildWidget = page;
+ this._showChild(page);
+ page.isFirstChild = (page == this.children[0]);
+ page.isLastChild = (page == this.children[this.children.length-1]);
+ dojo.event.topic.publish(this.widgetId+"-selectChild", page);
+ },
+
+ forward: function(){
+ // Summary: advance to next page
+ var index = dojo.lang.find(this.children, this.selectedChildWidget);
+ this.selectChild(this.children[index+1]);
+ },
+
+ back: function(){
+ // Summary: go back to previous page
+ var index = dojo.lang.find(this.children, this.selectedChildWidget);
+ this.selectChild(this.children[index-1]);
+ },
+
+ onResized: function(){
+ // Summary: called when any page is shown, to make it fit the container correctly
+ if(this.doLayout && this.selectedChildWidget){
+ with(this.selectedChildWidget.domNode.style){
+ top = dojo.html.getPixelValue(this.containerNode, "padding-top", true);
+ left = dojo.html.getPixelValue(this.containerNode, "padding-left", true);
+ }
+ var content = dojo.html.getContentBox(this.containerNode);
+ this.selectedChildWidget.resizeTo(content.width, content.height);
+ }
+ },
+
+ _showChild: function(page) {
+ // size the current page (in case this is the first time it's being shown, or I have been resized)
+ if(this.doLayout){
+ var content = dojo.html.getContentBox(this.containerNode);
+ page.resizeTo(content.width, content.height);
+ }
+
+ page.selected=true;
+ page.show();
+ },
+
+ _hideChild: function(page) {
+ page.selected=false;
+ page.hide();
+ },
+
+ closeChild: function(page) {
+ // summary
+ // callback when user clicks the [X] to remove a page
+ // if onClose() returns true then remove and destroy the childd
+ var remove = page.onClose(this, page);
+ if(remove) {
+ this.removeChild(page);
+ // makes sure we can clean up executeScripts in ContentPane onUnLoad
+ page.destroy();
+ }
+ },
+
+ destroy: function(){
+ this._beingDestroyed = true;
+ dojo.event.topic.destroy(this.widgetId+"-addChild");
+ dojo.event.topic.destroy(this.widgetId+"-removeChild");
+ dojo.event.topic.destroy(this.widgetId+"-selectChild");
+ dojo.widget.PageContainer.superclass.destroy.apply(this, arguments);
+ }
+});
+
+
+// PageController - set of buttons to select the page in a page list
+// When intialized, the PageController monitors the container, and whenever a page is
+// added or deleted updates itself accordingly.
+dojo.widget.defineWidget(
+ "dojo.widget.PageController",
+ dojo.widget.HtmlWidget,
+ {
+ templateString: "<span wairole='tablist' dojoAttachEvent='onKey'></span>",
+ isContainer: true,
+
+ // String
+ // the id of the page container that I point to
+ containerId: "",
+
+ // String
+ // the name of the button widget to create to correspond to each page
+ buttonWidget: "PageButton",
+
+ // String
+ // Class name to apply to the top dom node
+ "class": "dojoPageController",
+
+ fillInTemplate: function() {
+ dojo.html.addClass(this.domNode, this["class"]); // "class" is a reserved word in JS
+ dojo.widget.wai.setAttr(this.domNode, "waiRole", "role", "tablist");
+ },
+
+ postCreate: function(){
+ this.pane2button = {}; // mapping from panes to buttons
+
+ // If children have already been added to the page container then create buttons for them
+ var container = dojo.widget.byId(this.containerId);
+ if(container){
+ dojo.lang.forEach(container.children, this.onAddChild, this);
+ }
+
+ dojo.event.topic.subscribe(this.containerId+"-addChild", this, "onAddChild");
+ dojo.event.topic.subscribe(this.containerId+"-removeChild", this, "onRemoveChild");
+ dojo.event.topic.subscribe(this.containerId+"-selectChild", this, "onSelectChild");
+ },
+
+ destroy: function(){
+ dojo.event.topic.unsubscribe(this.containerId+"-addChild", this, "onAddChild");
+ dojo.event.topic.unsubscribe(this.containerId+"-removeChild", this, "onRemoveChild");
+ dojo.event.topic.unsubscribe(this.containerId+"-selectChild", this, "onSelectChild");
+ dojo.widget.PageController.superclass.destroy.apply(this, arguments);
+ },
+
+ onAddChild: function(/* Widget */ page){
+ // summary
+ // Called whenever a page is added to the container.
+ // Create button corresponding to the page.
+ var button = dojo.widget.createWidget(this.buttonWidget,
+ {
+ label: page.label,
+ closeButton: page.closable
+ });
+ this.addChild(button);
+ this.domNode.appendChild(button.domNode);
+ this.pane2button[page]=button;
+ page.controlButton = button; // this value might be overwritten if two tabs point to same container
+
+ var _this = this;
+ dojo.event.connect(button, "onClick", function(){ _this.onButtonClick(page); });
+ dojo.event.connect(button, "onCloseButtonClick", function(){ _this.onCloseButtonClick(page); });
+ },
+
+ onRemoveChild: function(/* Widget */ page){
+ // summary
+ // Called whenever a page is removed from the container.
+ // Remove the button corresponding to the page.
+ if(this._currentChild == page){ this._currentChild = null; }
+ var button = this.pane2button[page];
+ if(button){
+ button.destroy();
+ }
+ this.pane2button[page] = null;
+ },
+
+ onSelectChild: function(/*Widget*/ page){
+ // Summary
+ // Called when a page has been selected in the PageContainer, either by me or by another PageController
+ if(this._currentChild){
+ var oldButton=this.pane2button[this._currentChild];
+ oldButton.clearSelected();
+ }
+ var newButton=this.pane2button[page];
+ newButton.setSelected();
+ this._currentChild=page;
+ },
+
+ onButtonClick: function(/*Widget*/ page){
+ // summary
+ // Called whenever one of my child buttons is pressed in an attempt to select a page
+ var container = dojo.widget.byId(this.containerId); // TODO: do this via topics?
+ container.selectChild(page, false, this);
+ },
+
+ onCloseButtonClick: function(/*Widget*/ page){
+ // summary
+ // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
+ var container = dojo.widget.byId(this.containerId);
+ container.closeChild(page);
+ },
+
+ onKey: function(evt){
+ // summary:
+ // Handle keystrokes on the page list, for advancing to next/previous button
+
+ if( (evt.keyCode == evt.KEY_RIGHT_ARROW)||
+ (evt.keyCode == evt.KEY_LEFT_ARROW) ){
+ var current = 0;
+ var next = null; // the next button to focus on
+
+ // find currently focused button in children array
+ var current = dojo.lang.find(this.children, this.pane2button[this._currentChild]);
+
+ // pick next button to focus on
+ if(evt.keyCode == evt.KEY_RIGHT_ARROW){
+ next = this.children[ (current+1) % this.children.length ];
+ }else{ // is LEFT_ARROW
+ next = this.children[ (current+ (this.children.length-1)) % this.children.length ];
+ }
+
+ dojo.event.browser.stopEvent(evt);
+ next.onClick();
+ }
+ }
+ }
+);
+
+// PageButton (the thing you click to select or delete a page)
+dojo.widget.defineWidget("dojo.widget.PageButton", dojo.widget.HtmlWidget,
+{
+ templateString: "<span class='item'>" +
+ "<span dojoAttachEvent='onClick' dojoAttachPoint='titleNode' class='selectButton'>${this.label}</span>" +
+ "<span dojoAttachEvent='onClick:onCloseButtonClick' class='closeButton'>[X]</span>" +
+ "</span>",
+
+ // String
+ // Name to print on the button
+ label: "foo",
+
+ // Boolean
+ // true iff we should also print a close icon to destroy corresponding page
+ closeButton: false,
+
+ onClick: function(){
+ // summary
+ // Basically this is the attach point PageController listens to, to select the page
+ this.focus();
+ },
+
+ onCloseButtonMouseOver: function(){
+ // summary
+ // The close button changes color a bit when you mouse over
+ dojo.html.addClass(this.closeButtonNode, "closeHover");
+ },
+
+ onCloseButtonMouseOut: function(){
+ // summary
+ // Revert close button to normal color on mouse out
+ dojo.html.removeClass(this.closeButtonNode, "closeHover");
+ },
+
+ onCloseButtonClick: function(evt){
+ // summary
+ // Handle clicking the close button for this tab
+ },
+
+ setSelected: function(){
+ // summary
+ // This is run whenever the page corresponding to this button has been selected
+ dojo.html.addClass(this.domNode, "current");
+ this.titleNode.setAttribute("tabIndex","0");
+ },
+
+ clearSelected: function(){
+ // summary
+ // This function is run whenever the page corresponding to this button has been deselected (and another page has been shown)
+ dojo.html.removeClass(this.domNode, "current");
+ this.titleNode.setAttribute("tabIndex","-1");
+ },
+
+ focus: function(){
+ // summary
+ // This will focus on the this button (for accessibility you need to do this when the button is selected)
+ if(this.titleNode.focus){ // mozilla 1.7 doesn't have focus() func
+ this.titleNode.focus();
+ }
+ }
+});
+
+// These arguments can be specified for the children of a PageContainer.
+// Since any widget can be specified as a PageContainer child, mix them
+// into the base widget class. (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+ label: "",
+ selected: false, // is this tab currently selected?
+ closable: false, // true if user can close this tab pane
+ onClose: function(){ return true; } // callback if someone tries to close the child, child will be closed if func returns true
+});
Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PageContainer.js
------------------------------------------------------------------------------
svn:eol-style = native
Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Parse.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Parse.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Parse.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Parse.js Mon Nov 13 14:54:45 2006
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2004-2005, The Dojo Foundation
+ Copyright (c) 2004-2006, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
@@ -9,110 +9,119 @@
*/
dojo.provide("dojo.widget.Parse");
-
dojo.require("dojo.widget.Manager");
-dojo.require("dojo.string");
dojo.require("dojo.dom");
-dojo.widget.Parse = function(fragment) {
+//
+// dojoML parser should be moved out of 'widget', codifying the difference between a 'component'
+// and a 'widget'. A 'component' being anything that can be generated from a tag.
+//
+// a particular dojoML tag would be handled by a registered tagHandler with a hook for a default handler
+// if the widget system is loaded, a widget builder would be attach itself as the default handler
+//
+// widget tags are no longer registered themselves:
+// they are now arbitrarily namespaced, so we cannot register them all, and the non-prefixed portions
+// are no longer guaranteed unique
+//
+// therefore dojo.widget.tags should go with this parser code out of the widget module
+//
+
+dojo.widget.Parse = function(fragment){
this.propertySetsList = [];
this.fragment = fragment;
-
- /* createComponents recurses over a raw JavaScript object structure,
- and calls the corresponding handler for its normalized tagName if it exists
- */
- this.createComponents = function(fragment, parentComp){
- var djTags = dojo.widget.tags;
- var returnValue = [];
- // this allows us to parse without having to include the parent
- // it is commented out as it currently breaks the existing mechanism for
- // adding widgets programmatically. Once that is fixed, this can be used
- /*if( (fragment["tagName"])&&
- (fragment != fragment["nodeRef"])){
- var tn = new String(fragment["tagName"]);
- // we split so that you can declare multiple
- // non-destructive widgets from the same ctor node
- var tna = tn.split(";");
- for(var x=0; x<tna.length; x++){
- var ltn = dojo.text.trim(tna[x]).toLowerCase();
- if(djTags[ltn]){
- fragment.tagName = ltn;
- returnValue.push(djTags[ltn](fragment, this, parentComp, count++));
- }else{
- if(ltn.substr(0, 5)=="dojo:"){
- dj_debug("no tag handler registed for type: ", ltn);
- }
- }
- }
- }*/
- for(var item in fragment){
- var built = false;
- // if we have items to parse/create at this level, do it!
- try{
- if( fragment[item] && (fragment[item]["tagName"])&&
- (fragment[item] != fragment["nodeRef"])){
- var tn = new String(fragment[item]["tagName"]);
- // we split so that you can declare multiple
- // non-destructive widgets from the same ctor node
- var tna = tn.split(";");
- for(var x=0; x<tna.length; x++){
- var ltn = dojo.string.trim(tna[x]).toLowerCase();
- if(djTags[ltn]){
+
+ this.createComponents = function(frag, parentComp){
+ var comps = [];
+ var built = false;
+ // if we have items to parse/create at this level, do it!
+ try{
+ if((frag)&&(frag["tagName"])&&(frag!=frag["nodeRef"])){
+
+ // these are in fact, not ever for widgets per-se anymore,
+ // but for other markup elements (aka components)
+ var djTags = dojo.widget.tags;
+
+ // we split so that you can declare multiple
+ // non-destructive components from the same ctor node
+ var tna = String(frag["tagName"]).split(";");
+ for(var x=0; x<tna.length; x++){
+ var ltn = (tna[x].replace(/^\s+|\s+$/g, "")).toLowerCase();
+ // FIXME: unsure what this does
+ frag.tagName = ltn;
+ if(djTags[ltn]){
+ built = true;
+ var ret = djTags[ltn](frag, this, parentComp, frag["index"]);
+ comps.push(ret);
+ } else {
+ // we require a namespace prefix, default to dojo:
+ if (ltn.indexOf(":") == -1){
+ ltn = "dojo:"+ltn;
+ }
+ // FIXME: handling failure condition correctly?
+ //var ret = djTags[ltn](frag, this, parentComp, frag["index"]);
+ var ret = dojo.widget.buildWidgetFromParseTree(ltn, frag, this, parentComp, frag["index"]);
+ if (ret) {
built = true;
- // var tic = new Date();
- fragment[item].tagName = ltn;
- var ret = djTags[ltn](fragment[item], this, parentComp, fragment[item]["index"])
- returnValue.push(ret);
- }else{
- if((dojo.lang.isString(ltn))&&(ltn.substr(0, 5)=="dojo:")){
- dojo.debug("no tag handler registed for type: ", ltn);
- }
+ comps.push(ret);
}
}
}
- }catch(e){
- dojo.debug("fragment creation error:", e);
- // throw(e);
- // IE is such a bitch sometimes
}
+ }catch(e){
+ dojo.debug("dojo.widget.Parse: error:" + e);
+ // note, commenting out the next line is breaking several widgets for me
+ // throw e;
+ // IE is such a bitch sometimes
+ }
+ // if there's a sub-frag, build widgets from that too
+ if(!built){
+ comps = comps.concat(this.createSubComponents(frag, parentComp));
+ }
+ return comps;
+ }
- // if there's a sub-frag, build widgets from that too
- if( (!built) && (typeof fragment[item] == "object")&&
- (fragment[item] != fragment.nodeRef)&&
- (fragment[item] != fragment["tagName"])){
- returnValue.push(this.createComponents(fragment[item], parentComp));
+ /* createSubComponents recurses over a raw JavaScript object structure,
+ and calls the corresponding handler for its normalized tagName if it exists
+ */
+ this.createSubComponents = function(fragment, parentComp){
+ var frag, comps = [];
+ for(var item in fragment){
+ frag = fragment[item];
+ if ((frag)&&(typeof frag == "object")&&(frag!=fragment.nodeRef)&&(frag!=fragment["tagName"])){
+ comps = comps.concat(this.createComponents(frag, parentComp));
}
}
- return returnValue;
+ return comps;
}
/* parsePropertySets checks the top level of a raw JavaScript object
structure for any propertySets. It stores an array of references to
propertySets that it finds.
*/
- this.parsePropertySets = function(fragment) {
+ this.parsePropertySets = function(fragment){
return [];
+ /*
var propertySets = [];
for(var item in fragment){
- if( (fragment[item]["tagName"] == "dojo:propertyset") ) {
+ if((fragment[item]["tagName"] == "dojo:propertyset")){
propertySets.push(fragment[item]);
}
}
// FIXME: should we store these propertySets somewhere for later retrieval
this.propertySetsList.push(propertySets);
return propertySets;
+ */
}
/* parseProperties checks a raw JavaScript object structure for
properties, and returns an array of properties that it finds.
*/
- this.parseProperties = function(fragment) {
+ this.parseProperties = function(fragment){
var properties = {};
for(var item in fragment){
// FIXME: need to check for undefined?
// case: its a tagName or nodeRef
- if((fragment[item] == fragment["tagName"])||
- (fragment[item] == fragment.nodeRef)){
+ if((fragment[item] == fragment["tagName"])||(fragment[item] == fragment.nodeRef)){
// do nothing
}else{
if((fragment[item]["tagName"])&&
@@ -121,7 +130,7 @@
// so do something else
// FIXME: needs to be a better/stricter check
// TODO: handle xlink:href for external property sets
- }else if((fragment[item][0])&&(fragment[item][0].value!="")){
+ }else if((fragment[item][0])&&(fragment[item][0].value!="")&&(fragment[item][0].value!=null)){
try{
// FIXME: need to allow more than one provider
if(item.toLowerCase() == "dataprovider") {
@@ -137,7 +146,15 @@
}
}catch(e){ dojo.debug(e); }
}
- }
+ switch(item.toLowerCase()){
+ case "checked":
+ case "disabled":
+ if (typeof properties[item] != "boolean"){
+ properties[item] = true;
+ }
+ break;
+ }
+ }
}
return properties;
}
@@ -145,7 +162,7 @@
/* getPropertySetById returns the propertySet that matches the provided id
*/
- this.getDataProvider = function(objRef, dataUrl) {
+ this.getDataProvider = function(objRef, dataUrl){
// FIXME: this is currently sync. To make this async, we made need to move
//this step into the widget ctor, so that it is loaded when it is needed
// to populate the widget
@@ -179,6 +196,7 @@
for(var x=0; x < this.propertySetsList.length; x++){
var cpl = this.propertySetsList[x];
var cpcc = cpl["componentClass"]||cpl["componentType"]||null;
+ var propertySetId = this.propertySetsList[x]["id"][0].value;
if((cpcc)&&(propertySetId == cpcc[0].value)){
propertySets.push(cpl);
}
@@ -199,7 +217,7 @@
// FIXME: need a better test to see if this is local or external
// FIXME: doesn't handle nested propertySets, or propertySets that
// just contain information about css documents, etc.
- for(propertySetId in propertyProviderIds){
+ for(var propertySetId in propertyProviderIds){
if((propertySetId.indexOf("..")==-1)&&(propertySetId.indexOf("://")==-1)){
// get a reference to a propertySet within the current parsed structure
var propertySet = this.getPropertySetById(propertySetId);
@@ -225,21 +243,19 @@
componentName is the expected dojo widget name, i.e. Button of ContextMenu
properties is an object of name value pairs
+ ns is the namespace of the widget. Defaults to "dojo"
*/
- this.createComponentFromScript = function(nodeRef, componentName, properties){
- var ltn = "dojo:" + componentName.toLowerCase();
+ this.createComponentFromScript = function(nodeRef, componentName, properties, ns){
+ properties.fastMixIn = true;
+ // FIXME: we pulled it apart and now we put it back together ...
+ var ltn = (ns || "dojo") + ":" + componentName.toLowerCase();
if(dojo.widget.tags[ltn]){
- properties.fastMixIn = true;
return [dojo.widget.tags[ltn](properties, this, null, null, properties)];
- }else{
- if(ltn.substr(0, 5)=="dojo:"){
- dojo.debug("no tag handler registed for type: ", ltn);
- }
}
+ return [dojo.widget.buildWidgetFromParseTree(ltn, properties, this, null, null, properties)];
}
}
-
dojo.widget._parser_collection = {"dojo": new dojo.widget.Parse() };
dojo.widget.getParser = function(name){
if(!name){ name = "dojo"; }
@@ -252,37 +268,47 @@
/**
* Creates widget.
*
- * @param name The name of the widget to create
+ * @param name The name of the widget to create with optional namespace prefix,
+ * e.g."ns:widget", namespace defaults to "dojo".
* @param props Key-Value pairs of properties of the widget
- * @param refNode If the last argument is specified this node is used as
- * a reference for inserting this node into a DOM tree else
- * it beomces the domNode
+ * @param refNode If the position argument is specified, this node is used as
+ * a reference for inserting this node into a DOM tree; else
+ * the widget becomes the domNode
* @param position The position to insert this widget's node relative to the
* refNode argument
* @return The new Widget object
*/
-
-dojo.widget.createWidget = function (name, props, refNode, position) {
- function fromScript (placeKeeperNode, name, props) {
+dojo.widget.createWidget = function(name, props, refNode, position){
+ var isNode = false;
+ var isNameStr = (typeof name == "string");
+ if(isNameStr){
+ var pos = name.indexOf(":");
+ var ns = (pos > -1) ? name.substring(0,pos) : "dojo";
+ if(pos > -1){ name = name.substring(pos+1); }
var lowerCaseName = name.toLowerCase();
- var namespacedName = "dojo:" + lowerCaseName;
+ var namespacedName = ns + ":" + lowerCaseName;
+ isNode = (dojo.byId(name) && (!dojo.widget.tags[namespacedName]));
+ }
+
+ if((arguments.length == 1) && ((isNode)||(!isNameStr))){
+ // we got a DOM node
+ var xp = new dojo.xml.Parse();
+ // FIXME: we should try to find the parent!
+ var tn = (isNode) ? dojo.byId(name) : name;
+ return dojo.widget.getParser().createComponents(xp.parseElement(tn, null, true))[0];
+ }
+
+ function fromScript(placeKeeperNode, name, props, ns){
props[namespacedName] = {
dojotype: [{value: lowerCaseName}],
nodeRef: placeKeeperNode,
fastMixIn: true
};
- return dojo.widget.getParser().createComponentFromScript(
- placeKeeperNode, name, props, true);
+ props.ns = ns;
+ return dojo.widget.getParser().createComponentFromScript(placeKeeperNode, name, props, ns);
}
- if (typeof name != "string" && typeof props == "string") {
- dojo.deprecated("dojo.widget.createWidget",
- "argument order is now of the form " +
- "dojo.widget.createWidget(NAME, [PROPERTIES, [REFERENCENODE, [POSITION]]])");
- return fromScript(name, props, refNode);
- }
-
props = props||{};
var notRef = false;
var tn = null;
@@ -294,27 +320,27 @@
notRef = true;
refNode = tn;
if(h){
- dojo.html.body().appendChild(refNode);
+ dojo.body().appendChild(refNode);
}
}else if(position){
dojo.dom.insertAtPosition(tn, refNode, position);
}else{ // otherwise don't replace, but build in-place
tn = refNode;
}
- var widgetArray = fromScript(tn, name, props);
- if (!widgetArray[0] || typeof widgetArray[0].widgetType == "undefined") {
+ var widgetArray = fromScript(tn, name.toLowerCase(), props, ns);
+ if( (!widgetArray)||(!widgetArray[0])||
+ (typeof widgetArray[0].widgetType == "undefined") ){
throw new Error("createWidget: Creation of \"" + name + "\" widget failed.");
}
- if (notRef) {
- if (widgetArray[0].domNode.parentNode) {
- widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
+ try{
+ if(notRef){
+ if(widgetArray[0].domNode.parentNode){
+ widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
+ }
}
+ }catch(e){
+ /* squelch for Safari */
+ dojo.debug(e);
}
return widgetArray[0]; // just return the widget
-}
-
-dojo.widget.fromScript = function(name, props, refNode, position){
- dojo.deprecated("dojo.widget.fromScript", " use " +
- "dojo.widget.createWidget instead");
- return dojo.widget.createWidget(name, props, refNode, position);
}
Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PopupContainer.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PopupContainer.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PopupContainer.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PopupContainer.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,452 @@
+/*
+ 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.PopupContainer");
+
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+// summary:
+// PopupContainerBase is the mixin class which provide popup behaviors:
+// it can open in a given position x,y or around a given node.
+// In addition, it handles animation and IE bleed through workaround.
+// description:
+// This class can not be used standalone: it should be mixed-in to a
+// dojo.widget.HtmlWidget. Use PopupContainer instead if you want a
+// a standalone popup widget
+dojo.declare(
+ "dojo.widget.PopupContainerBase",
+ null,
+ function(){
+ this.queueOnAnimationFinish = [];
+ },
+{
+ isContainer: true,
+ templateString: '<div dojoAttachPoint="containerNode" style="display:none;position:absolute;" class="dojoPopupContainer" ></div>',
+
+ // Boolean: whether this popup is shown
+ isShowingNow: false,
+
+ // Widget: the shown sub popup if any
+ currentSubpopup: null,
+
+ // Int: the minimal popup zIndex
+ beginZIndex: 1000,
+
+ // Widget: parent popup widget
+ parentPopup: null,
+ // Widget: parent Widget
+ parent: null,
+ // Int: level of sub popup
+ popupIndex: 0,
+
+ // dojo.html.boxSizing: which bounding box to use for open aroundNode. By default use BORDER box of the aroundNode
+ aroundBox: dojo.html.boxSizing.BORDER_BOX,
+
+ // Object: in which window, the open() is triggered
+ openedForWindow: null,
+
+ processKey: function(/*Event*/evt){
+ // summary: key event handler
+ return false;
+ },
+
+ applyPopupBasicStyle: function(){
+ // summary: apply necessary css rules to the top domNode
+ // description:
+ // this function should be called in sub class where a custom
+ // templateString/templateStringPath is used (see Tooltip widget)
+ with(this.domNode.style){
+ display = 'none';
+ position = 'absolute';
+ }
+ },
+
+ aboutToShow: function() {
+ // summary: connect to this stub to modify the content of the popup
+ },
+
+ open: function(/*Integer*/x, /*Integer*/y, /*DomNode*/parent, /*Object*/explodeSrc, /*String?*/orient, /*Array?*/padding){
+ // summary:
+ // Open the popup at position (x,y), relative to dojo.body()
+ // Or open(node, parent, explodeSrc, aroundOrient) to open
+ // around node
+ if (this.isShowingNow){ return; }
+
+ this.aboutToShow();
+
+ // if I click right button and menu is opened, then it gets 2 commands: close -> open
+ // so close enables animation and next "open" is put to queue to occur at new location
+ if(this.animationInProgress){
+ this.queueOnAnimationFinish.push(this.open, arguments);
+ return;
+ }
+
+ // save this so that the focus can be returned
+ this.parent = parent;
+
+ var around = false, node, aroundOrient;
+ if(typeof x == 'object'){
+ node = x;
+ aroundOrient = explodeSrc;
+ explodeSrc = parent;
+ parent = y;
+ around = true;
+ }
+
+ // for unknown reasons even if the domNode is attached to the body in postCreate(),
+ // it's not attached here, so have to attach it here.
+ dojo.body().appendChild(this.domNode);
+
+ // if explodeSrc isn't specified then explode from my parent widget
+ explodeSrc = explodeSrc || parent["domNode"] || [];
+
+ //keep track of parent popup to decided whether this is a top level popup
+ var parentPopup = null;
+ this.isTopLevel = true;
+ while(parent){
+ if(parent !== this && (parent.setOpenedSubpopup != undefined && parent.applyPopupBasicStyle != undefined)){
+ parentPopup = parent;
+ this.isTopLevel = false;
+ parentPopup.setOpenedSubpopup(this);
+ break;
+ }
+ parent = parent.parent;
+ }
+
+ this.parentPopup = parentPopup;
+ this.popupIndex = parentPopup ? parentPopup.popupIndex + 1 : 1;
+
+ if(this.isTopLevel){
+ var button = dojo.html.isNode(explodeSrc) ? explodeSrc : null;
+ dojo.widget.PopupManager.opened(this, button);
+ }
+
+ //Store the current selection and restore it before the action for a menu item
+ //is executed. This is required as clicking on an menu item deselects current selection
+ if(this.isTopLevel && !dojo.withGlobal(this.openedForWindow||dojo.global(), dojo.html.selection.isCollapsed)){
+ this._bookmark = dojo.withGlobal(this.openedForWindow||dojo.global(), dojo.html.selection.getBookmark);
+ }else{
+ this._bookmark = null;
+ }
+
+ //convert explodeSrc from format [x, y] to
+ //{left: x, top: y, width: 0, height: 0} which is the new
+ //format required by dojo.html.toCoordinateObject
+ if(explodeSrc instanceof Array){
+ explodeSrc = {left: explodeSrc[0], top: explodeSrc[1], width: 0, height: 0};
+ }
+
+ // display temporarily, and move into position, then hide again
+ with(this.domNode.style){
+ display="";
+ zIndex = this.beginZIndex + this.popupIndex;
+ }
+
+ if(around){
+ this.move(node, padding, aroundOrient);
+ }else{
+ this.move(x, y, padding, orient);
+ }
+ this.domNode.style.display="none";
+
+ this.explodeSrc = explodeSrc;
+
+ // then use the user defined method to display it
+ this.show();
+
+ this.isShowingNow = true;
+ },
+
+ // TODOC: move(node, padding, aroundOrient) how to do this?
+ move: function(/*Int*/x, /*Int*/y, /*Integer?*/padding, /*String?*/orient){
+ // summary: calculate where to place the popup
+
+ var around = (typeof x == "object");
+ if(around){
+ var aroundOrient=padding;
+ var node=x;
+ padding=y;
+ if(!aroundOrient){ //By default, attempt to open above the aroundNode, or below
+ aroundOrient = {'BL': 'TL', 'TL': 'BL'};
+ }
+ dojo.html.placeOnScreenAroundElement(this.domNode, node, padding, this.aroundBox, aroundOrient);
+ }else{
+ if(!orient){ orient = 'TL,TR,BL,BR';}
+ dojo.html.placeOnScreen(this.domNode, x, y, padding, true, orient);
+ }
+ },
+
+ close: function(/*Boolean?*/force){
+ // summary: hide the popup
+ if(force){
+ this.domNode.style.display="none";
+ }
+
+ // If we are in the process of opening the menu and we are asked to close it
+ if(this.animationInProgress){
+ this.queueOnAnimationFinish.push(this.close, []);
+ return;
+ }
+
+ this.closeSubpopup(force);
+ this.hide();
+ if(this.bgIframe){
+ this.bgIframe.hide();
+ this.bgIframe.size({left: 0, top: 0, width: 0, height: 0});
+ }
+ if(this.isTopLevel){
+ dojo.widget.PopupManager.closed(this);
+ }
+ this.isShowingNow = false;
+ // return focus to the widget that opened the menu
+ try {
+ this.parent.domNode.focus();
+ } catch(e) {}
+
+ //do not need to restore if current selection is not empty
+ //(use keyboard to select a menu item)
+ if(this._bookmark && dojo.withGlobal(this.openedForWindow||dojo.global(), dojo.html.selection.isCollapsed)){
+ if(this.openedForWindow){
+ this.openedForWindow.focus()
+ }
+ dojo.withGlobal(this.openedForWindow||dojo.global(), "moveToBookmark", dojo.html.selection, [this._bookmark]);
+ }
+ this._bookmark = null;
+ },
+
+ closeAll: function(/*Boolean?*/force){
+ // summary: hide all popups including sub ones
+ if (this.parentPopup){
+ this.parentPopup.closeAll(force);
+ }else{
+ this.close(force);
+ }
+ },
+
+ setOpenedSubpopup: function(/*Widget*/popup) {
+ // summary: used by sub popup to set currentSubpopup in the parent popup
+ this.currentSubpopup = popup;
+ },
+
+ closeSubpopup: function(/*Boolean?*/force) {
+ // summary: close opened sub popup
+ if(this.currentSubpopup == null){ return; }
+
+ this.currentSubpopup.close(force);
+ this.currentSubpopup = null;
+ },
+
+ onShow: function() {
+ dojo.widget.PopupContainer.superclass.onShow.apply(this, arguments);
+ // With some animation (wipe), after close, the size of the domnode is 0
+ // and next time when shown, the open() function can not determine
+ // the correct place to popup, so we store the opened size here and
+ // set it after close (in function onHide())
+ this.openedSize={w: this.domNode.style.width, h: this.domNode.style.height};
+ // prevent IE bleed through
+ if(dojo.render.html.ie){
+ if(!this.bgIframe){
+ this.bgIframe = new dojo.html.BackgroundIframe();
+ this.bgIframe.setZIndex(this.domNode);
+ }
+
+ this.bgIframe.size(this.domNode);
+ this.bgIframe.show();
+ }
+ this.processQueue();
+ },
+
+ processQueue: function() {
+ // summary: do events from queue
+ if (!this.queueOnAnimationFinish.length) return;
+
+ var func = this.queueOnAnimationFinish.shift();
+ var args = this.queueOnAnimationFinish.shift();
+
+ func.apply(this, args);
+ },
+
+ onHide: function() {
+ dojo.widget.HtmlWidget.prototype.onHide.call(this);
+
+ //restore size of the domnode, see comment in
+ //function onShow()
+ if(this.openedSize){
+ with(this.domNode.style){
+ width=this.openedSize.w;
+ height=this.openedSize.h;
+ }
+ }
+
+ this.processQueue();
+ }
+});
+
+// summary: dojo.widget.PopupContainer is the widget version of dojo.widget.PopupContainerBase
+dojo.widget.defineWidget(
+ "dojo.widget.PopupContainer",
+ [dojo.widget.HtmlWidget, dojo.widget.PopupContainerBase], {});
+
+
+// summary:
+// the popup manager makes sure we don't have several popups
+// open at once. the root popup in an opening sequence calls
+// opened(). when a root menu closes it calls closed(). then
+// everything works. lovely.
+dojo.widget.PopupManager = new function(){
+ this.currentMenu = null;
+ this.currentButton = null; // button that opened current menu (if any)
+ this.currentFocusMenu = null; // the (sub)menu which receives key events
+ this.focusNode = null;
+ this.registeredWindows = [];
+
+ this.registerWin = function(/*Window*/win){
+ // summary: register a window so that when clicks/scroll in it, the popup can be closed automatically
+ if(!win.__PopupManagerRegistered)
+ {
+ dojo.event.connect(win.document, 'onmousedown', this, 'onClick');
+ dojo.event.connect(win, "onscroll", this, "onClick");
+ dojo.event.connect(win.document, "onkey", this, 'onKey');
+ win.__PopupManagerRegistered = true;
+ this.registeredWindows.push(win);
+ }
+ };
+
+ /*
+
+ */
+ this.registerAllWindows = function(/*Window*/targetWindow){
+ // summary:
+ // This function register all the iframes and the top window,
+ // so that whereever the user clicks in the page, the popup
+ // menu will be closed
+ // In case you add an iframe after onload event, please call
+ // dojo.widget.PopupManager.registerWin manually
+
+ //starting from window.top, clicking everywhere in this page
+ //should close popup menus
+ if(!targetWindow) { //see comment below
+ targetWindow = dojo.html.getDocumentWindow(window.top && window.top.document || window.document);
+ }
+
+ this.registerWin(targetWindow);
+
+ for (var i = 0; i < targetWindow.frames.length; i++){
+ try{
+ //do not remove dojo.html.getDocumentWindow, see comment in it
+ var win = dojo.html.getDocumentWindow(targetWindow.frames[i].document);
+ if(win){
+ this.registerAllWindows(win);
+ }
+ }catch(e){ /* squelch error for cross domain iframes */ }
+ }
+ };
+
+ this.unRegisterWin = function(/*Window*/win){
+ // summary: remove listeners on the registered window
+ if(win.__PopupManagerRegistered)
+ {
+ dojo.event.disconnect(win.document, 'onmousedown', this, 'onClick');
+ dojo.event.disconnect(win, "onscroll", this, "onClick");
+ dojo.event.disconnect(win.document, "onkey", this, 'onKey');
+ win.__PopupManagerRegistered = false;
+ }
+ };
+
+ this.unRegisterAllWindows = function(){
+ // summary: remove listeners on all the registered windows
+ for(var i=0;i<this.registeredWindows.length;++i){
+ this.unRegisterWin(this.registeredWindows[i]);
+ }
+ this.registeredWindows = [];
+ };
+
+ dojo.addOnLoad(this, "registerAllWindows");
+ dojo.addOnUnload(this, "unRegisterAllWindows");
+
+ this.closed = function(/*Widget*/menu){
+ // summary: notify the manager that menu is closed
+ if (this.currentMenu == menu){
+ this.currentMenu = null;
+ this.currentButton = null;
+ this.currentFocusMenu = null;
+ }
+ };
+
+ this.opened = function(/*Widget*/menu, /*DomNode*/button){
+ // summary: sets the current opened popup
+ if (menu == this.currentMenu){ return; }
+
+ if (this.currentMenu){
+ this.currentMenu.close();
+ }
+
+ this.currentMenu = menu;
+ this.currentFocusMenu = menu;
+ this.currentButton = button;
+ };
+
+ this.setFocusedMenu = function(/*Widget*/menu){
+ // summary:
+ // Set the current focused popup, This is used by popups which supports keyboard navigation
+ this.currentFocusMenu = menu;
+ };
+
+ this.onKey = function(/*Event*/e){
+ if (!e.key) { return; }
+ if(!this.currentMenu || !this.currentMenu.isShowingNow){ return; }
+
+ var m = this.currentFocusMenu;
+ while (m){
+ if(m.processKey(e)){
+ e.preventDefault();
+ e.stopPropagation();
+ break;
+ }
+ m = m.parentPopup;
+ }
+ },
+
+ this.onClick = function(/*Event*/e){
+ if (!this.currentMenu){ return; }
+
+ var scrolloffset = dojo.html.getScroll().offset;
+
+ // starting from the base menu, perform a hit test
+ // and exit when one succeeds
+
+ var m = this.currentMenu;
+
+ while (m){
+ if(dojo.html.overElement(m.domNode, e) || dojo.html.isDescendantOf(e.target, m.domNode)){
+ return;
+ }
+ m = m.currentSubpopup;
+ }
+
+ // Also, if user clicked the button that opened this menu, then
+ // that button will send the menu a close() command, so this code
+ // shouldn't try to close the menu. Closing twice messes up animation.
+ if (this.currentButton && dojo.html.overElement(this.currentButton, e)){
+ return;
+ }
+
+ // the click didn't fall within the open menu tree
+ // so close it
+
+ this.currentMenu.close();
+ };
+}
\ No newline at end of file
Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/PopupContainer.js
------------------------------------------------------------------------------
svn:eol-style = native